diff --git a/mysql-test/main/opt_context_replay_basic.result b/mysql-test/main/opt_context_replay_basic.result index e7b541a9115cb..44fce84142b7a 100644 --- a/mysql-test/main/opt_context_replay_basic.result +++ b/mysql-test/main/opt_context_replay_basic.result @@ -315,4 +315,26 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 system NULL NULL NULL NULL 1 set optimizer_replay_context=''; drop table t1; +# +# MDEV-39440: Failed to match the stats from replay context with the optimizer stats +# +create table t1 (btn char(10) not null, key using HASH (btn)) engine=heap; +insert into t1 values ("a"),("b"),("c"),("d"); +alter table t1 add column new_col char(1) not null, add key using HASH (btn,new_col), drop key btn; +update t1 set new_col=left(btn,1); +set optimizer_record_context=1; +explain select * from t1 where btn="a"; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL btn NULL NULL NULL 4 Using where +select context into dumpfile "../../tmp/dump1.sql" +from information_schema.optimizer_context; +set optimizer_record_context=0; +drop table t1; +set optimizer_replay_context='opt_context'; +# Same query as above, must have same explain: +explain select * from t1 where btn="a"; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL btn NULL NULL NULL 4 Using where +set optimizer_replay_context=''; +drop table t1; drop database db1; diff --git a/mysql-test/main/opt_context_replay_basic.test b/mysql-test/main/opt_context_replay_basic.test index 7e987b202ff6f..70b712ac825df 100644 --- a/mysql-test/main/opt_context_replay_basic.test +++ b/mysql-test/main/opt_context_replay_basic.test @@ -173,4 +173,31 @@ set optimizer_replay_context=''; --remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql" drop table t1; +--echo # +--echo # MDEV-39440: Failed to match the stats from replay context with the optimizer stats +--echo # +create table t1 (btn char(10) not null, key using HASH (btn)) engine=heap; +insert into t1 values ("a"),("b"),("c"),("d"); +alter table t1 add column new_col char(1) not null, add key using HASH (btn,new_col), drop key btn; +update t1 set new_col=left(btn,1); + +set optimizer_record_context=1; +explain select * from t1 where btn="a"; +select context into dumpfile "../../tmp/dump1.sql" +from information_schema.optimizer_context; +set optimizer_record_context=0; +drop table t1; +--disable_query_log +--disable_result_log +--source "$MYSQLTEST_VARDIR/tmp/dump1.sql" +--enable_query_log +--enable_result_log +set optimizer_replay_context='opt_context'; +--echo # Same query as above, must have same explain: +explain select * from t1 where btn="a"; + +set optimizer_replay_context=''; +--remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql" +drop table t1; + drop database db1; diff --git a/sql/opt_context_store_replay.cc b/sql/opt_context_store_replay.cc index e0f9594a0dbca..4f1b1da38ae7e 100644 --- a/sql/opt_context_store_replay.cc +++ b/sql/opt_context_store_replay.cc @@ -775,13 +775,9 @@ Optimizer_context_recorder::search(uchar *tbl_name, size_t tbl_name_len) } void Optimizer_context_recorder::record_multi_range_read_info_const( - const TABLE_LIST *tbl, - uint keynr, - Range_print_enumerator *ranges, - ha_rows rows, - const Cost_estimate *cost, - ha_rows max_index_blocks, - ha_rows max_row_blocks) + const TABLE_LIST *tbl, uint keynr, Range_print_enumerator *ranges, + ha_rows rows, const Cost_estimate *cost, const ha_rows *max_index_blocks, + const ha_rows *max_row_blocks) { /* Do not record calls that are made at execution phase by "Range checked @@ -801,8 +797,17 @@ void Optimizer_context_recorder::record_multi_range_read_info_const( range_ctx->rows= rows; range_ctx->cost= *cost; - range_ctx->max_index_blocks= max_index_blocks; - range_ctx->max_row_blocks= max_row_blocks; + if (rows != HA_POS_ERROR) + { + range_ctx->max_index_blocks= *max_index_blocks; + range_ctx->max_row_blocks= *max_row_blocks; + } + else + { + // Not provided. Write 0. + range_ctx->max_index_blocks= 0; + range_ctx->max_row_blocks= 0; + } while (!ranges->next()) { diff --git a/sql/opt_context_store_replay.h b/sql/opt_context_store_replay.h index 916e56356d17f..1ec2d7a84e80a 100644 --- a/sql/opt_context_store_replay.h +++ b/sql/opt_context_store_replay.h @@ -47,13 +47,12 @@ class Optimizer_context_recorder ~Optimizer_context_recorder(); - void record_multi_range_read_info_const(const TABLE_LIST *tbl, - uint keynr, + void record_multi_range_read_info_const(const TABLE_LIST *tbl, uint keynr, Range_print_enumerator *ranges, ha_rows rows, const Cost_estimate *cost, - ha_rows max_index_blocks, - ha_rows max_row_blocks); + const ha_rows *max_index_blocks, + const ha_rows *max_row_blocks); void record_cost_index_read(const TABLE_LIST *tbl, uint key, ha_rows records, bool eq_ref, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index cf9e256bd2576..39b1d19622f0e 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -12471,6 +12471,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit, ha_rows replay_ctx_max_index_blocks; ha_rows replay_ctx_max_row_blocks; bool replay_ctx_rc; + TABLE::OPT_RANGE *range= param->table->opt_range + keynr; DBUG_ENTER("check_quick_select"); /* Range not calculated yet */ @@ -12534,7 +12535,6 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit, param->quick_rows[keynr]= rows; if (rows != HA_POS_ERROR) { - TABLE::OPT_RANGE *range= param->table->opt_range + keynr; ha_rows table_records= param->table->stat_records(); if (rows > table_records) { @@ -12560,15 +12560,6 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit, range->max_row_blocks= MY_MIN(file->row_blocks(), rows * file->stats.block_size / IO_SIZE); - if (Optimizer_context_recorder *rec= param->thd->opt_ctx_recorder) - { - Range_print_enumerator_impl range_iter(param, idx, tree); - rec->record_multi_range_read_info_const(param->table->pos_in_table_list, - keynr, &range_iter, rows, cost, - range->max_index_blocks, - range->max_row_blocks); - } - if (update_tbl_stats) { param->table->opt_range_keys.set_bit(keynr); @@ -12597,6 +12588,19 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit, } } + if (Optimizer_context_recorder *rec= param->thd->opt_ctx_recorder) + { + Range_print_enumerator_impl range_iter(param, idx, tree); + /* + We pass range->max_index_blocks, and range->max_row_blocks by address, + as they might not have been initialized except when rows != HA_POS_ERROR. + This way, ASAN and valgrind also wouldn't complain. + */ + rec->record_multi_range_read_info_const( + param->table->pos_in_table_list, keynr, &range_iter, rows, cost, + &range->max_index_blocks, &range->max_row_blocks); + } + /* Figure out if the key scan is ROR (returns rows in ROWID order) or not */ enum ha_key_alg key_alg= param->table->key_info[seq.real_keyno].algorithm; if ((key_alg != HA_KEY_ALG_BTREE) && (key_alg!= HA_KEY_ALG_UNDEF))