diff --git a/mysql-test/main/opt_context_load_stats_basic.result b/mysql-test/main/opt_context_load_stats_basic.result index 054dc827ed7aa..d89209d1373d4 100644 --- a/mysql-test/main/opt_context_load_stats_basic.result +++ b/mysql-test/main/opt_context_load_stats_basic.result @@ -343,7 +343,7 @@ set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].name'); select * from t1 where a > 10; a b Warnings: -Warning 4253 Failed to parse saved optimizer context: "name" element not present at offset 1387. +Warning 4253 Failed to parse saved optimizer context: "name" element not present at offset 1409. set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].ddl'); select * from t1 where a > 10; a b @@ -354,12 +354,12 @@ set @opt_context=json_remove(@saved_opt_context_1, '$.list_contexts[0].file_stat 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. +Warning 4253 Failed to parse saved optimizer context: "file_stat_records" element not present at offset 1402. 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. +Warning 4253 Failed to parse saved optimizer context: "file_stat_records" element not present at offset 1402. 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/sql/opt_context_store_replay.cc b/sql/opt_context_store_replay.cc index 7692b34d9a1e5..c57e21eee4021 100644 --- a/sql/opt_context_store_replay.cc +++ b/sql/opt_context_store_replay.cc @@ -138,6 +138,13 @@ class records_in_range_call_record : public Sql_alloc ha_rows records; }; +class count_agg : public Sql_alloc +{ +public: + ulong call_number; + longlong count; +}; + /* structure to store all the index range records, and the cost for reading indexes, pertaining to a table @@ -152,6 +159,7 @@ class table_context_for_store : public Sql_alloc List irc_list; List rir_list; List const_tbl_ins_stmt_list; + List count_agg_list; }; namespace Show @@ -182,6 +190,8 @@ static bool parse_range_cost_estimate(MEM_ROOT*, json_engine_t *je, static int parse_records_in_range_context(MEM_ROOT *mem_root, json_engine_t *je, String *err_buf, records_in_range_call_record *rir_ctx); +static int parse_count_agg_context(MEM_ROOT *mem_root, json_engine_t *je, + String *err_buf, count_agg *out); static char *strdup_root(MEM_ROOT *root, const String *str) { @@ -299,6 +309,20 @@ void dump_records_in_range_calls(List *rir_list, } } +static void dump_count_agg_calls(List *count_agg_list, + Json_writer *ctx_writer) +{ + Json_writer_array list_cnt_agg_wrapper(ctx_writer, "list_count_agg"); + List_iterator li(*count_agg_list); + + while (count_agg *cnt_agg= li++) + { + Json_writer_object obj(ctx_writer); + obj.add("call_number", cnt_agg->call_number); + obj.add("count", cnt_agg->count); + } +} + static void dump_recorded_table_calls(THD *thd, uchar *tbl_name, size_t tbl_name_len, Json_writer *ctx_writer) @@ -312,6 +336,7 @@ void dump_recorded_table_calls(THD *thd, uchar *tbl_name, size_t tbl_name_len, dump_mrr_info_calls(&table_context->mrr_list, ctx_writer); dump_index_read_calls(&table_context->irc_list, ctx_writer); dump_records_in_range_calls(&table_context->rir_list, ctx_writer); + dump_count_agg_calls(&table_context->count_agg_list, ctx_writer); } /* @@ -794,6 +819,23 @@ Optimizer_context_recorder::~Optimizer_context_recorder() my_hash_free(&tbl_ctx_hash); } +void Optimizer_context_recorder::record_count_agg(TABLE *tbl, longlong count) +{ + table_context_for_store *table_ctx= + get_table_context(tbl->pos_in_table_list); + + if (unlikely(!table_ctx)) + return; // OOM + + auto *count_agg_ctx= new (mem_root) count_agg; + if (unlikely(!count_agg_ctx)) + return; // OOM + + count_agg_ctx->call_number= ++count_agg_counter; + count_agg_ctx->count= count; + table_ctx->count_agg_list.push_back(count_agg_ctx, mem_root); +} + bool Optimizer_context_recorder::has_records() { return tbl_ctx_hash.records > 0; @@ -1004,6 +1046,7 @@ class table_context_for_replay : public Sql_alloc List ranges_list; List irc_list; List rir_list; + List count_agg_list; }; /* @@ -1211,7 +1254,8 @@ static int parse_table_context(MEM_ROOT *mem_root, json_engine_t *je, Read_named_member array[]= { {"name", Read_string(mem_root, &table_ctx->name), false}, {"file_stat_records", - Read_non_neg_integer(&table_ctx->file_stat_records), + Read_non_neg_integer( + &table_ctx->file_stat_records), false}, {"read_cost_io", Read_double(&table_ctx->read_cost_io), false}, {"read_cost_cpu", Read_double(&table_ctx->read_cost_cpu), false}, @@ -1231,6 +1275,10 @@ static int parse_table_context(MEM_ROOT *mem_root, json_engine_t *je, Read_array_into_list( mem_root, &table_ctx->rir_list, parse_records_in_range_context), true}, + {"list_count_agg", + Read_array_into_list(mem_root, &table_ctx->count_agg_list, + parse_count_agg_context), + true}, {NULL, Read_double(NULL), true}}; return parse_context_obj_from_json_array(je, err_buf, err_msg, array); @@ -1412,6 +1460,34 @@ static int parse_records_in_range_context(MEM_ROOT *mem_root, return parse_context_obj_from_json_array(je, err_buf, err_msg, array); } +/* + Parses the count_agg information for reading count_agg + JSON structure of the optimizer context. + To be specific, single array element of list_count_agg + is parsed in this method. + Refer to the file opt_context_schema.inc, and + the description at the start of this file. + + @return + 0 OK + 1 Parse Error + -1 EOF +*/ +static int parse_count_agg_context(MEM_ROOT *mem_root, json_engine_t *je, + String *err_buf, count_agg *out) +{ + const char *err_msg= "Expected an object in the count_agg array"; + + Read_named_member array[]= { + {"call_number", + Read_non_neg_integer(&out->call_number), false}, + {"count", Read_non_neg_integer(&out->count), + false}, + {NULL, Read_double(NULL), true}}; + + return parse_context_obj_from_json_array(je, err_buf, err_msg, array); +} + Optimizer_context_replay::Optimizer_context_replay(THD *thd_arg) : thd(thd_arg) { parse(); // TODO: error handling? @@ -1625,10 +1701,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:")); @@ -1747,10 +1823,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:")); @@ -1763,6 +1839,42 @@ bool Optimizer_context_replay::infuse_records_in_range( return true; } +bool Optimizer_context_replay::infuse_count_agg(const TABLE *tbl, + longlong *count) +{ + if (!has_records() || !is_base_table(tbl->pos_in_table_list)) + return true; + + String tbl_name; + append_full_table_name(tbl->pos_in_table_list, &tbl_name); + count_agg_counter++; + + if (table_context_for_replay *tbl_ctx= + find_table_context(tbl_name.c_ptr_safe())) + { + List_iterator iter(tbl_ctx->count_agg_list); + while (count_agg *rec= iter++) + { + if (count_agg_counter == rec->call_number) + { + *count= rec->count; + return false; + } + } + } + + String warn_msg(256); + warn_msg.append(tbl_name); + warn_msg.append(STRING_WITH_LEN(" with count_agg call_number: ")); + warn_msg.q_append_int64(count_agg_counter); + push_warning_printf( + thd, Sql_condition::WARN_LEVEL_WARN, + ER_JSON_OPTIMIZER_REPLAY_CONTEXT_MATCH_FAILED, + ER_THD(thd, ER_JSON_OPTIMIZER_REPLAY_CONTEXT_MATCH_FAILED), + warn_msg.c_ptr_safe(), "list_count_agg"); + return true; +} + /* @brief restore the saved stats for the tables, and indexes that were diff --git a/sql/opt_context_store_replay.h b/sql/opt_context_store_replay.h index 1ec2d7a84e80a..31ba3121a84b6 100644 --- a/sql/opt_context_store_replay.h +++ b/sql/opt_context_store_replay.h @@ -71,7 +71,7 @@ class Optimizer_context_recorder /* use table->record[0] */ record_table_row(tbl, 0); } - + void record_count_agg(TABLE *tbl, longlong count); bool has_records(); table_context_for_store *search(uchar *tbl_name, size_t tbl_name_len); @@ -88,6 +88,10 @@ class Optimizer_context_recorder table_context_for_store *get_table_context(const TABLE_LIST *tbl); static const uchar *get_tbl_ctx_key(const void *entry_, size_t *length, my_bool flags); + /* + counter that tracks count_agg calls + */ + ulong count_agg_counter= 0; }; @@ -145,6 +149,7 @@ class Optimizer_context_replay bool infuse_records_in_range(const TABLE *tbl, const KEY_PART_INFO *key_part, uint keynr, const key_range *min_range, const key_range *max_range, ha_rows *records); + bool infuse_count_agg(const TABLE *tbl, longlong *count); private: bool infuse_table_rows(const TABLE *tbl, ha_rows *rows); @@ -164,6 +169,10 @@ class Optimizer_context_replay void store_range_contexts(const TABLE *tbl, const char *idx_name, List *list); table_context_for_replay *find_table_context(const char *name); + /* + counter that tracks count_agg calls + */ + ulong count_agg_counter= 0; #ifndef DBUG_OFF void dbug_print_read_stats(); #endif diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 9ab9dcfaabe09..f920f0fd7ea58 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -374,8 +374,28 @@ int opt_sum_query(THD *thd, } is_exact_count= 1; // count is now exact } - ((Item_sum_count*) item)->make_const((longlong) count); + Item_sum_count *cnt_item= static_cast(item); + cnt_item->make_const((longlong) count); recalc_const_item= 1; + Item *expr= item_sum->get_arg(0); + if (expr && ((expr->used_tables() & OUTER_REF_TABLE_BIT) == 0) && + expr->real_item()->type() == Item::FIELD_ITEM) + { + Item_field *item_field= (Item_field *) (expr->real_item()); + TABLE *table= item_field->field->table; + if (Optimizer_context_replay *replay= thd->opt_ctx_replay) + { + longlong tmp_count; + if (!replay->infuse_count_agg(table, &tmp_count)) + { + count= tmp_count; + } + } + if (Optimizer_context_recorder *rec= thd->opt_ctx_recorder) + { + rec->record_count_agg(table, count); + } + } } else const_result= 0;