From 3ef25d4389b96d10a953a5a2cfd7495fed8d1e69 Mon Sep 17 00:00:00 2001 From: zhanghongxing <15150573650@163.com> Date: Tue, 7 Feb 2023 11:53:03 +0000 Subject: [PATCH] feat(tianmu):improve the code quality and refactoring derived table optimize for tianmu(#1277, #1276, #1258) --- mysql-test/suite/tianmu/r/issue1258.result | 43 ++ mysql-test/suite/tianmu/t/issue1258.test | 46 ++ sql/sql_derived.cc | 1 - sql/sql_lex.h | 5 +- sql/sql_optimizer.cc | 667 ++++++++++----------- sql/sql_optimizer.h | 16 +- sql/sql_parse.cc | 12 +- sql/sql_select.cc | 4 +- sql/sql_select.h | 2 +- sql/table.h | 3 + storage/tianmu/core/engine.h | 2 +- storage/tianmu/core/engine_execute.cpp | 156 ++++- storage/tianmu/core/query_compile.cpp | 2 +- storage/tianmu/handler/ha_my_tianmu.cpp | 7 +- storage/tianmu/handler/ha_my_tianmu.h | 3 +- 15 files changed, 589 insertions(+), 380 deletions(-) create mode 100644 mysql-test/suite/tianmu/r/issue1258.result create mode 100644 mysql-test/suite/tianmu/t/issue1258.test diff --git a/mysql-test/suite/tianmu/r/issue1258.result b/mysql-test/suite/tianmu/r/issue1258.result new file mode 100644 index 000000000..3250ec218 --- /dev/null +++ b/mysql-test/suite/tianmu/r/issue1258.result @@ -0,0 +1,43 @@ +DROP DATABASE IF EXISTS issue1258_test; +CREATE DATABASE issue1258_test; +USE issue1258_test; +set sql_mode='STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; +Warnings: +Warning 3135 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict mode. They will be merged with strict mode in a future release. +CREATE TABLE `t_test` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`first_name` varchar(10) NOT NULL, +`last_name` varchar(10) NOT NULL, +`sex` varchar(5) NOT NULL, +`score` int(11) NOT NULL, +`copy_id` int(11) NOT NULL, +PRIMARY KEY (`id`) +) ENGINE=TIANMU DEFAULT CHARSET=utf8mb4; +insert into t_test values(1,"syz1","stonedb1","nan",99,21); +insert into t_test values(2,"syz2","stonedb2","nan",99,22); +insert into t_test values(3,"syz3","stonedb3","nan",99,23); +insert into t_test values(4,"syz4","stonedb4","nan",99,24); +insert into t_test values(5,"syz5","stonedb5","nan",99,25); +SELECT +bb.first_name, +bb.last_name +FROM +( +SELECT +count(a.first_name ) AS first_name, +SUBSTR( a.last_name, 1, 10 ) AS last_name +FROM +t_test a, +t_test b +WHERE +a.id = b.id +GROUP BY +SUBSTR( a.last_name, 1, 10 ) +) bb; +first_name last_name +1 stonedb1 +1 stonedb2 +1 stonedb3 +1 stonedb4 +1 stonedb5 +DROP DATABASE issue1258_test; diff --git a/mysql-test/suite/tianmu/t/issue1258.test b/mysql-test/suite/tianmu/t/issue1258.test new file mode 100644 index 000000000..5675229fa --- /dev/null +++ b/mysql-test/suite/tianmu/t/issue1258.test @@ -0,0 +1,46 @@ +--source include/have_tianmu.inc + +--disable_warnings +DROP DATABASE IF EXISTS issue1258_test; +--enable_warnings + +CREATE DATABASE issue1258_test; + +USE issue1258_test; + +set sql_mode='STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; + +CREATE TABLE `t_test` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `first_name` varchar(10) NOT NULL, + `last_name` varchar(10) NOT NULL, + `sex` varchar(5) NOT NULL, + `score` int(11) NOT NULL, + `copy_id` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=TIANMU DEFAULT CHARSET=utf8mb4; + +insert into t_test values(1,"syz1","stonedb1","nan",99,21); +insert into t_test values(2,"syz2","stonedb2","nan",99,22); +insert into t_test values(3,"syz3","stonedb3","nan",99,23); +insert into t_test values(4,"syz4","stonedb4","nan",99,24); +insert into t_test values(5,"syz5","stonedb5","nan",99,25); + +SELECT + bb.first_name, + bb.last_name +FROM + ( +SELECT + count(a.first_name ) AS first_name, + SUBSTR( a.last_name, 1, 10 ) AS last_name +FROM + t_test a, + t_test b +WHERE + a.id = b.id +GROUP BY + SUBSTR( a.last_name, 1, 10 ) + ) bb; + +DROP DATABASE issue1258_test; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 8964bc110..4037568cd 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -222,7 +222,6 @@ bool TABLE_LIST::optimize_derived(THD *thd) DBUG_RETURN(false); } - /** Create result table for a materialized derived table/view. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 69996f055..7c4c60d8d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -655,10 +655,10 @@ class st_select_lex_unit: public Sql_alloc bool prepare(THD *thd, Query_result *result, ulonglong added_options, ulonglong removed_options); bool optimize(THD *thd); - //TIANMU UPGRADE END + int optimize_for_tianmu(); int optimize_after_tianmu(); - //END + bool execute(THD *thd); bool explain(THD *ethd); bool cleanup(bool full); @@ -1416,6 +1416,7 @@ class st_select_lex: public Sql_alloc bool setup_conds(THD *thd); bool prepare(THD *thd); bool optimize(THD *thd); + bool optimize_select_for_tianmu(THD *thd); void reset_nj_counters(List *join_list= NULL); bool check_only_full_group_by(THD *thd); diff --git a/sql/sql_optimizer.cc b/sql/sql_optimizer.cc index 9735797a0..a8f654cff 100644 --- a/sql/sql_optimizer.cc +++ b/sql/sql_optimizer.cc @@ -137,7 +137,7 @@ static uint32 get_key_length_tmp_table(Item *item); @retval 1 Error, error code saved in member JOIN::error. */ int -JOIN::optimize(unsigned char part) //TIANMU UPGRADE +JOIN::optimize(OptimizePhase phase) { uint no_jbuf_after= UINT_MAX; @@ -145,363 +145,363 @@ JOIN::optimize(unsigned char part) //TIANMU UPGRADE assert(select_lex->leaf_table_count == 0 || thd->lex->is_query_tables_locked() || select_lex == unit->fake_select_lex); - //assert(tables == 0 && - // primary_tables == 0 && - // tables_list == (TABLE_LIST*)1); - //TIANMU UPGRADE BEGIN + /* - Two more values of part were introduced part=3 and part=4. The main reason is to break optimization in sense of part=2 in point - where all transformations of LOJ conditions are finished. The optimization is continued in case we switch to MySQL. - The case was wrong result in "select * from t1 left join t2 on a1=b1 and b1=3 where a1=1;". That was due to optimization - of ON condition "a1=b1 and b1=3" into "a1=b1 and b1=1" which after part=2 would be transformed to FALSE. Part=3 does this transformation. - However, it has to be stopped at some point (compared to part=2) to avoid rest of optimizations, e.g., creation of temporary tables. (P.S.) + Two more values of part were introduced phase=Finish_LOJ_Transform and part=Done_Optimization. The main reason is to + break optimization in sense of part=After_LOJ_Transform in point where all transformations of LOJ conditions are finished. + The optimization is continued in case we switch to MySQL.The case was wrong result in "select * from t1 left join t2 on + a1=b1 and b1=3 where a1=1;". That was due to optimization of ON condition "a1=b1 and b1=3" into "a1=b1 and b1=1" which + after part=After_LOJ_Transform would be transformed to FALSE. Part=Finish_LOJ_Transform does this transformation. + However, it has to be stopped at some point (compared to part=After_LOJ_Transform) to avoid rest of optimizations, + e.g., creation of temporary tables. (P.S.) */ - //const bool first_optimization= select_lex->first_cond_optimization; + Opt_trace_context * const trace= &thd->opt_trace; Opt_trace_object trace_wrapper(trace); Opt_trace_object trace_optimize(trace, "join_optimization"); trace_optimize.add_select_number(select_lex->select_number); Opt_trace_array trace_steps(trace, "steps"); - if(part != 4) { - if (part==0 || part==1) + if(phase != OptimizePhase::Done_Optimization) + { + if (phase == OptimizePhase::Beginning || phase == OptimizePhase::Before_LOJ_Transform) { - //END - // to prevent double initialization on EXPLAIN - if (optimized) - DBUG_RETURN(0); + assert(tables == 0 && primary_tables == 0 && tables_list == (TABLE_LIST*)1); + + // to prevent double initialization on EXPLAIN + if (optimized) + DBUG_RETURN(0); - Prepare_error_tracker tracker(thd); + Prepare_error_tracker tracker(thd); - DEBUG_SYNC(thd, "before_join_optimize"); + DEBUG_SYNC(thd, "before_join_optimize"); - THD_STAGE_INFO(thd, stage_optimizing); + THD_STAGE_INFO(thd, stage_optimizing); - if (select_lex->first_execution) - { - /** - @todo - This query block didn't transform itself in SELECT_LEX::prepare(), so - belongs to a parent query block. That parent, or its parents, had to - transform us - it has not; maybe it is itself in prepare() and - evaluating the present query block as an Item_subselect. Such evaluation - in prepare() is expected to be a rare case to be eliminated in the - future ("SET x=(subq)" is one such case; because it locks tables before - prepare()). - */ - if (select_lex->apply_local_transforms(thd, false)) - DBUG_RETURN(error= 1); - } - //TIANMU UPGRADE - /* - Opt_trace_context * const trace= &thd->opt_trace; - Opt_trace_object trace_wrapper(trace); - Opt_trace_object trace_optimize(trace, "join_optimization"); - trace_optimize.add_select_number(select_lex->select_number); - Opt_trace_array trace_steps(trace, "steps"); - */ - //END - count_field_types(select_lex, &tmp_table_param, all_fields, false, false); + if (select_lex->first_execution) + { + /** + @todo + This query block didn't transform itself in SELECT_LEX::prepare(), so + belongs to a parent query block. That parent, or its parents, had to + transform us - it has not; maybe it is itself in prepare() and + evaluating the present query block as an Item_subselect. Such evaluation + in prepare() is expected to be a rare case to be eliminated in the + future ("SET x=(subq)" is one such case; because it locks tables before + prepare()). + */ + if (select_lex->apply_local_transforms(thd, false)) + DBUG_RETURN(error= 1); + } + + count_field_types(select_lex, &tmp_table_param, all_fields, false, false); - assert(tmp_table_param.sum_func_count == 0 || - group_list || implicit_grouping); + assert(tmp_table_param.sum_func_count == 0 || + group_list || implicit_grouping); - if (select_lex->olap == ROLLUP_TYPE && optimize_rollup()) - DBUG_RETURN(true); /* purecov: inspected */ + if (select_lex->olap == ROLLUP_TYPE && optimize_rollup()) + DBUG_RETURN(true); /* purecov: inspected */ - if (alloc_func_list()) - DBUG_RETURN(1); /* purecov: inspected */ + if (alloc_func_list()) + DBUG_RETURN(1); /* purecov: inspected */ - if (select_lex->get_optimizable_conditions(thd, &where_cond, &having_cond)) - DBUG_RETURN(1); + if (select_lex->get_optimizable_conditions(thd, &where_cond, &having_cond)) + DBUG_RETURN(1); + + set_optimized(); + + tables_list= select_lex->get_table_list(); + + /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ + /* + Run optimize phase for all derived tables/views used in this SELECT, + including those in semi-joins. + */ + if (select_lex->materialized_derived_table_count) + { + for (TABLE_LIST *tl= select_lex->leaf_tables; tl; tl= tl->next_leaf) + { + if (phase == OptimizePhase::Beginning) + { + if (tl->is_view_or_derived() && tl->optimize_derived(thd)) + DBUG_RETURN(1); + } + if (phase == OptimizePhase::Before_LOJ_Transform) + { + if (tl->is_view_or_derived() && tl->optimize_derived_for_tianmu(thd)) + DBUG_RETURN(1); + } + } + } - set_optimized(); + /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ + row_limit= ((select_distinct || order || group_list) ? + HA_POS_ERROR : unit->select_limit_cnt); + // m_select_limit is used to decide if we are likely to scan the whole table. + m_select_limit= unit->select_limit_cnt; - tables_list= select_lex->get_table_list(); + if (unit->first_select()->active_options() & OPTION_FOUND_ROWS) + { + /* + Calculate found rows if + - LIMIT is set, and + - Query block is not equipped with "braces". In this case, each + query block must be calculated fully and the limit is applied on + the final UNION evaluation. + */ + calc_found_rows= m_select_limit != HA_POS_ERROR && !select_lex->braces; + } + if (having_cond || calc_found_rows) + m_select_limit= HA_POS_ERROR; - /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ - /* - Run optimize phase for all derived tables/views used in this SELECT, - including those in semi-joins. - */ - if (select_lex->materialized_derived_table_count) - { - for (TABLE_LIST *tl= select_lex->leaf_tables; tl; tl= tl->next_leaf) + if (unit->select_limit_cnt == 0 && !calc_found_rows) + { + zero_result_cause= "Zero limit"; + best_rowcount= 0; + goto setup_subq_exit; + } + } // END phase=OptimizePhase::Beginning||phase=OptimizePhase::Before_LOJ_Transform + if (where_cond || select_lex->outer_join) { - if (tl->is_view_or_derived() && tl->optimize_derived(thd)) + if (optimize_cond(thd, &where_cond, &cond_equal, + &select_lex->top_join_list, &select_lex->cond_value,phase)) + { + error= 1; + DBUG_PRINT("error",("Error from optimize_cond")); DBUG_RETURN(1); + } + if (select_lex->cond_value == Item::COND_FALSE) + { + zero_result_cause= "Impossible WHERE"; + best_rowcount= 0; + goto setup_subq_exit; + } } - } - - /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ - - row_limit= ((select_distinct || order || group_list) ? - HA_POS_ERROR : unit->select_limit_cnt); - // m_select_limit is used to decide if we are likely to scan the whole table. - m_select_limit= unit->select_limit_cnt; - - if (unit->first_select()->active_options() & OPTION_FOUND_ROWS) - { - /* - Calculate found rows if - - LIMIT is set, and - - Query block is not equipped with "braces". In this case, each - query block must be calculated fully and the limit is applied on - the final UNION evaluation. - */ - calc_found_rows= m_select_limit != HA_POS_ERROR && !select_lex->braces; - } - if (having_cond || calc_found_rows) - m_select_limit= HA_POS_ERROR; - - if (unit->select_limit_cnt == 0 && !calc_found_rows) - { - zero_result_cause= "Zero limit"; - best_rowcount= 0; - goto setup_subq_exit; - } - }//TIANMU UPGRADE END part=0||part=1 - if (where_cond || select_lex->outer_join) - { - if (optimize_cond(thd, &where_cond, &cond_equal, - &select_lex->top_join_list, &select_lex->cond_value,part))//TIANMU UPGRADE + if (having_cond) { - error= 1; - DBUG_PRINT("error",("Error from optimize_cond")); - DBUG_RETURN(1); + if (optimize_cond(thd, &having_cond, &cond_equal, NULL, + &select_lex->having_value, phase)) + { + error= 1; + DBUG_PRINT("error",("Error from optimize_cond")); + DBUG_RETURN(1); + } + if (select_lex->having_value == Item::COND_FALSE) + { + zero_result_cause= "Impossible HAVING"; + best_rowcount= 0; + goto setup_subq_exit; + } } - if (select_lex->cond_value == Item::COND_FALSE) + + if (phase == OptimizePhase::Before_LOJ_Transform) { - zero_result_cause= "Impossible WHERE"; - best_rowcount= 0; - goto setup_subq_exit; + error= 0; + DBUG_RETURN(0); } - } - if (having_cond) - { - if (optimize_cond(thd, &having_cond, &cond_equal, NULL, - &select_lex->having_value, part))//TIANMU UPGRADE + + if (select_lex->partitioned_table_count && prune_table_partitions()) { error= 1; - DBUG_PRINT("error",("Error from optimize_cond")); + DBUG_PRINT("error", ("Error from prune_partitions")); DBUG_RETURN(1); } - if (select_lex->having_value == Item::COND_FALSE) - { - zero_result_cause= "Impossible HAVING"; - best_rowcount= 0; - goto setup_subq_exit; - } - } - //TIANMU UPGRADE - if (part == 1) - { - error= 0; - DBUG_RETURN(0); - } - //END - if (select_lex->partitioned_table_count && prune_table_partitions()) - { - error= 1; - DBUG_PRINT("error", ("Error from prune_partitions")); - DBUG_RETURN(1); - } - - /* - Try to optimize count(*), min() and max() to const fields if - there is implicit grouping (aggregate functions but no - group_list). In this case, the result set shall only contain one - row. - */ - if (tables_list && implicit_grouping) - { - int res; - /* - opt_sum_query() returns HA_ERR_KEY_NOT_FOUND if no rows match - the WHERE condition, - or 1 if all items were resolved (optimized away), - or 0, or an error number HA_ERR_... - If all items were resolved by opt_sum_query, there is no need to - open any tables. + /* + Try to optimize count(*), min() and max() to const fields if + there is implicit grouping (aggregate functions but no + group_list). In this case, the result set shall only contain one + row. */ - if ((res= opt_sum_query(thd, select_lex->leaf_tables, all_fields, - where_cond))) + if (tables_list && implicit_grouping) { - best_rowcount= 0; - if (res == HA_ERR_KEY_NOT_FOUND) - { - DBUG_PRINT("info",("No matching min/max row")); - zero_result_cause= "No matching min/max row"; - goto setup_subq_exit; - } - if (res > 1) - { - error= res; - DBUG_PRINT("error",("Error from opt_sum_query")); - DBUG_RETURN(1); - } - if (res < 0) + int res; + /* + opt_sum_query() returns HA_ERR_KEY_NOT_FOUND if no rows match + the WHERE condition, + or 1 if all items were resolved (optimized away), + or 0, or an error number HA_ERR_... + + If all items were resolved by opt_sum_query, there is no need to + open any tables. + */ + if ((res= opt_sum_query(thd, select_lex->leaf_tables, all_fields, + where_cond))) { - DBUG_PRINT("info",("No matching min/max row")); + best_rowcount= 0; + if (res == HA_ERR_KEY_NOT_FOUND) + { + DBUG_PRINT("info",("No matching min/max row")); zero_result_cause= "No matching min/max row"; + goto setup_subq_exit; + } + if (res > 1) + { + error= res; + DBUG_PRINT("error",("Error from opt_sum_query")); + DBUG_RETURN(1); + } + if (res < 0) + { + DBUG_PRINT("info",("No matching min/max row")); + zero_result_cause= "No matching min/max row"; + goto setup_subq_exit; + } + DBUG_PRINT("info",("Select tables optimized away")); + zero_result_cause= "Select tables optimized away"; + tables_list= 0; // All tables resolved + best_rowcount= 1; + const_tables= tables= primary_tables= select_lex->leaf_table_count; + /* + Extract all table-independent conditions and replace the WHERE + clause with them. All other conditions were computed by opt_sum_query + and the MIN/MAX/COUNT function(s) have been replaced by constants, + so there is no need to compute the whole WHERE clause again. + Notice that make_cond_for_table() will always succeed to remove all + computed conditions, because opt_sum_query() is applicable only to + conjunctions. + Preserve conditions for EXPLAIN. + */ + if (where_cond && !thd->lex->describe) + { + Item *table_independent_conds= + make_cond_for_table(where_cond, PSEUDO_TABLE_BITS, 0, 0); + DBUG_EXECUTE("where", + print_where(table_independent_conds, + "where after opt_sum_query()", + QT_ORDINARY);); + where_cond= table_independent_conds; + } goto setup_subq_exit; } - DBUG_PRINT("info",("Select tables optimized away")); - zero_result_cause= "Select tables optimized away"; - tables_list= 0; // All tables resolved + } + if (!tables_list) + { + DBUG_PRINT("info",("No tables")); best_rowcount= 1; - const_tables= tables= primary_tables= select_lex->leaf_table_count; - /* - Extract all table-independent conditions and replace the WHERE - clause with them. All other conditions were computed by opt_sum_query - and the MIN/MAX/COUNT function(s) have been replaced by constants, - so there is no need to compute the whole WHERE clause again. - Notice that make_cond_for_table() will always succeed to remove all - computed conditions, because opt_sum_query() is applicable only to - conjunctions. - Preserve conditions for EXPLAIN. - */ - if (where_cond && !thd->lex->describe) - { - Item *table_independent_conds= - make_cond_for_table(where_cond, PSEUDO_TABLE_BITS, 0, 0); - DBUG_EXECUTE("where", - print_where(table_independent_conds, - "where after opt_sum_query()", - QT_ORDINARY);); - where_cond= table_independent_conds; - } - goto setup_subq_exit; + error= 0; + if (make_tmp_tables_info()) + DBUG_RETURN(1); + count_field_types(select_lex, &tmp_table_param, all_fields, false, false); + // Make plan visible for EXPLAIN + set_plan_state(NO_TABLES); + DBUG_RETURN(0); } - } - if (!tables_list) - { - DBUG_PRINT("info",("No tables")); - best_rowcount= 1; - error= 0; - if (make_tmp_tables_info()) - DBUG_RETURN(1); - count_field_types(select_lex, &tmp_table_param, all_fields, false, false); - // Make plan visible for EXPLAIN - set_plan_state(NO_TABLES); - DBUG_RETURN(0); - } - error= -1; // Error is sent to client - sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables); - - if ((where_cond || group_list || order) && - substitute_gc(thd, select_lex, where_cond, group_list, order)) - { - // We added hidden fields to the all_fields list, count them. - count_field_types(select_lex, &tmp_table_param, select_lex->all_fields, - false, false); - } + error= -1; // Error is sent to client + sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables); - // Set up join order and initial access paths - THD_STAGE_INFO(thd, stage_statistics); - if (make_join_plan()) - { - if (thd->killed) - thd->send_kill_message(); - DBUG_PRINT("error",("Error: JOIN::make_join_plan() failed")); - DBUG_RETURN(1); - } - - // At this stage, join_tab==NULL, JOIN_TABs are listed in order by best_ref. - ASSERT_BEST_REF_IN_JOIN_ORDER(this); - - if (zero_result_cause) - goto setup_subq_exit; + if ((where_cond || group_list || order) && + substitute_gc(thd, select_lex, where_cond, group_list, order)) + { + // We added hidden fields to the all_fields list, count them. + count_field_types(select_lex, &tmp_table_param, select_lex->all_fields, + false, false); + } - if (rollup.state != ROLLUP::STATE_NONE) - { - if (rollup_process_const_fields()) + // Set up join order and initial access paths + THD_STAGE_INFO(thd, stage_statistics); + if (make_join_plan()) { - DBUG_PRINT("error", ("Error: rollup_process_fields() failed")); + if (thd->killed) + thd->send_kill_message(); + DBUG_PRINT("error",("Error: JOIN::make_join_plan() failed")); DBUG_RETURN(1); } - /* - Fields may have been replaced by Item_func_rollup_const, so - recalculate the number of fields and functions for this query block. - */ - // JOIN::optimize_rollup() may set quick_group=0, and we must not undo that. - const uint save_quick_group= tmp_table_param.quick_group; + // At this stage, join_tab==NULL, JOIN_TABs are listed in order by best_ref. + ASSERT_BEST_REF_IN_JOIN_ORDER(this); - count_field_types(select_lex, &tmp_table_param, all_fields, false, false); - tmp_table_param.quick_group= save_quick_group; - } - else - { - /* Remove distinct if only const tables */ - select_distinct&= !plan_is_const(); - } + if (zero_result_cause) + goto setup_subq_exit; - if (const_tables && !thd->locked_tables_mode && - !(select_lex->active_options() & SELECT_NO_UNLOCK )&& part!=3) //TIANMU UPGRADE - { - TABLE *ct[MAX_TABLES]; - for (uint i= 0; i < const_tables; i++) - ct[i]= best_ref[i]->table(); - mysql_unlock_some_tables(thd, ct, const_tables); - } - if (!where_cond && select_lex->outer_join) - { - /* Handle the case where we have an OUTER JOIN without a WHERE */ - where_cond=new Item_int((longlong) 1,1); // Always true - } + if (rollup.state != ROLLUP::STATE_NONE) + { + if (rollup_process_const_fields()) + { + DBUG_PRINT("error", ("Error: rollup_process_fields() failed")); + DBUG_RETURN(1); + } + /* + Fields may have been replaced by Item_func_rollup_const, so + recalculate the number of fields and functions for this query block. + */ - error= 0; - /* - Among the equal fields belonging to the same multiple equality - choose the one that is to be retrieved first and substitute - all references to these in where condition for a reference for - the selected field. - */ - if (where_cond) - { - where_cond= substitute_for_best_equal_field(where_cond, cond_equal, - map2table); - if (thd->is_error()) + // JOIN::optimize_rollup() may set quick_group=0, and we must not undo that. + const uint save_quick_group= tmp_table_param.quick_group; + + count_field_types(select_lex, &tmp_table_param, all_fields, false, false); + tmp_table_param.quick_group= save_quick_group; + } + else { - error= 1; - DBUG_PRINT("error",("Error from substitute_for_best_equal")); - DBUG_RETURN(1); + /* Remove distinct if only const tables */ + select_distinct&= !plan_is_const(); } - where_cond->update_used_tables(); - DBUG_EXECUTE("where", - print_where(where_cond, - "after substitute_best_equal", - QT_ORDINARY);); - } - /* - Perform the same optimization on field evaluation for all join conditions. - */ - for (uint i= const_tables; i < tables ; ++i) - { - JOIN_TAB *const tab= best_ref[i]; - if (tab->position() && tab->join_cond()) + if (const_tables && !thd->locked_tables_mode && + !(select_lex->active_options() & SELECT_NO_UNLOCK ) && phase != OptimizePhase::Finish_LOJ_Transform) { - tab->set_join_cond(substitute_for_best_equal_field(tab->join_cond(), - tab->cond_equal, - map2table)); + TABLE *ct[MAX_TABLES]; + for (uint i= 0; i < const_tables; i++) + ct[i]= best_ref[i]->table(); + mysql_unlock_some_tables(thd, ct, const_tables); + } + if (!where_cond && select_lex->outer_join) + { + /* Handle the case where we have an OUTER JOIN without a WHERE */ + where_cond=new Item_int((longlong) 1,1); // Always true + } + + error= 0; + /* + Among the equal fields belonging to the same multiple equality + choose the one that is to be retrieved first and substitute + all references to these in where condition for a reference for + the selected field. + */ + if (where_cond) + { + where_cond= substitute_for_best_equal_field(where_cond, cond_equal, + map2table); if (thd->is_error()) { error= 1; DBUG_PRINT("error",("Error from substitute_for_best_equal")); DBUG_RETURN(1); } - tab->join_cond()->update_used_tables(); + where_cond->update_used_tables(); + DBUG_EXECUTE("where", + print_where(where_cond, + "after substitute_best_equal", + QT_ORDINARY);); } - } - //TIANMU UPGRADE BEGIN - // this is end of part=3 and beginning of part=4 - if(part == 3) { - DBUG_RETURN(0); // error == 0 - } - //END -}// end of if(part!=4) + + /* + Perform the same optimization on field evaluation for all join conditions. + */ + for (uint i= const_tables; i < tables ; ++i) + { + JOIN_TAB *const tab= best_ref[i]; + if (tab->position() && tab->join_cond()) + { + tab->set_join_cond(substitute_for_best_equal_field(tab->join_cond(), + tab->cond_equal, + map2table)); + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from substitute_for_best_equal")); + DBUG_RETURN(1); + } + tab->join_cond()->update_used_tables(); + } + } + + // this is end of phase=Finish_LOJ_Transform and beginning of phase=Done_Optimization + if(phase == OptimizePhase::Finish_LOJ_Transform) { + DBUG_RETURN(0); // error == 0 + } + + }// end of if(phase!=Done_Optimization) if (init_ref_access()) { error= 1; @@ -567,7 +567,7 @@ JOIN::optimize(unsigned char part) //TIANMU UPGRADE { having_cond->update_used_tables(); if (remove_eq_conds(thd, having_cond, &having_cond, - &select_lex->having_value, part)) + &select_lex->having_value, phase)) { error= 1; DBUG_PRINT("error",("Error from remove_eq_conds")); @@ -5285,8 +5285,6 @@ bool JOIN::init_planner_arrays() assert(primary_tables == 0 && tables == 0); - if (!(primary_tables == 0 && tables == 0)) return true; //tmp fix crash - if (!(join_tab= alloc_jtab_array(thd, table_count))) return true; @@ -10064,7 +10062,7 @@ ORDER *JOIN::remove_const(ORDER *first_order, Item *cond, bool change_list, bool optimize_cond(THD *thd, Item **cond, COND_EQUAL **cond_equal, List *join_list, Item::cond_result *cond_value, - unsigned char part)//TIANMU UPGRADE + OptimizePhase phase) { Opt_trace_context * const trace= &thd->opt_trace; DBUG_ENTER("optimize_cond"); @@ -10092,10 +10090,10 @@ bool optimize_cond(THD *thd, Item **cond, COND_EQUAL **cond_equal, This is performed for the WHERE condition and any join conditions, but not for the HAVING condition. */ - if (part == 0 || part == 1) + if (phase == OptimizePhase::Beginning || phase == OptimizePhase::Before_LOJ_Transform) { - if (join_list) - { + if(join_list) + { Opt_trace_object step_wrapper(trace); step_wrapper.add_alnum("transformation", "equality_propagation"); { @@ -10108,32 +10106,25 @@ bool optimize_cond(THD *thd, Item **cond, COND_EQUAL **cond_equal, } step_wrapper.add("resulting_condition", *cond); } - - } - /* change field = field to field = const for each found field = const */ - if (*cond) - { - // - if (part==0 || part==1) - { - Opt_trace_object step_wrapper(trace); - step_wrapper.add_alnum("transformation", "constant_propagation"); + /* change field = field to field = const for each found field = const */ + if (*cond) { - Opt_trace_disable_I_S - disable_trace_wrapper(trace, !(*cond)->has_subquery()); - Opt_trace_array trace_subselect(trace, "subselect_evaluation"); - if (propagate_cond_constants(thd, NULL, *cond, *cond)) - DBUG_RETURN(true); + Opt_trace_object step_wrapper(trace); + step_wrapper.add_alnum("transformation", "constant_propagation"); + { + Opt_trace_disable_I_S + disable_trace_wrapper(trace, !(*cond)->has_subquery()); + Opt_trace_array trace_subselect(trace, "subselect_evaluation"); + if (propagate_cond_constants(thd, NULL, *cond, *cond)) + DBUG_RETURN(true); + } + step_wrapper.add("resulting_condition", *cond); + /* + Remove all instances of item == item + Remove all and-levels where CONST item != CONST item + */ + DBUG_EXECUTE("where",print_where(*cond,"after const change", QT_ORDINARY);); } - step_wrapper.add("resulting_condition", *cond); - - - /* - Remove all instances of item == item - Remove all and-levels where CONST item != CONST item - */ - DBUG_EXECUTE("where",print_where(*cond,"after const change", QT_ORDINARY);); - } } if (*cond) { @@ -10143,7 +10134,7 @@ bool optimize_cond(THD *thd, Item **cond, COND_EQUAL **cond_equal, Opt_trace_disable_I_S disable_trace_wrapper(trace, !(*cond)->has_subquery()); Opt_trace_array trace_subselect(trace, "subselect_evaluation"); - if (remove_eq_conds(thd, *cond, cond, cond_value, part)) + if (remove_eq_conds(thd, *cond, cond, cond_value, phase)) DBUG_RETURN(true); } step_wrapper.add("resulting_condition", *cond); @@ -10171,7 +10162,7 @@ bool optimize_cond(THD *thd, Item **cond, COND_EQUAL **cond_equal, static bool internal_remove_eq_conds(THD *thd, Item *cond, Item **retcond, Item::cond_result *cond_value, - unsigned char part) + OptimizePhase phase) { if (cond->type() == Item::COND_ITEM) { @@ -10189,7 +10180,7 @@ static bool internal_remove_eq_conds(THD *thd, Item *cond, { Item *new_item; Item::cond_result tmp_cond_value; - if (internal_remove_eq_conds(thd, item, &new_item, &tmp_cond_value, part)) + if (internal_remove_eq_conds(thd, item, &new_item, &tmp_cond_value, phase)) return true; if (new_item == NULL) @@ -10444,14 +10435,14 @@ static bool internal_remove_eq_conds(THD *thd, Item *cond, } if (cond->const_item()) { - if (part!=1 || cond->type()!=Item::SUBSELECT_ITEM) - {//TIANMU UPGRADE + if (phase != OptimizePhase::Before_LOJ_Transform || cond->type()!=Item::SUBSELECT_ITEM) + { bool value; if (eval_const_cond(thd, cond, &value)) return true; *cond_value= value ? Item::COND_TRUE : Item::COND_FALSE; *retcond= NULL; - }//END + } return false; } } @@ -10507,7 +10498,7 @@ static bool internal_remove_eq_conds(THD *thd, Item *cond, */ bool remove_eq_conds(THD *thd, Item *cond, Item **retcond, - Item::cond_result *cond_value, unsigned char part)//TIANMU UPGRADE + Item::cond_result *cond_value, OptimizePhase phase) { if (cond->type() == Item::FUNC_ITEM && down_cast(cond)->functype() == Item_func::ISNULL_FUNC) @@ -10558,7 +10549,7 @@ bool remove_eq_conds(THD *thd, Item *cond, Item **retcond, } } } - return internal_remove_eq_conds(thd, cond, retcond, cond_value, part);//TIANMU UPGRADE + return internal_remove_eq_conds(thd, cond, retcond, cond_value, phase); } diff --git a/sql/sql_optimizer.h b/sql/sql_optimizer.h index 47ef82ec6..33c48651a 100644 --- a/sql/sql_optimizer.h +++ b/sql/sql_optimizer.h @@ -55,6 +55,15 @@ typedef struct st_rollup List *fields; } ROLLUP; +enum class OptimizePhase // for Tianmu to indicate which optimization phase. +{ + Beginning = 0, + Before_LOJ_Transform = 1, + After_LOJ_Transform = 2, + Finish_LOJ_Transform = 3, + Done_Optimization = 4 +}; + class JOIN :public Sql_alloc { JOIN(const JOIN &rhs); /**< not implemented */ @@ -555,8 +564,9 @@ class JOIN :public Sql_alloc tables taking part in semi-join materialization). */ bool plan_is_single_table() { return primary_tables - const_tables == 1; } + + int optimize(OptimizePhase phase = OptimizePhase::Beginning); - int optimize(unsigned char part = 0);//TIANMU UPGRADE void reset(); void exec(); bool prepare_result(); @@ -874,10 +884,10 @@ class Prepare_error_tracker bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno, bool other_tbls_ok); bool remove_eq_conds(THD *thd, Item *cond, Item **retcond, - Item::cond_result *cond_value, unsigned char part = 0);//TIANMU UPGRADE + Item::cond_result *cond_value, OptimizePhase phase = OptimizePhase::Beginning); bool optimize_cond(THD *thd, Item **conds, COND_EQUAL **cond_equal, List *join_list, - Item::cond_result *cond_value, unsigned char part = 0);//TIANMU UPGRADE + Item::cond_result *cond_value, OptimizePhase phase = OptimizePhase::Beginning); Item* substitute_for_best_equal_field(Item *cond, COND_EQUAL *cond_equal, void *table_join_idx); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0dfe73f15..3c7ab4822 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3281,10 +3281,10 @@ case SQLCOM_PREPARE: //res= handle_query(thd, lex, result, SELECT_NO_UNLOCK, 0); // Tianmu hook added - int tianmu_res, free_join_from_tianmu, optimize_after_tianmu; + int tianmu_res, free_join_from_tianmu, is_optimize_after_tianmu; if (Tianmu::handler::QueryRouteTo::kToMySQL == - Tianmu::handler::ha_my_tianmu_query(thd, lex, result, 0, tianmu_res, optimize_after_tianmu, free_join_from_tianmu, (int)true)) - res = handle_query(thd, lex, result, SELECT_NO_UNLOCK, (ulong)0, optimize_after_tianmu, free_join_from_tianmu); + Tianmu::handler::ha_my_tianmu_query(thd, lex, result, 0, tianmu_res, is_optimize_after_tianmu, free_join_from_tianmu, (int)true)) + res = handle_query(thd, lex, result, SELECT_NO_UNLOCK, (ulong)0, is_optimize_after_tianmu, free_join_from_tianmu); else res = tianmu_res; @@ -5203,10 +5203,10 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) } //res= handle_query(thd, lex, result, 0, 0, 0, 0); - int tianmu_res, free_join_from_tianmu, optimize_after_tianmu; + int tianmu_res, free_join_from_tianmu, is_optimize_after_tianmu; if (Tianmu::handler::QueryRouteTo::kToMySQL == - Tianmu::handler::ha_my_tianmu_query(thd, lex, result, (ulong)0, tianmu_res, optimize_after_tianmu, free_join_from_tianmu)) { - res = handle_query(thd, lex, result, (ulonglong)0, (ulonglong)0, optimize_after_tianmu, free_join_from_tianmu); + Tianmu::handler::ha_my_tianmu_query(thd, lex, result, (ulong)0, tianmu_res, is_optimize_after_tianmu, free_join_from_tianmu)) { + res = handle_query(thd, lex, result, (ulonglong)0, (ulonglong)0, is_optimize_after_tianmu, free_join_from_tianmu); } else res = tianmu_res; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 1b19995c8..79bbba8b2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -102,7 +102,7 @@ uint find_shortest_key(TABLE *table, const key_map *usable_keys); Such queries are executed with a more direct code path. */ bool handle_query(THD *thd, LEX *lex, Query_result *result, ulonglong added_options, - ulonglong removed_options, int optimize_after_bh, int free_join_from_bh) + ulonglong removed_options, int is_optimize_after_tianmu, int free_join_from_tianmu) { DBUG_ENTER("handle_query"); @@ -144,7 +144,7 @@ bool handle_query(THD *thd, LEX *lex, Query_result *result, ulonglong added_opti else { res = FALSE; - if (optimize_after_bh) + if (is_optimize_after_tianmu) res = lex->unit->optimize_after_tianmu(); // optimization after Tianmu if (!res) if (unit->prepare(thd, result, SELECT_NO_UNLOCK | added_options, diff --git a/sql/sql_select.h b/sql/sql_select.h index edf4e7976..a502e2743 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1106,7 +1106,7 @@ class store_key_const_item :public store_key_item bool error_if_full_join(JOIN *join); bool handle_query(THD *thd, LEX *lex, Query_result *result, ulonglong added_options, ulonglong removed_options, - int optimize_after_bh=0, int free_join_from_bh=0); + int is_optimize_after_tianmu=0, int free_join_from_tianmu=0); void free_underlaid_joins(THD *thd, SELECT_LEX *select); diff --git a/sql/table.h b/sql/table.h index 15331160d..e1d177d95 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2099,6 +2099,9 @@ struct TABLE_LIST /// Optimize the query expression representing a derived table/view bool optimize_derived(THD *thd); + /// Optimize the query expression representing a derived table/view for tianmu table + bool optimize_derived_for_tianmu(THD *thd); + /// Create result table for a materialized derived table/view bool create_derived(THD *thd); diff --git a/storage/tianmu/core/engine.h b/storage/tianmu/core/engine.h index aacda2a16..c8d770951 100644 --- a/storage/tianmu/core/engine.h +++ b/storage/tianmu/core/engine.h @@ -110,7 +110,7 @@ class Engine final { Transaction *GetTx(THD *thd); void ClearTx(THD *thd); QueryRouteTo HandleSelect(THD *thd, LEX *lex, Query_result *&result_output, ulong setup_tables_done_option, int &res, - int &optimize_after_tianmu, int &tianmu_free_join, int with_insert = false); + int &is_optimize_after_tianmu, int &tianmu_free_join, int with_insert = false); system::ResourceManager *getResourceManager() const { return m_resourceManager; } std::shared_ptr GetTableRD(const std::string &table_path); int InsertRow(const std::string &tablename, Transaction *trans_, TABLE *table, std::shared_ptr &share); diff --git a/storage/tianmu/core/engine_execute.cpp b/storage/tianmu/core/engine_execute.cpp index 008a52b50..3e158bab2 100644 --- a/storage/tianmu/core/engine_execute.cpp +++ b/storage/tianmu/core/engine_execute.cpp @@ -32,7 +32,7 @@ namespace Tianmu { namespace core { int optimize_select(THD *thd, ulong select_options, Query_result *result, SELECT_LEX *select_lex, - int &optimize_after_tianmu, int &free_join); + int &is_optimize_after_tianmu, int &tianmu_free_join); class KillTimer { public: @@ -78,12 +78,12 @@ QueryRouteTo::kToMySQL is returned and MySQL engine continues query execution. */ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulong setup_tables_done_option, int &res, - int &optimize_after_tianmu, int &tianmu_free_join, int with_insert) { + int &is_optimize_after_tianmu, int &tianmu_free_join, int with_insert) { KillTimer timer(thd, tianmu_sysvar_max_execution_time); int in_case_of_failure_can_go_to_mysql; - optimize_after_tianmu = FALSE; + is_optimize_after_tianmu = FALSE; tianmu_free_join = 0; SELECT_LEX_UNIT *unit = nullptr; @@ -122,7 +122,7 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo // optimizing and executing derived tables is passed over, then optimization // of derived tables must go here. res = FALSE; - int free_join = FALSE; + int tianmu_free_join = FALSE; lex->thd->derived_tables_processing = TRUE; for (SELECT_LEX *sl = lex->all_selects_list; sl; sl = sl->next_select_in_list()) // for all selects for (TABLE_LIST *cursor = sl->get_table_list(); cursor; cursor = cursor->next_local) // for all tables @@ -151,12 +151,12 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo int optimize_derived_after_tianmu = FALSE; res = optimize_select( thd, ulong(first_select->active_options() | thd->variables.option_bits | SELECT_NO_UNLOCK), - (Query_result *)cursor->derived_result, first_select, optimize_derived_after_tianmu, free_join); + (Query_result *)cursor->derived_result, first_select, optimize_derived_after_tianmu, tianmu_free_join); if (optimize_derived_after_tianmu) derived_optimized.push_back(cursor->derived_unit()); } lex->set_current_select(save_current_select); - if (!res && free_join) // no error & + if (!res && tianmu_free_join) // no error & route = QueryRouteTo::kToMySQL; if (res || route == QueryRouteTo::kToMySQL) goto ret_derived; @@ -185,7 +185,7 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo else { int old_executed = unit->is_executed(); res = unit->optimize_for_tianmu(); //====exec() - optimize_after_tianmu = TRUE; + is_optimize_after_tianmu = TRUE; if (!res) { try { route = ha_tianmu_engine_->Execute(unit->thd, unit->thd->lex, result, unit); @@ -215,7 +215,7 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo } if (res || route == QueryRouteTo::kToTianmu) { res |= (int)unit->cleanup(0); - optimize_after_tianmu = FALSE; + is_optimize_after_tianmu = FALSE; } } else { unit->set_limit(unit->global_parameters()); // the fragment of original @@ -230,7 +230,7 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo int err; err = optimize_select(thd, ulong(select_lex->active_options() | thd->variables.option_bits | setup_tables_done_option), - result, select_lex, optimize_after_tianmu, tianmu_free_join); + result, select_lex, is_optimize_after_tianmu, tianmu_free_join); // RCBase query engine entry point if (!err) { @@ -257,7 +257,7 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo if (err || route == QueryRouteTo::kToTianmu) { thd->proc_info = "end"; err |= (int)select_lex->cleanup(0); - optimize_after_tianmu = FALSE; + is_optimize_after_tianmu = FALSE; tianmu_free_join = 0; } res = (err || thd->is_error()); @@ -265,8 +265,8 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo res = select_lex->join->error; } if (select_lex->join && Query::IsLOJ(select_lex->join_list)) - optimize_after_tianmu = 2; // optimize partially (part=4), since part of LOJ - // optimization was already done + is_optimize_after_tianmu = TRUE; // optimize partially (phase=Doneoptimization), since part of LOJ + // optimization was already done res |= (int)thd->is_error(); // the ending of original handle_select(...) */ if (unlikely(res)) { // If we had a another error reported earlier then this will be ignored // @@ -308,10 +308,10 @@ QueryRouteTo Engine::HandleSelect(THD *thd, LEX *lex, Query_result *&result, ulo Prepares and optimizes a single select for Tianmu engine */ int optimize_select(THD *thd, ulong select_options, Query_result *result, SELECT_LEX *select_lex, - int &optimize_after_tianmu, int &free_join) { + int &is_optimize_after_tianmu, int &tianmu_free_join) { // copied from sql_select.cpp from the beginning of mysql_select(...) int err = 0; - free_join = 1; + tianmu_free_join = 1; select_lex->context.resolve_in_select_list = TRUE; JOIN *join; if (select_lex->join != 0) { @@ -328,7 +328,7 @@ int optimize_select(THD *thd, ulong select_options, Query_result *result, SELECT } } } - free_join = 0; + tianmu_free_join = 0; join->select_options = select_options; } else { thd_proc_info(thd, "init"); @@ -344,8 +344,8 @@ int optimize_select(THD *thd, ulong select_options, Query_result *result, SELECT select_lex->set_join(join); } join->best_rowcount = 2; - optimize_after_tianmu = TRUE; - if ((err = join->optimize(1))) + is_optimize_after_tianmu = TRUE; + if ((err = join->optimize(OptimizePhase::Before_LOJ_Transform))) return err; return FALSE; } @@ -572,7 +572,8 @@ int st_select_lex_unit::optimize_for_tianmu() { if (is_executed() && !uncacheable && !thd->lex->is_explain()) return FALSE; - executed = 1; + + set_executed(); if (uncacheable || !item || !item->assigned() || thd->lex->is_explain()) { if (item) @@ -623,7 +624,7 @@ int st_select_lex_unit::optimize_for_tianmu() { sl->join->select_options = (select_limit_cnt == HA_POS_ERROR || sl->braces) ? sl->active_options() & ~OPTION_FOUND_ROWS : sl->active_options() | found_rows_for_union; - saved_error = sl->join->optimize(1); + saved_error = sl->join->optimize(OptimizePhase::Before_LOJ_Transform); } // HERE ends the code from bool st_select_lex_unit::exec() @@ -671,7 +672,7 @@ int st_select_lex_unit::optimize_for_tianmu() { } } - optimized = 1; + set_optimized(); thd->lex->set_current_select(lex_select_save); return FALSE; } @@ -689,7 +690,7 @@ int st_select_lex_unit::optimize_after_tianmu() { } sl->set_join(join); } - int res = sl->join->optimize(2); + int res = sl->join->optimize(OptimizePhase::After_LOJ_Transform); if (res) { thd->lex->set_current_select(lex_select_save); return res; @@ -705,3 +706,116 @@ int st_select_lex_unit::optimize_after_tianmu() { thd->lex->set_current_select(lex_select_save); return FALSE; } + +/** + Optimize a query block and all inner query expressions for tianmu + + @param thd thread handler + @returns false if success, true if error +*/ + +bool SELECT_LEX::optimize_select_for_tianmu(THD *thd) { + DBUG_ENTER("SELECT_LEX::optimize_select_for_tianmu"); + + JOIN *const join = new JOIN(thd, this); + if (!join) + DBUG_RETURN(true); + + set_join(join); + + if (join->optimize(OptimizePhase::Before_LOJ_Transform)) + DBUG_RETURN(true); + + for (SELECT_LEX_UNIT *unit = first_inner_unit(); unit; unit = unit->next_unit()) { + if (!unit->is_optimized()) + DBUG_RETURN(true); + + SELECT_LEX *save_select = thd->lex->current_select(); + + for (SELECT_LEX *sl = unit->first_select(); sl; sl = sl->next_select()) { + thd->lex->set_current_select(sl); + unit->set_limit(sl); + + if (sl->optimize_select_for_tianmu(thd)) + DBUG_RETURN(true); + + if (unit->query_result()) + unit->query_result()->estimated_rowcount += + sl->is_implicitly_grouped() || sl->join->group_optimized_away ? 2 : sl->join->best_rowcount; + } + SELECT_LEX *unit_fake_select_lex = unit->fake_select_lex; + + if (unit_fake_select_lex) { + thd->lex->set_current_select(unit_fake_select_lex); + unit->set_limit(unit_fake_select_lex); + + if (unit_fake_select_lex->optimize_select_for_tianmu(thd)) + DBUG_RETURN(true); + } + + unit->set_optimized(); + + thd->lex->set_current_select(save_select); + } + + DBUG_RETURN(false); +} + +/** + Optimize the query expression representing a derived table/view for tianmu table. + + @note + If optimizer finds out that the derived table/view is of the type + "SELECT a_constant" this functions also materializes it. + + @param thd thread handle + + @returns false if success, true if error. +*/ + +bool TABLE_LIST::optimize_derived_for_tianmu(THD *thd) { + DBUG_ENTER("TABLE_LIST::optimize_derived_for_tianmu"); + + SELECT_LEX_UNIT *const unit = derived_unit(); + + assert(unit && unit->is_prepared() && !unit->is_optimized()); + + if (unit && unit->is_prepared() && !unit->is_optimized()) { + SELECT_LEX *save_select = thd->lex->current_select(); + + for (SELECT_LEX *sl = unit->first_select(); sl; sl = sl->next_select()) { + thd->lex->set_current_select(sl); + + // LIMIT is required for optimization + unit->set_limit(sl); + + if (sl->optimize_select_for_tianmu(thd)) + DBUG_RETURN(true); + + if (unit->query_result()) + unit->query_result()->estimated_rowcount += + sl->is_implicitly_grouped() || sl->join->group_optimized_away ? 2 : sl->join->best_rowcount; + } + st_select_lex *unit_fake_select_lex = unit->fake_select_lex; + if (unit_fake_select_lex) { + thd->lex->set_current_select(unit_fake_select_lex); + + unit->set_limit(unit_fake_select_lex); + + if (unit_fake_select_lex->optimize_select_for_tianmu(thd)) + DBUG_RETURN(true); + } + + unit->set_optimized(); + + thd->lex->set_current_select(save_select); + } + + if (thd->is_error()) + DBUG_RETURN(true); + + if (materializable_is_const() && (create_derived(thd) || materialize_derived(thd))) + DBUG_RETURN(true); + + DBUG_RETURN(false); +} diff --git a/storage/tianmu/core/query_compile.cpp b/storage/tianmu/core/query_compile.cpp index 95a44889d..06c29d51b 100644 --- a/storage/tianmu/core/query_compile.cpp +++ b/storage/tianmu/core/query_compile.cpp @@ -1072,7 +1072,7 @@ QueryRouteTo Query::Compile(CompiledQuery *compiled_query, SELECT_LEX *selects_l // necessary due to already done basic transformation of conditions // see comments in sql_select.cc:JOIN::optimize() if (IsLOJ(join_list)) - sl->join->optimize(3); + sl->join->optimize(OptimizePhase::Finish_LOJ_Transform); if (left_expr_for_subselect) if (!ClearSubselectTransformation(*oper_for_subselect, field_for_subselect, conds, having, cond_to_reinsert, diff --git a/storage/tianmu/handler/ha_my_tianmu.cpp b/storage/tianmu/handler/ha_my_tianmu.cpp index f117d8f75..6e38a492d 100644 --- a/storage/tianmu/handler/ha_my_tianmu.cpp +++ b/storage/tianmu/handler/ha_my_tianmu.cpp @@ -80,13 +80,14 @@ bool ha_my_tianmu_set_statement_allowed(THD *thd, LEX *lex) { } QueryRouteTo ha_my_tianmu_query(THD *thd, LEX *lex, Query_result *&result_output, ulong setup_tables_done_option, - int &res, int &optimize_after_tianmu, int &tianmu_free_join, int with_insert) { + int &res, int &is_optimize_after_tianmu, int &tianmu_free_join, int with_insert) { QueryRouteTo ret = QueryRouteTo::kToTianmu; try { // handle_select_ret is introduced here because in case of some exceptions // (e.g. thrown from ForbiddenMySQLQueryPath) we want to return - QueryRouteTo handle_select_ret = ha_tianmu_engine_->HandleSelect( - thd, lex, result_output, setup_tables_done_option, res, optimize_after_tianmu, tianmu_free_join, with_insert); + QueryRouteTo handle_select_ret = + ha_tianmu_engine_->HandleSelect(thd, lex, result_output, setup_tables_done_option, res, + is_optimize_after_tianmu, tianmu_free_join, with_insert); if (handle_select_ret == QueryRouteTo::kToMySQL && AtLeastOneTianmuTableInvolved(lex) && ForbiddenMySQLQueryPath(lex)) { my_message(static_cast(common::ErrorCode::UNKNOWN_ERROR), diff --git a/storage/tianmu/handler/ha_my_tianmu.h b/storage/tianmu/handler/ha_my_tianmu.h index 1ce8f2276..24c1b4612 100644 --- a/storage/tianmu/handler/ha_my_tianmu.h +++ b/storage/tianmu/handler/ha_my_tianmu.h @@ -29,7 +29,8 @@ enum class QueryRouteTo { // processing the queries which routed to Tianmu engine. QueryRouteTo ha_my_tianmu_query(THD *thd, LEX *lex, Query_result *&result_output, ulong setup_tables_done_option, - int &res, int &optimize_after_tianmu, int &tianmu_free_join, int with_insert = false); + int &res, int &is_optimize_after_tianmu, int &tianmu_free_join, + int with_insert = false); // update the comment for a column. void ha_my_tianmu_update_and_store_col_comment(TABLE *table, int field_id, Field *source_field, int source_field_id,