diff options
-rw-r--r-- | mysql-test/suite/encryption/r/load_infile.result | 8 | ||||
-rw-r--r-- | mysql-test/suite/encryption/t/load_infile.test | 55 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test | 3 | ||||
-rw-r--r-- | storage/innobase/buf/buf0buf.cc | 25 | ||||
-rw-r--r-- | storage/innobase/buf/buf0lru.cc | 23 | ||||
-rw-r--r-- | storage/innobase/buf/buf0rea.cc | 5 | ||||
-rw-r--r-- | storage/innobase/include/buf0buf.h | 2 | ||||
-rw-r--r-- | storage/innobase/include/buf0buf.ic | 32 | ||||
-rw-r--r-- | storage/innobase/include/buf0lru.h | 14 | ||||
-rw-r--r-- | storage/innobase/include/buf0types.h | 6 | ||||
-rw-r--r-- | storage/innobase/include/log0recv.h | 2 | ||||
-rw-r--r-- | storage/innobase/row/row0row.cc | 14 |
12 files changed, 162 insertions, 27 deletions
diff --git a/mysql-test/suite/encryption/r/load_infile.result b/mysql-test/suite/encryption/r/load_infile.result new file mode 100644 index 00000000000..6e84484b2c3 --- /dev/null +++ b/mysql-test/suite/encryption/r/load_infile.result @@ -0,0 +1,8 @@ +CREATE TABLE t1 (pk INT PRIMARY KEY AUTO_INCREMENT, c VARCHAR(256)) ENGINE=INNODB ENCRYPTED=YES; +SELECT * FROM t1 INTO OUTFILE "MYSQLTEST_VARDIR/tmp/t1.outfile"; +CREATE TABLE t2 (pk INT, c CHAR(255)) ENGINE=INNODB ENCRYPTED=YES; +INSERT INTO t2 SELECT NULL, 'c' FROM seq_1_to_1000; +# Corrupt the pages +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/t1.outfile' INTO table t2; +Got one of the listed errors +DROP TABLE t1, t2; diff --git a/mysql-test/suite/encryption/t/load_infile.test b/mysql-test/suite/encryption/t/load_infile.test new file mode 100644 index 00000000000..d0560da611e --- /dev/null +++ b/mysql-test/suite/encryption/t/load_infile.test @@ -0,0 +1,55 @@ +-- source include/have_innodb.inc +-- source include/have_file_key_management_plugin.inc + +--disable_query_log +call mtr.add_suppression("InnoDB: The page \\[page id: space=[0-9][0-9]*, page number=6\\] in file '.*test.t2\\.ibd' cannot be decrypted\\."); +call mtr.add_suppression("InnoDB: Error code: [0-9][0-9][0-9]* btr_pcur_open_low level: 0 called from file: .*"); +--enable_query_log + +CREATE TABLE t1 (pk INT PRIMARY KEY AUTO_INCREMENT, c VARCHAR(256)) ENGINE=INNODB ENCRYPTED=YES; + +--disable_warnings +--disable_query_log +begin; +let $i = 10; +while ($i) +{ +INSERT INTO t1 values(NULL, REPEAT('x', 32)); +dec $i; +} +commit; +--enable_warnings +--enable_query_log + +let INNODB_PAGE_SIZE=`select @@innodb_page_size`; +let MYSQLD_DATADIR=`select @@datadir`; + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SELECT * FROM t1 INTO OUTFILE "$MYSQLTEST_VARDIR/tmp/t1.outfile"; + +CREATE TABLE t2 (pk INT, c CHAR(255)) ENGINE=INNODB ENCRYPTED=YES; +--source include/have_sequence.inc +INSERT INTO t2 SELECT NULL, 'c' FROM seq_1_to_1000; + +--source include/shutdown_mysqld.inc +--echo # Corrupt the pages + +perl; +my $ps = $ENV{INNODB_PAGE_SIZE}; + +my $file = "$ENV{MYSQLD_DATADIR}/test/t2.ibd"; +open(FILE, "+<$file") || die "Unable to open $file"; +binmode FILE; +seek (FILE, $ENV{INNODB_PAGE_SIZE} * 6, SEEK_SET) or die "seek"; +print FILE "junk"; +close FILE or die "close"; +EOF + +--source include/start_mysqld.inc + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error ER_GET_ERRMSG, 192 +eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/t1.outfile' INTO table t2; + +DROP TABLE t1, t2; +--remove_file $MYSQLTEST_VARDIR/tmp/t1.outfile diff --git a/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test b/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test index f9f2b11471a..b49cbe01a0c 100644 --- a/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test +++ b/mysql-test/suite/innodb/t/leaf_page_corrupted_during_recovery.test @@ -9,6 +9,7 @@ call mtr.add_suppression("\\[ERROR\\] InnoDB: Plugin initialization aborted at s call mtr.add_suppression("\\[ERROR\\] Plugin 'InnoDB' (init function|registration)"); call mtr.add_suppression("\\[ERROR\\] InnoDB: We detected index corruption"); call mtr.add_suppression("\\[ERROR\\] mysqld.*: Index for table 't1' is corrupt; try to repair it"); +call mtr.add_suppression("InnoDB: Error code: [0-9][0-9][0-9]* btr_pcur_open_low level: 0 called from file: .*"); --enable_query_log CREATE TABLE t1 (pk INT PRIMARY KEY, c CHAR(255))ENGINE=InnoDB STATS_PERSISTENT=0; @@ -46,8 +47,6 @@ EOF SELECT * FROM t1 WHERE PK = 1; let $restart_parameters=--innodb-force-recovery=1; -# Work around MDEV-19435 to avoid crash in row_purge_reset_trx_id() -let $restart_parameters=--innodb-force-recovery=2; --source include/restart_mysqld.inc SELECT * FROM t1 WHERE PK = 1; --error ER_NOT_KEYFILE diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 9182275c695..2a7ce5235ae 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -4850,6 +4850,23 @@ evict_from_pool: and block->lock. */ buf_wait_for_read(fix_block); + if (fix_block->page.id != page_id) { + + buf_block_unfix(fix_block); + +#ifdef UNIV_DEBUG + if (!fsp_is_system_temporary(page_id.space())) { + rw_lock_s_unlock(&fix_block->debug_latch); + } +#endif /* UNIV_DEBUG */ + + if (err) { + *err = DB_PAGE_CORRUPTED; + } + + return NULL; + } + mtr_memo_type_t fix_type; switch (rw_latch) { @@ -5820,14 +5837,18 @@ buf_corrupt_page_release(buf_page_t* bpage, const fil_space_t* space) buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); const ibool uncompressed = (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); + page_id_t old_page_id = bpage->id; /* First unfix and release lock on the bpage */ buf_pool_mutex_enter(buf_pool); mutex_enter(buf_page_get_mutex(bpage)); ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); - ut_ad(bpage->buf_fix_count == 0); ut_ad(bpage->id.space() == space->id); + /* buf_fix_count can be greater than zero. Because other thread + can wait in buf_page_wait_read() for the page to be read. */ + + bpage->id.set_corrupt_id(); /* Set BUF_IO_NONE before we remove the block from LRU list */ buf_page_set_io_fix(bpage, BUF_IO_NONE); @@ -5844,7 +5865,7 @@ buf_corrupt_page_release(buf_page_t* bpage, const fil_space_t* space) } /* After this point bpage can't be referenced. */ - buf_LRU_free_one_page(bpage); + buf_LRU_free_one_page(bpage, old_page_id); ut_ad(buf_pool->n_pend_reads > 0); buf_pool->n_pend_reads--; diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc index c00dd414aa6..6cef80e1ce4 100644 --- a/storage/innobase/buf/buf0lru.cc +++ b/storage/innobase/buf/buf0lru.cc @@ -2165,25 +2165,34 @@ buf_LRU_block_free_hashed_page( buf_page_mutex_exit(block); } -/******************************************************************//** -Remove one page from LRU list and put it to free list */ +/** Remove one page from LRU list and put it to free list. +@param[in,out] bpage block, must contain a file page and be in a state + where it can be freed; there may or may not be a + hash index to the page +@param[in] old_page_id old page number before setting corruption id. */ void buf_LRU_free_one_page( -/*==================*/ - buf_page_t* bpage) /*!< in/out: block, must contain a file page and - be in a state where it can be freed; there - may or may not be a hash index to the page */ + buf_page_t* bpage, + page_id_t old_page_id) { buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); - rw_lock_t* hash_lock = buf_page_hash_lock_get(buf_pool, bpage->id); + rw_lock_t* hash_lock = buf_page_hash_lock_get(buf_pool, old_page_id); BPageMutex* block_mutex = buf_page_get_mutex(bpage); ut_ad(buf_pool_mutex_own(buf_pool)); rw_lock_x_lock(hash_lock); + + while (buf_block_get_fix(bpage) > 0) { + /* Wait for other threads to release the fix count + before releasing the bpage from LRU list. */ + } + mutex_enter(block_mutex); + bpage->id = old_page_id; + if (buf_LRU_block_remove_hashed(bpage, true)) { buf_LRU_block_free_hashed_page((buf_block_t*) bpage); } diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc index 76eb01c8e91..9dd5857df17 100644 --- a/storage/innobase/buf/buf0rea.cc +++ b/storage/innobase/buf/buf0rea.cc @@ -63,13 +63,14 @@ buf_read_page_handle_error( buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); const bool uncompressed = (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); + const page_id_t old_page_id = bpage->id; /* First unfix and release lock on the bpage */ buf_pool_mutex_enter(buf_pool); mutex_enter(buf_page_get_mutex(bpage)); ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); - ut_ad(bpage->buf_fix_count == 0); + bpage->id.set_corrupt_id(); /* Set BUF_IO_NONE before we remove the block from LRU list */ buf_page_set_io_fix(bpage, BUF_IO_NONE); @@ -82,7 +83,7 @@ buf_read_page_handle_error( mutex_exit(buf_page_get_mutex(bpage)); /* remove the block from LRU list */ - buf_LRU_free_one_page(bpage); + buf_LRU_free_one_page(bpage, old_page_id); ut_ad(buf_pool->n_pend_reads > 0); buf_pool->n_pend_reads--; diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index e7593cf482d..1af5455ed43 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -1435,7 +1435,7 @@ public: page_size_t size; /** Count of how manyfold this block is currently bufferfixed. */ - ib_uint32_t buf_fix_count; + int32 buf_fix_count; /** type of pending I/O operation; also protected by buf_pool->mutex for writes only */ diff --git a/storage/innobase/include/buf0buf.ic b/storage/innobase/include/buf0buf.ic index eccf723faa6..0525616b05a 100644 --- a/storage/innobase/include/buf0buf.ic +++ b/storage/innobase/include/buf0buf.ic @@ -953,7 +953,9 @@ ulint buf_block_fix( buf_page_t* bpage) { - return uint32(my_atomic_add32((int32*) &bpage->buf_fix_count, 1) + 1); + return uint32(my_atomic_add32_explicit( + (int32*) &bpage->buf_fix_count, 1, + MY_MEMORY_ORDER_RELAXED) + 1); } /** Increments the bufferfix count. @@ -967,6 +969,29 @@ buf_block_fix( return(buf_block_fix(&block->page)); } +/** Get the bufferfix count. +@param[in] bpage block to bufferfix +@return the count */ +UNIV_INLINE +ulint +buf_block_get_fix( + buf_page_t* bpage) +{ + return my_atomic_load32_explicit(&bpage->buf_fix_count, + MY_MEMORY_ORDER_RELAXED); +} + +/** Get the bufferfix count. +@param[in] bpage block to bufferfix +@return the count */ +UNIV_INLINE +ulint +buf_block_get_fix( + buf_block_t* block) +{ + return(buf_block_get_fix(&block->page)); +} + /*******************************************************************//** Increments the bufferfix count. */ UNIV_INLINE @@ -1001,8 +1026,9 @@ ulint buf_block_unfix( buf_page_t* bpage) { - uint32 count = uint32(my_atomic_add32((int32*) &bpage->buf_fix_count, - -1)); + uint32 count = uint32(my_atomic_add32_explicit( + (int32*) &bpage->buf_fix_count, + -1, MY_MEMORY_ORDER_RELAXED)); ut_ad(count != 0); return count - 1; } diff --git a/storage/innobase/include/buf0lru.h b/storage/innobase/include/buf0lru.h index 6583629be8c..2568e1f15cd 100644 --- a/storage/innobase/include/buf0lru.h +++ b/storage/innobase/include/buf0lru.h @@ -33,6 +33,7 @@ Created 11/5/1995 Heikki Tuuri // Forward declaration struct trx_t; struct fil_space_t; +class page_id_t; /******************************************************************//** Returns TRUE if less than 25 % of the buffer pool is available. This can be @@ -202,14 +203,15 @@ void buf_LRU_stat_update(void); /*=====================*/ -/******************************************************************//** -Remove one page from LRU list and put it to free list */ +/** Remove one page from LRU list and put it to free list +@param[in,out] bpage block, must contain a file page and be in a + state where it can be freed; there may or may + not be a hash index to the page +@param[in] old_page_id old page number before setting corruption id. */ void buf_LRU_free_one_page( -/*==================*/ - buf_page_t* bpage) /*!< in/out: block, must contain a file page and - be in a state where it can be freed; there - may or may not be a hash index to the page */ + buf_page_t* bpage, + page_id_t old_page_id) MY_ATTRIBUTE((nonnull)); /******************************************************************//** diff --git a/storage/innobase/include/buf0types.h b/storage/innobase/include/buf0types.h index bc74b59f3b1..bd5e26df47b 100644 --- a/storage/innobase/include/buf0types.h +++ b/storage/innobase/include/buf0types.h @@ -175,6 +175,12 @@ public: ut_ad(page_no <= 0xFFFFFFFFU); } + /** Set the FIL_NULL for the space and page_no */ + void set_corrupt_id() + { + m_space = m_page_no = ULINT32_UNDEFINED; + } + private: /** Tablespace id. */ diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h index 927e04ce04e..4c36d805395 100644 --- a/storage/innobase/include/log0recv.h +++ b/storage/innobase/include/log0recv.h @@ -36,6 +36,8 @@ Created 9/20/1997 Heikki Tuuri #include <list> #include <vector> +class page_id_t; + /** Is recv_writer_thread active? */ extern bool recv_writer_thread_active; diff --git a/storage/innobase/row/row0row.cc b/storage/innobase/row/row0row.cc index f3b40ce0900..a2eb6ea6cf4 100644 --- a/storage/innobase/row/row0row.cc +++ b/storage/innobase/row/row0row.cc @@ -1033,9 +1033,12 @@ row_search_on_row_ref( if (UNIV_UNLIKELY(ref->info_bits != 0)) { ut_ad(ref->info_bits == REC_INFO_METADATA); ut_ad(ref->n_fields <= index->n_uniq); - btr_pcur_open_at_index_side(true, index, mode, pcur, true, 0, - mtr); - btr_pcur_move_to_next_user_rec(pcur, mtr); + if (btr_pcur_open_at_index_side( + true, index, mode, pcur, true, 0, mtr) + != DB_SUCCESS + || !btr_pcur_move_to_next_user_rec(pcur, mtr)) { + return FALSE; + } /* We do not necessarily have index->is_instant() here, because we could be executing a rollback of an instant ADD COLUMN operation. The function @@ -1046,7 +1049,10 @@ row_search_on_row_ref( & REC_INFO_MIN_REC_FLAG; } else { ut_a(ref->n_fields == index->n_uniq); - btr_pcur_open(index, ref, PAGE_CUR_LE, mode, pcur, mtr); + if (btr_pcur_open(index, ref, PAGE_CUR_LE, mode, pcur, mtr) + != DB_SUCCESS) { + return FALSE; + } } low_match = btr_pcur_get_low_match(pcur); |