From 60125a77b7edc6fda16a16b15b0455b9e222971b Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 10 Jul 2024 11:43:03 +0530 Subject: [PATCH] MDEV-34474 InnoDB: Failing assertion: stat_n_leaf_pages > 0 in ha_innobase::estimate_rows_upper_bound - Column stat_value and sample_size in mysql.innodb_index_stats table is declared as BIGINT UNSIGNED without any check constraint. user manually updates the value of stat_value and sample_size to zero. InnoDB aborts the server while reading the statistics information because InnoDB expects at least one leaf page to exist for the index. - To fix this issue, InnoDB should interpret the value of stat_n_leaf_pages, stat_index_size in innodb_index_stats stat_clustered_index_size, stat_sum_of_other_index_sizes in innodb_table_stats as valid one even though user mentioned it as 0. --- .../suite/innodb/r/innodb_stats_fetch.result | 10 +++++++ .../suite/innodb/t/innodb_stats_fetch.test | 12 ++++++++ storage/innobase/dict/dict0stats.cc | 30 +++++++++++++------ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_stats_fetch.result b/mysql-test/suite/innodb/r/innodb_stats_fetch.result index df6bc4b0cf7..76d6b022f03 100644 --- a/mysql-test/suite/innodb/r/innodb_stats_fetch.result +++ b/mysql-test/suite/innodb/r/innodb_stats_fetch.result @@ -174,3 +174,13 @@ SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_VIRTUAL LIMIT ROWS EXAMINED 5; SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN LIMIT ROWS EXAMINED 5; SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS LIMIT ROWS EXAMINED 5; DROP TABLE t1; +# +# MDEV-34474 InnoDB: Failing assertion: stat_n_leaf_pages > 0 +# in ha_innobase::estimate_rows_upper_bound +# +CREATE TABLE t (c1 INT,c2 INT, +INDEX(c1))STATS_PERSISTENT=1 ENGINE=INNODB; +UPDATE mysql.innodb_index_stats SET stat_value=0 WHERE database_name like "test" and table_name like 't'; +UPDATE mysql.innodb_table_stats SET clustered_index_size= 0, sum_of_other_index_sizes=0 WHERE database_name like "test" and table_name like 't'; +UPDATE t SET c1=+1 ORDER BY c2; +DROP TABLE t; diff --git a/mysql-test/suite/innodb/t/innodb_stats_fetch.test b/mysql-test/suite/innodb/t/innodb_stats_fetch.test index 99fc115af1d..8968b48e0b2 100644 --- a/mysql-test/suite/innodb/t/innodb_stats_fetch.test +++ b/mysql-test/suite/innodb/t/innodb_stats_fetch.test @@ -96,3 +96,15 @@ SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_VIRTUAL LIMIT ROWS EXAMINED 5; SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN LIMIT ROWS EXAMINED 5; SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS LIMIT ROWS EXAMINED 5; DROP TABLE t1; + +--echo # +--echo # MDEV-34474 InnoDB: Failing assertion: stat_n_leaf_pages > 0 +--echo # in ha_innobase::estimate_rows_upper_bound +--echo # + +CREATE TABLE t (c1 INT,c2 INT, + INDEX(c1))STATS_PERSISTENT=1 ENGINE=INNODB; +UPDATE mysql.innodb_index_stats SET stat_value=0 WHERE database_name like "test" and table_name like 't'; +UPDATE mysql.innodb_table_stats SET clustered_index_size= 0, sum_of_other_index_sizes=0 WHERE database_name like "test" and table_name like 't'; +UPDATE t SET c1=+1 ORDER BY c2; +DROP TABLE t; diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 9b6abab162a..8ab8cec47e3 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -2665,25 +2665,34 @@ dict_stats_fetch_table_stats_step( break; case 1: /* mysql.innodb_table_stats.clustered_index_size */ - + { ut_a(dtype_get_mtype(type) == DATA_INT); ut_a(len == 8); table->stat_clustered_index_size - = (ulint) mach_read_from_8(data); - + = std::max(mach_read_from_8(data), 1); break; + } case 2: /* mysql.innodb_table_stats.sum_of_other_index_sizes */ - + { ut_a(dtype_get_mtype(type) == DATA_INT); ut_a(len == 8); - table->stat_sum_of_other_index_sizes + ulint stat_other_idx_size = (ulint) mach_read_from_8(data); + if (!stat_other_idx_size + && UT_LIST_GET_LEN(table->indexes) > 1) { + stat_other_idx_size + = UT_LIST_GET_LEN(table->indexes) - 1; + } + table->stat_sum_of_other_index_sizes + = std::max( + mach_read_from_8(data), + UT_LIST_GET_LEN(table->indexes) - 1); break; - + } default: /* someone changed SELECT @@ -2866,12 +2875,14 @@ dict_stats_fetch_index_stats_step( if (stat_name_len == 4 /* strlen("size") */ && strncasecmp("size", stat_name, stat_name_len) == 0) { - index->stat_index_size = (ulint) stat_value; + index->stat_index_size + = std::max(stat_value, 1); arg->stats_were_modified = true; } else if (stat_name_len == 12 /* strlen("n_leaf_pages") */ && strncasecmp("n_leaf_pages", stat_name, stat_name_len) == 0) { - index->stat_n_leaf_pages = (ulint) stat_value; + index->stat_n_leaf_pages + = std::max(stat_value, 1); arg->stats_were_modified = true; } else if (stat_name_len == 12 /* strlen("n_page_split") */ && strncasecmp("n_page_split", stat_name, stat_name_len) @@ -2951,7 +2962,8 @@ dict_stats_fetch_index_stats_step( index->stat_n_diff_key_vals[n_pfx - 1] = stat_value; if (sample_size != UINT64_UNDEFINED) { - index->stat_n_sample_sizes[n_pfx - 1] = sample_size; + index->stat_n_sample_sizes[n_pfx - 1] = + std::max(sample_size, 1); } else { /* hmm, strange... the user must have UPDATEd the table manually and SET sample_size = NULL */