mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 04:22:27 +01:00
MDEV-13534 InnoDB STATS_PERSISTENT fails to ignore garbage delete-mark flag on node pointer pages
This bug was a regression caused by MDEV-12698. On non-leaf pages, the delete-mark flag in the node pointer records is basically garbage. (Delete-marking only makes sense at the leaf level anyway. The purpose of the delete-mark is to tell MVCC, locking and purge that a leaf-level record does not exist in the READ UNCOMMITTED view, but it used to exist.) Node pointer records and non-leaf pages are glue that attaches multiple leaf pages to an index. This glue is supposed to be transparent to the transactional layer. When a page is split, InnoDB creates a node pointer record out of the child page record that the cursor is positioned on. The node pointer record for the parent page will be a copy of the child page record, amended with the child page number. If the child page record happened to carry the delete-mark flag, then the node pointer record would also carry this flag (even though the flag makes no sense outside child pages). (On a related note, for the first node pointer record in the first node pointer page of each tree level, if the MIN_REC_FLAG is set, the rest of the record contents (except the child page number) is basically garbage. From this garbage you could deduce at which point the child was originally split.) page_scan_method_t: Replace with bool, as there are only 2 values. dict_stats_scan_page(): Replace the parameter scan_method with is_leaf. Ignore the bogus (garbage) delete-mark flag if !is_leaf.
This commit is contained in:
parent
ae0759ad45
commit
e7bf8bca2f
3 changed files with 62 additions and 16 deletions
|
@ -0,0 +1,23 @@
|
|||
#
|
||||
# MDEV-13534 InnoDB STATS_PERSISTENT fails to ignore
|
||||
# garbage delete-mark flag on node pointer pages
|
||||
#
|
||||
CREATE TABLE t(a INT UNSIGNED PRIMARY KEY)
|
||||
ENGINE=InnoDB STATS_PERSISTENT=1 STATS_SAMPLE_PAGES=1;
|
||||
BEGIN;
|
||||
SET @save_debug = @@GLOBAL.innodb_limit_optimistic_insert_debug;
|
||||
SET GLOBAL innodb_limit_optimistic_insert_debug=2;
|
||||
INSERT t VALUES(1),(5);
|
||||
DELETE FROM t;
|
||||
INSERT t VALUES(4);
|
||||
DELETE FROM t;
|
||||
INSERT t VALUES(3);
|
||||
DELETE FROM t;
|
||||
SET GLOBAL innodb_limit_optimistic_insert_debug = @save_debug;
|
||||
connect con1, localhost, root,,;
|
||||
ANALYZE TABLE t;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t analyze status OK
|
||||
disconnect con1;
|
||||
connection default;
|
||||
DROP TABLE t;
|
32
mysql-test/suite/innodb/t/innodb_stats_persistent_debug.test
Normal file
32
mysql-test/suite/innodb/t/innodb_stats_persistent_debug.test
Normal file
|
@ -0,0 +1,32 @@
|
|||
--source include/have_innodb.inc
|
||||
--source include/have_debug.inc
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-13534 InnoDB STATS_PERSISTENT fails to ignore
|
||||
--echo # garbage delete-mark flag on node pointer pages
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t(a INT UNSIGNED PRIMARY KEY)
|
||||
ENGINE=InnoDB STATS_PERSISTENT=1 STATS_SAMPLE_PAGES=1;
|
||||
|
||||
BEGIN;
|
||||
# Create an index tree of height 3.
|
||||
# This is adapted from innodb.innodb_bug14676111.
|
||||
|
||||
SET @save_debug = @@GLOBAL.innodb_limit_optimistic_insert_debug;
|
||||
SET GLOBAL innodb_limit_optimistic_insert_debug=2;
|
||||
|
||||
INSERT t VALUES(1),(5);
|
||||
DELETE FROM t;
|
||||
INSERT t VALUES(4);
|
||||
DELETE FROM t;
|
||||
INSERT t VALUES(3);
|
||||
DELETE FROM t;
|
||||
SET GLOBAL innodb_limit_optimistic_insert_debug = @save_debug;
|
||||
|
||||
connect(con1, localhost, root,,);
|
||||
ANALYZE TABLE t;
|
||||
disconnect con1;
|
||||
|
||||
connection default;
|
||||
DROP TABLE t;
|
|
@ -1335,16 +1335,6 @@ dict_stats_analyze_index_level(
|
|||
mem_heap_free(heap);
|
||||
}
|
||||
|
||||
/* aux enum for controlling the behavior of dict_stats_scan_page() @{ */
|
||||
enum page_scan_method_t {
|
||||
/** scan the records on the given page, counting the number
|
||||
of distinct ones; @see srv_stats_include_delete_marked */
|
||||
COUNT_ALL_NON_BORING,
|
||||
/** quit on the first record that differs from its right neighbor */
|
||||
QUIT_ON_FIRST_NON_BORING
|
||||
};
|
||||
/* @} */
|
||||
|
||||
/** Scan a page, reading records from left to right and counting the number
|
||||
of distinct records (looking only at the first n_prefix
|
||||
columns) and the number of external pages pointed by records from this page.
|
||||
|
@ -1361,7 +1351,7 @@ be big enough)
|
|||
@param[in] index index of the page
|
||||
@param[in] page the page to scan
|
||||
@param[in] n_prefix look at the first n_prefix columns
|
||||
@param[in] scan_method scan to the end of the page or not
|
||||
@param[in] is_leaf whether this is the leaf page
|
||||
@param[out] n_diff number of distinct records encountered
|
||||
@param[out] n_external_pages if this is non-NULL then it will be set
|
||||
to the number of externally stored pages which were encountered
|
||||
|
@ -1376,7 +1366,7 @@ dict_stats_scan_page(
|
|||
const dict_index_t* index,
|
||||
const page_t* page,
|
||||
ulint n_prefix,
|
||||
page_scan_method_t scan_method,
|
||||
bool is_leaf,
|
||||
ib_uint64_t* n_diff,
|
||||
ib_uint64_t* n_external_pages)
|
||||
{
|
||||
|
@ -1388,8 +1378,9 @@ dict_stats_scan_page(
|
|||
Because offsets1,offsets2 should be big enough,
|
||||
this memory heap should never be used. */
|
||||
mem_heap_t* heap = NULL;
|
||||
ut_ad(is_leaf == page_is_leaf(page));
|
||||
const rec_t* (*get_next)(const rec_t*)
|
||||
= srv_stats_include_delete_marked
|
||||
= !is_leaf || srv_stats_include_delete_marked
|
||||
? page_rec_get_next_const
|
||||
: page_rec_get_next_non_del_marked;
|
||||
|
||||
|
@ -1440,7 +1431,7 @@ dict_stats_scan_page(
|
|||
|
||||
(*n_diff)++;
|
||||
|
||||
if (scan_method == QUIT_ON_FIRST_NON_BORING) {
|
||||
if (!is_leaf) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1566,7 +1557,7 @@ dict_stats_analyze_index_below_cur(
|
|||
/* search for the first non-boring record on the page */
|
||||
offsets_rec = dict_stats_scan_page(
|
||||
&rec, offsets1, offsets2, index, page, n_prefix,
|
||||
QUIT_ON_FIRST_NON_BORING, n_diff, NULL);
|
||||
false, n_diff, NULL);
|
||||
|
||||
/* pages on level > 0 are not allowed to be empty */
|
||||
ut_a(offsets_rec != NULL);
|
||||
|
@ -1611,7 +1602,7 @@ dict_stats_analyze_index_below_cur(
|
|||
|
||||
offsets_rec = dict_stats_scan_page(
|
||||
&rec, offsets1, offsets2, index, page, n_prefix,
|
||||
COUNT_ALL_NON_BORING, n_diff,
|
||||
true, n_diff,
|
||||
n_external_pages);
|
||||
|
||||
#if 0
|
||||
|
|
Loading…
Reference in a new issue