Skip to content

Commit 17dece4

Browse files
MDEV-39440: Failed to match the stats from replay context with the optimizer stats
The problem is that handler->multi_range_read_info_const() call in check_quick_select() returned rows equal to HA_POS_ERROR, both during context capture and replay. However, we never stored the ranges info into the context when rows=HA_POS_ERROR. However, during replay, we try to infuse stats for the given range, and since no match was found in the context, we produced a warning. Solution is to store range_info for all the ranges even when they get HA_POS_ERROR number of rows.
1 parent 1508c9d commit 17dece4

5 files changed

Lines changed: 80 additions & 23 deletions

File tree

mysql-test/main/opt_context_replay_basic.result

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,26 @@ id select_type table type possible_keys key key_len ref rows Extra
315315
1 SIMPLE t1 system NULL NULL NULL NULL 1
316316
set optimizer_replay_context='';
317317
drop table t1;
318+
#
319+
# MDEV-39440: Failed to match the stats from replay context with the optimizer stats
320+
#
321+
create table t1 (btn char(10) not null, key using HASH (btn)) engine=heap;
322+
insert into t1 values ("a"),("b"),("c"),("d");
323+
alter table t1 add column new_col char(1) not null, add key using HASH (btn,new_col), drop key btn;
324+
update t1 set new_col=left(btn,1);
325+
set optimizer_record_context=1;
326+
explain select * from t1 where btn="a";
327+
id select_type table type possible_keys key key_len ref rows Extra
328+
1 SIMPLE t1 ALL btn NULL NULL NULL 4 Using where
329+
select context into dumpfile "../../tmp/dump1.sql"
330+
from information_schema.optimizer_context;
331+
set optimizer_record_context=0;
332+
drop table t1;
333+
set optimizer_replay_context='opt_context';
334+
# Same query as above, must have same explain:
335+
explain select * from t1 where btn="a";
336+
id select_type table type possible_keys key key_len ref rows Extra
337+
1 SIMPLE t1 ALL btn NULL NULL NULL 4 Using where
338+
set optimizer_replay_context='';
339+
drop table t1;
318340
drop database db1;

mysql-test/main/opt_context_replay_basic.test

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,31 @@ set optimizer_replay_context='';
173173
--remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql"
174174
drop table t1;
175175

176+
--echo #
177+
--echo # MDEV-39440: Failed to match the stats from replay context with the optimizer stats
178+
--echo #
179+
create table t1 (btn char(10) not null, key using HASH (btn)) engine=heap;
180+
insert into t1 values ("a"),("b"),("c"),("d");
181+
alter table t1 add column new_col char(1) not null, add key using HASH (btn,new_col), drop key btn;
182+
update t1 set new_col=left(btn,1);
183+
184+
set optimizer_record_context=1;
185+
explain select * from t1 where btn="a";
186+
select context into dumpfile "../../tmp/dump1.sql"
187+
from information_schema.optimizer_context;
188+
set optimizer_record_context=0;
189+
drop table t1;
190+
--disable_query_log
191+
--disable_result_log
192+
--source "$MYSQLTEST_VARDIR/tmp/dump1.sql"
193+
--enable_query_log
194+
--enable_result_log
195+
set optimizer_replay_context='opt_context';
196+
--echo # Same query as above, must have same explain:
197+
explain select * from t1 where btn="a";
198+
199+
set optimizer_replay_context='';
200+
--remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql"
201+
drop table t1;
202+
176203
drop database db1;

sql/opt_context_store_replay.cc

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -775,13 +775,9 @@ Optimizer_context_recorder::search(uchar *tbl_name, size_t tbl_name_len)
775775
}
776776

777777
void Optimizer_context_recorder::record_multi_range_read_info_const(
778-
const TABLE_LIST *tbl,
779-
uint keynr,
780-
Range_print_enumerator *ranges,
781-
ha_rows rows,
782-
const Cost_estimate *cost,
783-
ha_rows max_index_blocks,
784-
ha_rows max_row_blocks)
778+
const TABLE_LIST *tbl, uint keynr, Range_print_enumerator *ranges,
779+
ha_rows rows, const Cost_estimate *cost, const ha_rows *max_index_blocks,
780+
const ha_rows *max_row_blocks)
785781
{
786782
/*
787783
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(
801797

802798
range_ctx->rows= rows;
803799
range_ctx->cost= *cost;
804-
range_ctx->max_index_blocks= max_index_blocks;
805-
range_ctx->max_row_blocks= max_row_blocks;
800+
if (rows != HA_POS_ERROR)
801+
{
802+
range_ctx->max_index_blocks= *max_index_blocks;
803+
range_ctx->max_row_blocks= *max_row_blocks;
804+
}
805+
else
806+
{
807+
// Not provided. Write 0.
808+
range_ctx->max_index_blocks= 0;
809+
range_ctx->max_row_blocks= 0;
810+
}
806811

807812
while (!ranges->next())
808813
{

sql/opt_context_store_replay.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,12 @@ class Optimizer_context_recorder
4747

4848
~Optimizer_context_recorder();
4949

50-
void record_multi_range_read_info_const(const TABLE_LIST *tbl,
51-
uint keynr,
50+
void record_multi_range_read_info_const(const TABLE_LIST *tbl, uint keynr,
5251
Range_print_enumerator *ranges,
5352
ha_rows rows,
5453
const Cost_estimate *cost,
55-
ha_rows max_index_blocks,
56-
ha_rows max_row_blocks);
54+
const ha_rows *max_index_blocks,
55+
const ha_rows *max_row_blocks);
5756

5857
void record_cost_index_read(const TABLE_LIST *tbl,
5958
uint key, ha_rows records, bool eq_ref,

sql/opt_range.cc

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12471,6 +12471,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit,
1247112471
ha_rows replay_ctx_max_index_blocks;
1247212472
ha_rows replay_ctx_max_row_blocks;
1247312473
bool replay_ctx_rc;
12474+
TABLE::OPT_RANGE *range= param->table->opt_range + keynr;
1247412475
DBUG_ENTER("check_quick_select");
1247512476

1247612477
/* Range not calculated yet */
@@ -12534,7 +12535,6 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit,
1253412535
param->quick_rows[keynr]= rows;
1253512536
if (rows != HA_POS_ERROR)
1253612537
{
12537-
TABLE::OPT_RANGE *range= param->table->opt_range + keynr;
1253812538
ha_rows table_records= param->table->stat_records();
1253912539
if (rows > table_records)
1254012540
{
@@ -12560,15 +12560,6 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit,
1256012560
range->max_row_blocks=
1256112561
MY_MIN(file->row_blocks(), rows * file->stats.block_size / IO_SIZE);
1256212562

12563-
if (Optimizer_context_recorder *rec= param->thd->opt_ctx_recorder)
12564-
{
12565-
Range_print_enumerator_impl range_iter(param, idx, tree);
12566-
rec->record_multi_range_read_info_const(param->table->pos_in_table_list,
12567-
keynr, &range_iter, rows, cost,
12568-
range->max_index_blocks,
12569-
range->max_row_blocks);
12570-
}
12571-
1257212563
if (update_tbl_stats)
1257312564
{
1257412565
param->table->opt_range_keys.set_bit(keynr);
@@ -12597,6 +12588,19 @@ ha_rows check_quick_select(PARAM *param, uint idx, ha_rows limit,
1259712588
}
1259812589
}
1259912590

12591+
if (Optimizer_context_recorder *rec= param->thd->opt_ctx_recorder)
12592+
{
12593+
Range_print_enumerator_impl range_iter(param, idx, tree);
12594+
/*
12595+
We pass range->max_index_blocks, and range->max_row_blocks by address,
12596+
as they might not have been initialized except when rows != HA_POS_ERROR.
12597+
This way, ASAN and valgrind also wouldn't complain.
12598+
*/
12599+
rec->record_multi_range_read_info_const(
12600+
param->table->pos_in_table_list, keynr, &range_iter, rows, cost,
12601+
&range->max_index_blocks, &range->max_row_blocks);
12602+
}
12603+
1260012604
/* Figure out if the key scan is ROR (returns rows in ROWID order) or not */
1260112605
enum ha_key_alg key_alg= param->table->key_info[seq.real_keyno].algorithm;
1260212606
if ((key_alg != HA_KEY_ALG_BTREE) && (key_alg!= HA_KEY_ALG_UNDEF))

0 commit comments

Comments
 (0)