Commit 0f252702 authored by Jan Lindström's avatar Jan Lindström

MDEV-7139: Sporadic failure in innodb.innodb_corrupt_bit on P8

Use direct persistent index corruption set on InnoDB dictionary
for this test. Do not allow creating new indexes if one of the
existing indexes is already marked as corrupted.
parent ff832e0d
set names utf8;
CREATE TABLE corrupt_bit_test_ā(
a INT AUTO_INCREMENT PRIMARY KEY,
b CHAR(100),
c INT,
z INT,
INDEX(b))
ENGINE=InnoDB;
INSERT INTO corrupt_bit_test_ā VALUES(0,'x',1, 1);
CREATE UNIQUE INDEX idxā ON corrupt_bit_test_ā(c, b);
CREATE UNIQUE INDEX idxē ON corrupt_bit_test_ā(z, b);
SELECT * FROM corrupt_bit_test_ā;
a b c z a b c z
1 x 1 1 1 x 1 1
select @@unique_checks;
@@unique_checks
0
select @@innodb_change_buffering_debug;
@@innodb_change_buffering_debug
1
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+1,z+1 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+10,z+10 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+20,z+20 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+50,z+50 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+100,z+100 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+200,z+200 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+400,z+400 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+800,z+800 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+1600,z+1600 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+4000,z+4000 FROM corrupt_bit_test_ā;
select count(*) from corrupt_bit_test_ā;
count(*) count(*)
1024 2
CREATE INDEX idx3 ON corrupt_bit_test_ā(b, c);
INSERT INTO corrupt_bit_test_ā VALUES(13000,'x',1,1);
CREATE INDEX idx4 ON corrupt_bit_test_ā(b, z);
check table corrupt_bit_test_ā;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.corrupt_bit_test_ā check Warning InnoDB: The B-tree of index "idxā" is corrupted. test.corrupt_bit_test_ā check Warning InnoDB: Index "idx" is marked as corrupted
test.corrupt_bit_test_ā check Warning InnoDB: The B-tree of index "idxē" is corrupted. test.corrupt_bit_test_ā check Warning InnoDB: Index "idxā" is marked as corrupted
test.corrupt_bit_test_ā check Warning InnoDB: Index "idxē" is marked as corrupted
test.corrupt_bit_test_ā check error Corrupt test.corrupt_bit_test_ā check error Corrupt
select c from corrupt_bit_test_ā;
ERROR HY000: Index corrupt_bit_test_ā is corrupted ERROR HY000: Index corrupt_bit_test_ā is corrupted
select z from corrupt_bit_test_ā;
ERROR HY000: Index corrupt_bit_test_ā is corrupted ERROR HY000: Index corrupt_bit_test_ā is corrupted
show warnings; ERROR HY000: Index corrupt_bit_test_ā is corrupted
ERROR HY000: Index corrupt_bit_test_ā is corrupted
Level Code Message Level Code Message
Warning 179 InnoDB: Index "idxē" for table "test"."corrupt_bit_test_ā" is marked as corrupted Warning 179 InnoDB: Index "idxē" for table "test"."corrupt_bit_test_ā" is marked as corrupted
Warning 179 Got error 179 when reading table `test`.`corrupt_bit_test_ā` Warning 179 Got error 179 when reading table `test`.`corrupt_bit_test_ā`
Error 1712 Index corrupt_bit_test_ā is corrupted Error 1712 Index corrupt_bit_test_ā is corrupted
insert into corrupt_bit_test_ā values (10001, "a", 20001, 20001);
select * from corrupt_bit_test_ā use index(primary) where a = 10001;
a b c z a b c z
10001 a 20001 20001 10001 a 20001 20001
begin;
insert into corrupt_bit_test_ā values (10002, "a", 20002, 20002);
delete from corrupt_bit_test_ā where a = 10001;
insert into corrupt_bit_test_ā values (10001, "a", 20001, 20001);
rollback;
drop index idxā on corrupt_bit_test_ā;
check table corrupt_bit_test_ā;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.corrupt_bit_test_ā check Warning InnoDB: Index "idx" is marked as corrupted
test.corrupt_bit_test_ā check Warning InnoDB: Index "idxē" is marked as corrupted test.corrupt_bit_test_ā check Warning InnoDB: Index "idxē" is marked as corrupted
test.corrupt_bit_test_ā check error Corrupt test.corrupt_bit_test_ā check error Corrupt
set names utf8;
select z from corrupt_bit_test_ā;
ERROR HY000: Index corrupt_bit_test_ā is corrupted ERROR HY000: Index corrupt_bit_test_ā is corrupted
drop index idxē on corrupt_bit_test_ā; Table Create Table
select z from corrupt_bit_test_ā limit 10; corrupt_bit_test_ā CREATE TABLE `corrupt_bit_test_ā` (
`a` int(11) NOT NULL AUTO_INCREMENT,
`b` char(100) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
`z` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `idxē` (`z`,`b`),
KEY `idx` (`b`)
) ENGINE=InnoDB AUTO_INCREMENT=10003 DEFAULT CHARSET=latin1
ERROR HY000: Index corrupt_bit_test_ā is corrupted
ERROR HY000: Index corrupt_bit_test_ā is corrupted
Table Create Table
corrupt_bit_test_ā CREATE TABLE `corrupt_bit_test_ā` (
`a` int(11) NOT NULL AUTO_INCREMENT,
`b` char(100) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
`z` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `idx` (`b`)
) ENGINE=InnoDB AUTO_INCREMENT=10003 DEFAULT CHARSET=latin1
z z
20001 20001
1 1
1
2 2
11
12
21
22
31
32
drop table corrupt_bit_test_ā;
DROP DATABASE pad;
SET GLOBAL innodb_change_buffering_debug = 0;
...@@ -2,45 +2,23 @@ ...@@ -2,45 +2,23 @@
# Test for persistent corrupt bit for corrupted index and table # Test for persistent corrupt bit for corrupted index and table
# #
-- source include/have_innodb.inc -- source include/have_innodb.inc
-- source include/not_embedded.inc
# Issues with innodb_change_buffering_debug on Windows, so the test scenario
# cannot be created on windows
--source include/not_windows.inc
# This test needs debug server # This test needs debug server
--source include/have_debug.inc -- source include/have_debug.inc
-- disable_query_log -- disable_query_log
call mtr.add_suppression("Flagged corruption of idx.*in CHECK TABLE"); call mtr.add_suppression("Flagged corruption of idx.*in CHECK TABLE");
# This test setup is extracted from bug56680.test:
# The flag innodb_change_buffering_debug is only available in debug builds.
# It instructs InnoDB to try to evict pages from the buffer pool when
# change buffering is possible, so that the change buffer will be used
# whenever possible.
SET @innodb_change_buffering_debug_orig = @@innodb_change_buffering_debug;
SET GLOBAL innodb_change_buffering_debug = 1;
# Turn off Unique Check to create corrupted index with dup key
SET UNIQUE_CHECKS=0;
CREATE DATABASE pad;
let $i=338;
while ($i)
{
--eval CREATE TABLE pad.t$i(a INT PRIMARY KEY)ENGINE=InnoDB;
dec $i;
}
-- enable_query_log
set names utf8; set names utf8;
SET UNIQUE_CHECKS=0;
CREATE TABLE corrupt_bit_test_ā( CREATE TABLE corrupt_bit_test_ā(
a INT AUTO_INCREMENT PRIMARY KEY, a INT AUTO_INCREMENT PRIMARY KEY,
b CHAR(100), b CHAR(100),
c INT, c INT,
z INT, z INT,
INDEX(b)) INDEX idx(b))
ENGINE=InnoDB; ENGINE=InnoDB;
INSERT INTO corrupt_bit_test_ā VALUES(0,'x',1, 1); INSERT INTO corrupt_bit_test_ā VALUES(0,'x',1, 1);
...@@ -53,38 +31,21 @@ CREATE UNIQUE INDEX idxē ON corrupt_bit_test_ā(z, b); ...@@ -53,38 +31,21 @@ CREATE UNIQUE INDEX idxē ON corrupt_bit_test_ā(z, b);
SELECT * FROM corrupt_bit_test_ā; SELECT * FROM corrupt_bit_test_ā;
select @@unique_checks;
select @@innodb_change_buffering_debug;
# Create enough rows for the table, so that the insert buffer will be
# used for modifying the secondary index page. There must be multiple
# index pages, because changes to the root page are never buffered.
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+1,z+1 FROM corrupt_bit_test_ā; INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+1,z+1 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+10,z+10 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+20,z+20 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+50,z+50 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+100,z+100 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+200,z+200 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+400,z+400 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+800,z+800 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+1600,z+1600 FROM corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā SELECT 0,b,c+4000,z+4000 FROM corrupt_bit_test_ā;
select count(*) from corrupt_bit_test_ā; select count(*) from corrupt_bit_test_ā;
CREATE INDEX idx3 ON corrupt_bit_test_ā(b, c); # This will flag all secondary indexes corrupted
SET SESSION debug_dbug="+d,dict_set_index_corrupted";
# Create a dup key error on index "idxē" and "idxā" by inserting a dup value check table corrupt_bit_test_ā;
INSERT INTO corrupt_bit_test_ā VALUES(13000,'x',1,1); SET SESSION debug_dbug="-d,dict_set_index_corrupted";
# creating an index should succeed even if other secondary indexes are corrupted # Cannot create new indexes while corrupted indexes exist
--error ER_INDEX_CORRUPT
CREATE INDEX idx3 ON corrupt_bit_test_ā(b, c);
--error ER_INDEX_CORRUPT
CREATE INDEX idx4 ON corrupt_bit_test_ā(b, z); CREATE INDEX idx4 ON corrupt_bit_test_ā(b, z);
# Check table will find the unique indexes corrupted
# with dup key
check table corrupt_bit_test_ā;
# This selection intend to use the corrupted index. Expect to fail # This selection intend to use the corrupted index. Expect to fail
-- error ER_INDEX_CORRUPT -- error ER_INDEX_CORRUPT
select c from corrupt_bit_test_ā; select c from corrupt_bit_test_ā;
...@@ -108,7 +69,6 @@ delete from corrupt_bit_test_ā where a = 10001; ...@@ -108,7 +69,6 @@ delete from corrupt_bit_test_ā where a = 10001;
insert into corrupt_bit_test_ā values (10001, "a", 20001, 20001); insert into corrupt_bit_test_ā values (10001, "a", 20001, 20001);
rollback; rollback;
# Drop one corrupted index before reboot
drop index idxā on corrupt_bit_test_ā; drop index idxā on corrupt_bit_test_ā;
check table corrupt_bit_test_ā; check table corrupt_bit_test_ā;
...@@ -118,14 +78,26 @@ set names utf8; ...@@ -118,14 +78,26 @@ set names utf8;
-- error ER_INDEX_CORRUPT -- error ER_INDEX_CORRUPT
select z from corrupt_bit_test_ā; select z from corrupt_bit_test_ā;
show create table corrupt_bit_test_ā;
# Drop the corrupted index # Drop the corrupted index
drop index idxē on corrupt_bit_test_ā; drop index idxē on corrupt_bit_test_ā;
# Cannot create new indexes while a corrupt index exists.
--error ER_INDEX_CORRUPT
CREATE INDEX idx3 ON corrupt_bit_test_ā(b, c);
--error ER_INDEX_CORRUPT
CREATE INDEX idx4 ON corrupt_bit_test_ā(b, z);
show create table corrupt_bit_test_ā;
drop index idx on corrupt_bit_test_ā;
# Now that there exist no corrupted indexes, we can create new indexes.
CREATE INDEX idx3 ON corrupt_bit_test_ā(b, c);
CREATE INDEX idx4 ON corrupt_bit_test_ā(b, z);
# Now select back to normal # Now select back to normal
select z from corrupt_bit_test_ā limit 10; select z from corrupt_bit_test_ā limit 10;
# Drop table # Drop table
drop table corrupt_bit_test_ā; drop table corrupt_bit_test_ā;
DROP DATABASE pad;
SET GLOBAL innodb_change_buffering_debug = 0;
...@@ -2106,7 +2106,6 @@ no_db_name: ...@@ -2106,7 +2106,6 @@ no_db_name:
A wrapper function of innobase_convert_name(), convert a table or A wrapper function of innobase_convert_name(), convert a table or
index name to the MySQL system_charset_info (UTF-8) and quote it if needed. index name to the MySQL system_charset_info (UTF-8) and quote it if needed.
@return pointer to the end of buf */ @return pointer to the end of buf */
static inline
void void
innobase_format_name( innobase_format_name(
/*==================*/ /*==================*/
...@@ -8685,6 +8684,36 @@ ha_innobase::check( ...@@ -8685,6 +8684,36 @@ ha_innobase::check(
DBUG_RETURN(HA_ADMIN_CORRUPT); DBUG_RETURN(HA_ADMIN_CORRUPT);
} }
if (prebuilt->table->corrupted) {
char index_name[MAX_FULL_NAME_LEN + 1];
/* If some previous operation has marked the table as
corrupted in memory, and has not propagated such to
clustered index, we will do so here */
index = dict_table_get_first_index(prebuilt->table);
if (!dict_index_is_corrupted(index)) {
row_mysql_lock_data_dictionary(prebuilt->trx);
dict_set_corrupted(index);
row_mysql_unlock_data_dictionary(prebuilt->trx);
}
innobase_format_name(index_name, sizeof index_name,
index->name, TRUE);
push_warning_printf(thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
HA_ERR_INDEX_CORRUPT,
"InnoDB: Index %s is marked as"
" corrupted",
index_name);
/* Now that the table is already marked as corrupted,
there is no need to check any index of this table */
prebuilt->trx->op_info = "";
DBUG_RETURN(HA_ADMIN_CORRUPT);
}
prebuilt->trx->op_info = "checking table"; prebuilt->trx->op_info = "checking table";
old_isolation_level = prebuilt->trx->isolation_level; old_isolation_level = prebuilt->trx->isolation_level;
...@@ -8761,6 +8790,15 @@ ha_innobase::check( ...@@ -8761,6 +8790,15 @@ ha_innobase::check(
prebuilt->index_usable = row_merge_is_index_usable( prebuilt->index_usable = row_merge_is_index_usable(
prebuilt->trx, prebuilt->index); prebuilt->trx, prebuilt->index);
DBUG_EXECUTE_IF(
"dict_set_index_corrupted",
if (!dict_index_is_clust(index)) {
prebuilt->index_usable = FALSE;
row_mysql_lock_data_dictionary(prebuilt->trx);
dict_set_corrupted(index);
row_mysql_unlock_data_dictionary(prebuilt->trx);
});
if (UNIV_UNLIKELY(!prebuilt->index_usable)) { if (UNIV_UNLIKELY(!prebuilt->index_usable)) {
innobase_format_name( innobase_format_name(
index_name, sizeof index_name, index_name, sizeof index_name,
......
...@@ -654,6 +654,19 @@ public: ...@@ -654,6 +654,19 @@ public:
~ha_innobase_add_index() {} ~ha_innobase_add_index() {}
}; };
/*****************************************************************//**
A wrapper function of innobase_convert_name(), convert a table or
index name to the MySQL system_charset_info (UTF-8) and quote it if needed.
@return pointer to the end of buf */
void
innobase_format_name(
/*==================*/
char* buf, /*!< out: buffer for converted identifier */
ulint buflen, /*!< in: length of buf, in bytes */
const char* name, /*!< in: index or table name to format */
ibool is_index_name); /*!< in: index name */
/*******************************************************************//** /*******************************************************************//**
Create indexes. Create indexes.
@return 0 or error number */ @return 0 or error number */
...@@ -715,6 +728,28 @@ ha_innobase::add_index( ...@@ -715,6 +728,28 @@ ha_innobase::add_index(
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
/* Check if any of the existing indexes are marked as corruption,
and if they are, refuse adding more indexes. */
for (dict_index_t* check_index = dict_table_get_first_index(indexed_table);
check_index != NULL;
check_index = dict_table_get_next_index(check_index)) {
if (dict_index_is_corrupted(check_index)) {
char index_name[MAX_FULL_NAME_LEN + 1];
innobase_format_name(index_name, sizeof index_name,
check_index->name, TRUE);
push_warning_printf(user_thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
HA_ERR_INDEX_CORRUPT,
"InnoDB: Index %s is marked as"
" corrupted",
index_name);
DBUG_RETURN(HA_ERR_INDEX_CORRUPT);
}
}
/* Check that index keys are sensible */ /* Check that index keys are sensible */
error = innobase_check_index_keys(key_info, num_of_keys, prebuilt->table); error = innobase_check_index_keys(key_info, num_of_keys, prebuilt->table);
......
...@@ -2409,7 +2409,6 @@ no_db_name: ...@@ -2409,7 +2409,6 @@ no_db_name:
A wrapper function of innobase_convert_name(), convert a table or A wrapper function of innobase_convert_name(), convert a table or
index name to the MySQL system_charset_info (UTF-8) and quote it if needed. index name to the MySQL system_charset_info (UTF-8) and quote it if needed.
@return pointer to the end of buf */ @return pointer to the end of buf */
static inline
void void
innobase_format_name( innobase_format_name(
/*==================*/ /*==================*/
...@@ -9859,6 +9858,36 @@ ha_innobase::check( ...@@ -9859,6 +9858,36 @@ ha_innobase::check(
DBUG_RETURN(HA_ADMIN_CORRUPT); DBUG_RETURN(HA_ADMIN_CORRUPT);
} }
if (prebuilt->table->corrupted) {
char index_name[MAX_FULL_NAME_LEN + 1];
/* If some previous operation has marked the table as
corrupted in memory, and has not propagated such to
clustered index, we will do so here */
index = dict_table_get_first_index(prebuilt->table);
if (!dict_index_is_corrupted(index)) {
row_mysql_lock_data_dictionary(prebuilt->trx);
dict_set_corrupted(index);
row_mysql_unlock_data_dictionary(prebuilt->trx);
}
innobase_format_name(index_name, sizeof index_name,
index->name, TRUE);
push_warning_printf(thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
HA_ERR_INDEX_CORRUPT,
"InnoDB: Index %s is marked as"
" corrupted",
index_name);
/* Now that the table is already marked as corrupted,
there is no need to check any index of this table */
prebuilt->trx->op_info = "";
DBUG_RETURN(HA_ADMIN_CORRUPT);
}
prebuilt->trx->op_info = "checking table"; prebuilt->trx->op_info = "checking table";
old_isolation_level = prebuilt->trx->isolation_level; old_isolation_level = prebuilt->trx->isolation_level;
...@@ -9935,6 +9964,15 @@ ha_innobase::check( ...@@ -9935,6 +9964,15 @@ ha_innobase::check(
prebuilt->index_usable = row_merge_is_index_usable( prebuilt->index_usable = row_merge_is_index_usable(
prebuilt->trx, prebuilt->index); prebuilt->trx, prebuilt->index);
DBUG_EXECUTE_IF(
"dict_set_index_corrupted",
if (!dict_index_is_clust(index)) {
prebuilt->index_usable = FALSE;
row_mysql_lock_data_dictionary(prebuilt->trx);
dict_set_corrupted(index);
row_mysql_unlock_data_dictionary(prebuilt->trx);
});
if (UNIV_UNLIKELY(!prebuilt->index_usable)) { if (UNIV_UNLIKELY(!prebuilt->index_usable)) {
innobase_format_name( innobase_format_name(
index_name, sizeof index_name, index_name, sizeof index_name,
......
...@@ -655,6 +655,19 @@ public: ...@@ -655,6 +655,19 @@ public:
~ha_innobase_add_index() {} ~ha_innobase_add_index() {}
}; };
/*****************************************************************//**
A wrapper function of innobase_convert_name(), convert a table or
index name to the MySQL system_charset_info (UTF-8) and quote it if needed.
@return pointer to the end of buf */
void
innobase_format_name(
/*==================*/
char* buf, /*!< out: buffer for converted identifier */
ulint buflen, /*!< in: length of buf, in bytes */
const char* name, /*!< in: index or table name to format */
ibool is_index_name); /*!< in: index name */
/*******************************************************************//** /*******************************************************************//**
Create indexes. Create indexes.
@return 0 or error number */ @return 0 or error number */
...@@ -720,6 +733,28 @@ ha_innobase::add_index( ...@@ -720,6 +733,28 @@ ha_innobase::add_index(
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
/* Check if any of the existing indexes are marked as corruption,
and if they are, refuse adding more indexes. */
for (dict_index_t* check_index = dict_table_get_first_index(indexed_table);
check_index != NULL;
check_index = dict_table_get_next_index(check_index)) {
if (dict_index_is_corrupted(check_index)) {
char index_name[MAX_FULL_NAME_LEN + 1];
innobase_format_name(index_name, sizeof index_name,
check_index->name, TRUE);
push_warning_printf(user_thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
HA_ERR_INDEX_CORRUPT,
"InnoDB: Index %s is marked as"
" corrupted",
index_name);
DBUG_RETURN(HA_ERR_INDEX_CORRUPT);
}
}
/* Check that index keys are sensible */ /* Check that index keys are sensible */
error = innobase_check_index_keys(key_info, num_of_keys, prebuilt->table); error = innobase_check_index_keys(key_info, num_of_keys, prebuilt->table);
......
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