diff --git a/mysql-test/suite/innodb/r/insert_into_empty.result b/mysql-test/suite/innodb/r/insert_into_empty.result index 651cadd50b7..02d9549886d 100644 --- a/mysql-test/suite/innodb/r/insert_into_empty.result +++ b/mysql-test/suite/innodb/r/insert_into_empty.result @@ -13,3 +13,17 @@ SELECT * FROM t; a b 1 3 DROP TEMPORARY TABLE t; +# +# MDEV-24720 AHI removal during bulk index rollback +# +SET @save_ahi = @@global.innodb_adaptive_hash_index; +SET GLOBAL innodb_adaptive_hash_index = 1; +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 SELECT * FROM seq_1_to_65536; +ROLLBACK; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; +SET GLOBAL innodb_adaptive_hash_index = @save_ahi; diff --git a/mysql-test/suite/innodb/t/insert_into_empty.test b/mysql-test/suite/innodb/t/insert_into_empty.test index 945f0dbd627..af067cbd02d 100644 --- a/mysql-test/suite/innodb/t/insert_into_empty.test +++ b/mysql-test/suite/innodb/t/insert_into_empty.test @@ -1,4 +1,5 @@ --source include/have_innodb.inc +--source include/have_sequence.inc --echo # --echo # MDEV-24715 Assertion !node->table->skip_alter_undo @@ -12,3 +13,16 @@ CREATE TEMPORARY TABLE t (a INT UNIQUE) ENGINE=InnoDB REPLACE SELECT 1 AS a, 2 AS b UNION SELECT 1 AS a, 3 AS c; SELECT * FROM t; DROP TEMPORARY TABLE t; + +--echo # +--echo # MDEV-24720 AHI removal during bulk index rollback +--echo # +SET @save_ahi = @@global.innodb_adaptive_hash_index; +SET GLOBAL innodb_adaptive_hash_index = 1; +CREATE TABLE t1(f1 INT NOT NULL)ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 SELECT * FROM seq_1_to_65536; +ROLLBACK; +CHECK TABLE t1; +DROP TABLE t1; +SET GLOBAL innodb_adaptive_hash_index = @save_ahi; diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 4d85ce16df1..96fe35362d5 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -1118,7 +1118,11 @@ static void btr_free_but_not_root( buf_block_t* block, - mtr_log_t log_mode) + mtr_log_t log_mode +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif + ) { mtr_t mtr; @@ -1147,7 +1151,11 @@ leaf_loop: fsp0fsp. */ bool finished = fseg_free_step(root + PAGE_HEADER + PAGE_BTR_SEG_LEAF, - &mtr); + &mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); mtr_commit(&mtr); if (!finished) { @@ -1167,7 +1175,11 @@ top_loop: #endif /* UNIV_BTR_DEBUG */ finished = fseg_free_step_not_header( - root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr); + root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr +#ifdef BTR_CUR_HASH_ADAPT + ,ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); mtr_commit(&mtr); if (!finished) { @@ -1191,12 +1203,22 @@ void dict_index_t::clear(que_thr_t *thr) table->space->zip_size(), RW_X_LATCH, &mtr)) { - btr_free_but_not_root(root_block, mtr.get_log_mode()); + btr_free_but_not_root(root_block, mtr.get_log_mode() +#ifdef BTR_CUR_HASH_ADAPT + ,n_ahi_pages() != 0 +#endif + ); + mtr.memset(root_block, PAGE_HEADER + PAGE_BTR_SEG_LEAF, FSEG_HEADER_SIZE, 0); if (fseg_create(table->space, PAGE_HEADER + PAGE_BTR_SEG_LEAF, &mtr, false, root_block)) btr_root_page_init(root_block, id, this, &mtr); +#ifdef BTR_CUR_HASH_ADAPT + if (root_block->index) + btr_search_drop_page_hash_index(root_block); + ut_ad(n_ahi_pages() == 0); +#endif } mtr.commit(); diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 8c5a60abcc6..e95a35e80fc 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -2402,7 +2402,8 @@ try_to_extend: @param[in] seg_inode segment inode @param[in,out] space tablespace @param[in] offset page number -@param[in,out] mtr mini-transaction */ +@param[in,out] mtr mini-transaction +@param[in] ahi Drop adaptive hash index */ static void fseg_free_page_low( @@ -2410,7 +2411,11 @@ fseg_free_page_low( buf_block_t* iblock, fil_space_t* space, page_no_t offset, - mtr_t* mtr) + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ib_id_t descr_id; ib_id_t seg_id; @@ -2423,6 +2428,13 @@ fseg_free_page_low( ut_ad(iblock->frame == page_align(seg_inode)); ut_d(space->modify_check(*mtr)); +#ifdef BTR_CUR_HASH_ADAPT + if (ahi) { + btr_search_drop_page_hash_when_freed( + page_id_t(space->id, offset)); + } +#endif /* BTR_CUR_HASH_ADAPT */ + const uint32_t extent_size = FSP_EXTENT_SIZE; ut_ad(ut_is_2pow(extent_size)); buf_block_t* xdes; @@ -2593,7 +2605,11 @@ fseg_free_extent( buf_block_t* iblock, fil_space_t* space, uint32_t page, - mtr_t* mtr) + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ut_ad(mtr != NULL); @@ -2611,6 +2627,21 @@ fseg_free_extent( const uint16_t xoffset= uint16_t(descr - xdes->frame + XDES_FLST_NODE); const uint16_t ioffset= uint16_t(seg_inode - iblock->frame); +#ifdef BTR_CUR_HASH_ADAPT + if (ahi) { + for (uint32_t i = 0; i < FSP_EXTENT_SIZE; i++) { + if (!xdes_is_free(descr, i)) { + /* Drop search system page hash index + if the page is found in the pool and + is hashed */ + btr_search_drop_page_hash_when_freed( + page_id_t(space->id, + first_page_in_extent + i)); + } + } + } +#endif /* BTR_CUR_HASH_ADAPT */ + if (xdes_is_full(descr)) { flst_remove(iblock, static_cast(FSEG_FULL + ioffset), xdes, xoffset, mtr); @@ -2638,19 +2669,24 @@ fseg_free_extent( } } -/**********************************************************************//** -Frees part of a segment. This function can be used to free a segment by -repeatedly calling this function in different mini-transactions. Doing -the freeing in a single mini-transaction might result in too big a -mini-transaction. +/** Frees part of a segment. This function can be used to free +a segment by repeatedly calling this function in different +mini-transactions. Doing the freeing in a single mini-transaction +might result in too big a mini-transaction. +@param header segment header; NOTE: if the header resides on first + page of the frag list of the segment, this pointer + becomes obsolete after the last freeing step +@param mtr mini-transaction +@param ahi Drop the adaptive hash index @return whether the freeing was completed */ bool fseg_free_step( - fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header - resides on the first page of the frag list - of the segment, this pointer becomes obsolete - after the last freeing step */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ulint n; fseg_inode_t* inode; @@ -2686,7 +2722,11 @@ fseg_free_step( if (descr != NULL) { /* Free the extent held by the segment */ fseg_free_extent(inode, iblock, space, xdes_get_offset(descr), - mtr); + mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); DBUG_RETURN(false); } @@ -2702,7 +2742,11 @@ fseg_free_step( page_no_t page_no = fseg_get_nth_frag_page_no(inode, n); - fseg_free_page_low(inode, iblock, space, page_no, mtr); + fseg_free_page_low(inode, iblock, space, page_no, mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); buf_page_free(space, page_no, mtr); @@ -2718,15 +2762,14 @@ fseg_free_step( DBUG_RETURN(false); } -/**********************************************************************//** -Frees part of a segment. Differs from fseg_free_step because this function -leaves the header page unfreed. -@return whether the freeing was completed, except for the header page */ bool fseg_free_step_not_header( - fseg_header_t* header, /*!< in: segment header which must reside on - the first fragment page of the segment */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ) { ulint n; xdes_t* descr; @@ -2749,7 +2792,11 @@ fseg_free_step_not_header( if (descr != NULL) { /* Free the extent held by the segment */ fseg_free_extent(inode, iblock, space, xdes_get_offset(descr), - mtr); + mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); return false; } @@ -2765,7 +2812,11 @@ fseg_free_step_not_header( return true; } - fseg_free_page_low(inode, iblock, space, page_no, mtr); + fseg_free_page_low(inode, iblock, space, page_no, mtr +#ifdef BTR_CUR_HASH_ADAPT + , ahi +#endif /* BTR_CUR_HASH_ADAPT */ + ); buf_page_free(space, page_no, mtr); return false; } diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h index 43c45dcee0c..10765852529 100644 --- a/storage/innobase/include/fsp0fsp.h +++ b/storage/innobase/include/fsp0fsp.h @@ -493,29 +493,42 @@ fseg_free_page( bool fseg_page_is_free(fil_space_t* space, unsigned page) MY_ATTRIBUTE((nonnull, warn_unused_result)); -/**********************************************************************//** -Frees part of a segment. This function can be used to free a segment -by repeatedly calling this function in different mini-transactions. -Doing the freeing in a single mini-transaction might result in -too big a mini-transaction. + +/** Frees part of a segment. This function can be used to free +a segment by repeatedly calling this function in different +mini-transactions. Doing the freeing in a single mini-transaction +might result in too big a mini-transaction. +@param header segment header; NOTE: if the header resides on first + page of the frag list of the segment, this pointer + becomes obsolete after the last freeing step +@param mtr mini-transaction +@param ahi Drop the adaptive hash index @return whether the freeing was completed */ bool fseg_free_step( - fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header - resides on the first page of the frag list - of the segment, this pointer becomes obsolete - after the last freeing step */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) MY_ATTRIBUTE((warn_unused_result)); -/**********************************************************************//** -Frees part of a segment. Differs from fseg_free_step because this function -leaves the header page unfreed. + +/** Frees part of a segment. Differs from fseg_free_step because +this function leaves the header page unfreed. +@param header segment header which must reside on the first + fragment page of the segment +@param mtr mini-transaction +@param ahi drop the adaptive hash index @return whether the freeing was completed, except for the header page */ bool fseg_free_step_not_header( - fseg_header_t* header, /*!< in: segment header which must reside on - the first fragment page of the segment */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + fseg_header_t* header, + mtr_t* mtr +#ifdef BTR_CUR_HASH_ADAPT + ,bool ahi=false +#endif /* BTR_CUR_HASH_ADAPT */ + ) MY_ATTRIBUTE((warn_unused_result)); /** Reset the page type.