From cf18a5b6ef8360c3ce764bec121a243097386ae6 Mon Sep 17 00:00:00 2001 From: bsrikanth-mariadb Date: Fri, 29 May 2026 12:29:58 +0530 Subject: [PATCH] MDEV-39791: Handle count aggregate optimization for replay purpose During replay, when get_exact_record_count() is invoked from opt_sum_query(), hook the recorded table rows from the parsed context for each table, during the calculation of count. --- .../main/opt_context_load_stats_basic.result | 5 -- .../main/opt_context_load_stats_basic.test | 3 -- .../main/opt_context_replay_basic.result | 46 ++++++++++++++++ mysql-test/main/opt_context_replay_basic.test | 52 +++++++++++++++++++ sql/opt_context_store_replay.cc | 14 ++--- sql/opt_context_store_replay.h | 2 - sql/opt_sum.cc | 8 ++- 7 files changed, 111 insertions(+), 19 deletions(-) diff --git a/mysql-test/main/opt_context_load_stats_basic.result b/mysql-test/main/opt_context_load_stats_basic.result index 054dc827ed7aa..b1e4fc96de6ab 100644 --- a/mysql-test/main/opt_context_load_stats_basic.result +++ b/mysql-test/main/opt_context_load_stats_basic.result @@ -355,11 +355,6 @@ select * from t1 where a > 10; a b Warnings: Warning 4253 Failed to parse saved optimizer context: "file_stat_records" element not present at offset 1380. -set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].file_stat_records'); -select * from t1 where a > 10; -a b -Warnings: -Warning 4253 Failed to parse saved optimizer context: "file_stat_records" element not present at offset 1380. set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].indexes[0].index_name'); select * from t1 where a > 10; a b diff --git a/mysql-test/main/opt_context_load_stats_basic.test b/mysql-test/main/opt_context_load_stats_basic.test index 323648fe625cd..b572fe7303303 100644 --- a/mysql-test/main/opt_context_load_stats_basic.test +++ b/mysql-test/main/opt_context_load_stats_basic.test @@ -226,9 +226,6 @@ select * from t1 where a > 10; set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].file_stat_records'); select * from t1 where a > 10; -set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].file_stat_records'); -select * from t1 where a > 10; - set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].indexes[0].index_name'); select * from t1 where a > 10; diff --git a/mysql-test/main/opt_context_replay_basic.result b/mysql-test/main/opt_context_replay_basic.result index d08e3f2e06fef..127e31977c7ce 100644 --- a/mysql-test/main/opt_context_replay_basic.result +++ b/mysql-test/main/opt_context_replay_basic.result @@ -354,4 +354,50 @@ trace like '%foo%' select context like '%bar%' from information_schema.optimizer_context; context like '%bar%' 1 +drop table t1; +# +# MDEV-39791: Handle count aggregate optimization for replay purpose +# +create table t1 (a int primary key, b int not null, c varchar(10)); +insert into t1 select seq, seq%5, concat('a-', seq) from seq_1_to_10; +set optimizer_record_context=1; +explain +select count(a), count(b), count(c) from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +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 count(a), count(b), count(c) from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 +set optimizer_replay_context=''; +drop table t1; +create table t1 (a int primary key, b int not null, c varchar(10)); +insert into t1 select seq, seq%5, concat('a-', seq) from seq_1_to_10; +set optimizer_record_context=1; +explain +select count(c) from t1 where b = (select count(b) from t1) or a = (select count(a) from t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 10 Using where +3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +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 count(c) from t1 where b = (select count(b) from t1) or a = (select count(a) from t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 10 Using where +3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +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 d7f7b05f40b3e..d2a5c37b69c34 100644 --- a/mysql-test/main/opt_context_replay_basic.test +++ b/mysql-test/main/opt_context_replay_basic.test @@ -228,5 +228,57 @@ explain select * from t1 where a >'foo' or a < 'bar'; select trace like '%foo%' from information_schema.optimizer_trace; select context like '%bar%' from information_schema.optimizer_context; +drop table t1; + +--echo # +--echo # MDEV-39791: Handle count aggregate optimization for replay purpose +--echo # +create table t1 (a int primary key, b int not null, c varchar(10)); +insert into t1 select seq, seq%5, concat('a-', seq) from seq_1_to_10; + +set optimizer_record_context=1; + +explain +select count(a), count(b), count(c) from t1; +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 count(a), count(b), count(c) from t1; +set optimizer_replay_context=''; +drop table t1; +--remove_file "$MYSQLTEST_VARDIR/tmp/dump1.sql" + +create table t1 (a int primary key, b int not null, c varchar(10)); +insert into t1 select seq, seq%5, concat('a-', seq) from seq_1_to_10; + +set optimizer_record_context=1; +explain +select count(c) from t1 where b = (select count(b) from t1) or a = (select count(a) from t1); +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 count(c) from t1 where b = (select count(b) from t1) or a = (select count(a) from t1); + +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 d69db06bf35f1..0348731526c61 100644 --- a/sql/opt_context_store_replay.cc +++ b/sql/opt_context_store_replay.cc @@ -308,9 +308,9 @@ void dump_records_in_range_calls(List *rir_list, } } -static -void dump_recorded_table_calls(THD *thd, uchar *tbl_name, size_t tbl_name_len, - Json_writer *ctx_writer) +static void dump_recorded_table_calls(THD *thd, uchar *tbl_name, + size_t tbl_name_len, + Json_writer *ctx_writer) { table_context_for_store *table_context= thd->opt_ctx_recorder->search(tbl_name, tbl_name_len); @@ -1679,10 +1679,10 @@ bool Optimizer_context_replay::infuse_index_read_cost(const TABLE *tbl, } } - String warn_msg; + String warn_msg(256); warn_msg.append(tbl_name); warn_msg.append(STRING_WITH_LEN(" with key_number:")); - warn_msg.append(keynr); + warn_msg.q_append(keynr); warn_msg.append(STRING_WITH_LEN(", records:")); warn_msg.q_append_int64(records); warn_msg.append(STRING_WITH_LEN(", eq_ref:")); @@ -1801,10 +1801,10 @@ bool Optimizer_context_replay::infuse_records_in_range( } } - String warn_msg; + String warn_msg(256); warn_msg.append(tbl_name); warn_msg.append(STRING_WITH_LEN(" with key_number:")); - warn_msg.append(keynr); + warn_msg.q_append(keynr); warn_msg.append(STRING_WITH_LEN(" with min_key:")); warn_msg.append(min_key); warn_msg.append(STRING_WITH_LEN(" with max_key:")); diff --git a/sql/opt_context_store_replay.h b/sql/opt_context_store_replay.h index 00e74159d0f28..50648e9d20c52 100644 --- a/sql/opt_context_store_replay.h +++ b/sql/opt_context_store_replay.h @@ -148,8 +148,6 @@ class Optimizer_context_replay const key_range *max_range, ha_rows *records); private: - bool infuse_table_rows(const TABLE *tbl, ha_rows *rows); - THD *thd; /* Statistics that tables had before we've replaced them with values from diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 9ab9dcfaabe09..a21364be09b3c 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -78,13 +78,17 @@ static int maxmin_in_range(bool max_fl, Field* field, COND *cond); # Multiplication of number of rows in all tables */ -static ulonglong get_exact_record_count(List &tables) +static ulonglong get_exact_record_count(THD *thd, List &tables) { ulonglong count= 1; TABLE_LIST *tl; List_iterator ti(tables); while ((tl= ti++)) { + if (thd->opt_ctx_replay) + { + thd->opt_ctx_replay->infuse_table_rows(tl->table); + } ha_rows tmp= tl->table->file->records(); if (tmp == HA_POS_ERROR) return ULONGLONG_MAX; @@ -366,7 +370,7 @@ int opt_sum_query(THD *thd, { if (!is_exact_count) { - if ((count= get_exact_record_count(tables)) == ULONGLONG_MAX) + if ((count= get_exact_record_count(thd, tables)) == ULONGLONG_MAX) { /* Error from handler in counting rows. Don't optimize count() */ const_result= 0;