diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 4692a3f76cc..11d1fe9279f 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -1401,3 +1401,105 @@ pk c CNT 9 2 4 10 2 3 drop table t0,t1; +# +# EXPLAIN FORMAT=JSON support for window functions +# +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +explain format=json select rank() over (order by a) from t0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "window_functions_computation": { + "temporary_table": { + "table": { + "table_name": "t0", + "access_type": "ALL", + "rows": 10, + "filtered": 100 + } + } + } + } +} +create table t1 (a int, b int, c int); +insert into t1 select a,a,a from t0; +explain format=json +select +a, +rank() over (order by sum(b)) +from t1 +group by a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "filesort": { + "window_functions_computation": { + "temporary_table": { + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 10, + "filtered": 100 + } + } + } + } + } +} +explain format=json +select +a, +rank() over (order by sum(b)) +from t1 +group by a +order by null; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "window_functions_computation": { + "temporary_table": { + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 10, + "filtered": 100 + } + } + } + } +} +# +# Check how window function works together with GROUP BY and HAVING +# +select b,max(a) as MX, rank() over (order by b) from t1 group by b having MX in (3,5,7); +b MX rank() over (order by b) +3 3 1 +5 5 2 +7 7 3 +explain format=json +select b,max(a) as MX, rank() over (order by b) from t1 group by b having MX in (3,5,7); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "having_condition": "(MX in (3,5,7))", + "filesort": { + "window_functions_computation": { + "temporary_table": { + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 10, + "filtered": 100 + } + } + } + } + } +} +drop table t1; +drop table t0; diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index cc01d5e678e..35b6c1cbb72 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -924,8 +924,40 @@ execute stmt; drop table t0,t1; +--echo # +--echo # EXPLAIN FORMAT=JSON support for window functions +--echo # +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +explain format=json select rank() over (order by a) from t0; +create table t1 (a int, b int, c int); +insert into t1 select a,a,a from t0; +explain format=json +select + a, + rank() over (order by sum(b)) +from t1 +group by a; +explain format=json +select + a, + rank() over (order by sum(b)) +from t1 +group by a +order by null; + +--echo # +--echo # Check how window function works together with GROUP BY and HAVING +--echo # + +select b,max(a) as MX, rank() over (order by b) from t1 group by b having MX in (3,5,7); +explain format=json +select b,max(a) as MX, rank() over (order by b) from t1 group by b having MX in (3,5,7); + +drop table t1; +drop table t0; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index ae54f8bd455..e70ad7d5239 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -884,6 +884,9 @@ void Explain_select::print_explain_json(Explain_query *query, case AGGR_OP_REMOVE_DUPLICATES: writer->add_member("duplicate_removal").start_object(); break; + case AGGR_OP_WINDOW_FUNCS: + writer->add_member("window_functions_computation").start_object(); + break; default: DBUG_ASSERT(0); } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 9f8a0361cd5..8c632d7f36a 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -265,7 +265,8 @@ typedef enum AGGR_OP_TEMP_TABLE, AGGR_OP_FILESORT, //AGGR_OP_READ_SORTED_FILE, // need this? - AGGR_OP_REMOVE_DUPLICATES + AGGR_OP_REMOVE_DUPLICATES, + AGGR_OP_WINDOW_FUNCS //AGGR_OP_JOIN // Need this? } enum_explain_aggr_node_type; @@ -297,6 +298,11 @@ public: enum_explain_aggr_node_type get_type() { return AGGR_OP_REMOVE_DUPLICATES; } }; +class Explain_aggr_window_funcs : public Explain_aggr_node +{ +public: + enum_explain_aggr_node_type get_type() { return AGGR_OP_WINDOW_FUNCS; } +}; ///////////////////////////////////////////////////////////////////////////// diff --git a/sql/sql_select.cc b/sql/sql_select.cc index cc1310f1632..484e8619551 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2140,7 +2140,6 @@ bool JOIN::make_aggr_tables_info() All optimization is done. Check if we can use the storage engines group by handler to evaluate the group by */ - group_by_handler *gbh= NULL; if (tables_list && (tmp_table_param.sum_func_count || group_list) && !procedure) { @@ -2339,14 +2338,6 @@ bool JOIN::make_aggr_tables_info() curr_tab->fields= &tmp_fields_list1; set_postjoin_aggr_write_func(curr_tab); - // psergey-todo: this is probably an incorrect place: - if (select_lex->window_funcs.elements) - { - curr_tab->window_funcs= new Window_funcs_computation; - if (curr_tab->window_funcs->setup(thd, &select_lex->window_funcs)) - DBUG_RETURN(true); - } - tmp_table_param.func_count= 0; tmp_table_param.field_count+= tmp_table_param.func_count; if (sort_and_group || curr_tab->table->group) @@ -2658,6 +2649,24 @@ bool JOIN::make_aggr_tables_info() skip_sort_order= true; } } + + /* + Window functions computation step should be attached to the last join_tab + that's doing aggregation. + The last join_tab reads the data from the temp. table. It also may do + - sorting + - duplicate value removal + Both of these operations are done after window function computation step. + */ + curr_tab= join_tab + top_join_tab_count + aggr_tables - 1; + if (select_lex->window_funcs.elements) + { + curr_tab->window_funcs_step= new Window_funcs_computation; + if (curr_tab->window_funcs_step->setup(thd, &select_lex->window_funcs, + curr_tab)) + DBUG_RETURN(true); + } + fields= curr_fields_list; // Reset before execution set_items_ref_array(items0); @@ -19137,7 +19146,10 @@ bool test_if_use_dynamic_range_scan(JOIN_TAB *join_tab) int join_init_read_record(JOIN_TAB *tab) { int error; - + /* + Note: the query plan tree for the below operations is constructed in + save_agg_explain_data. + */ if (tab->distinct && tab->remove_duplicates()) // Remove duplicates. return 1; if (tab->filesort && tab->sort_table()) // Sort table. @@ -24174,6 +24186,14 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) node= new Explain_aggr_tmp_table; node->child= prev_node; + if (join_tab->window_funcs_step) + { + prev_node=node; + node= new Explain_aggr_window_funcs; + node->child= prev_node; + } + + /* The below matches execution in join_init_read_record() */ if (join_tab->distinct) { prev_node= node; @@ -25946,9 +25966,10 @@ AGGR_OP::end_send() // Update ref array join_tab->join->set_items_ref_array(*join_tab->ref_array); - if (join_tab->window_funcs) + if (join_tab->window_funcs_step) { - join_tab->window_funcs->exec(join); + if (join_tab->window_funcs_step->exec(join)) + return NESTED_LOOP_ERROR; } table->reginfo.lock_type= TL_UNLOCK; diff --git a/sql/sql_select.h b/sql/sql_select.h index f5b9cb4684d..1718a4676f1 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -428,7 +428,7 @@ typedef struct st_join_table { Non-NULL value means this join_tab must do window function computation before reading. */ - Window_funcs_computation* window_funcs; + Window_funcs_computation* window_funcs_step; /** List of topmost expressions in the select list. The *next* JOIN TAB diff --git a/sql/sql_window.cc b/sql/sql_window.cc index ef7f512f7fc..875930f13e0 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1565,11 +1565,20 @@ bool Window_func_runner::exec(JOIN *join) bool Window_funcs_computation::setup(THD *thd, - List *window_funcs) + List *window_funcs, + JOIN_TAB *tab) { List_iterator_fast it(*window_funcs); Item_window_func *item_win; Window_func_runner *runner; + + SQL_SELECT *sel= NULL; + if (tab->filesort && tab->filesort->select) + { + sel= tab->filesort->select; + DBUG_ASSERT(!sel->quick); + } + // for each window function while ((item_win= it++)) { @@ -1579,6 +1588,8 @@ bool Window_funcs_computation::setup(THD *thd, { return true; } + /* Apply the same condition that the subsequent sort will */ + runner->filesort->select= sel; win_func_runners.push_back(runner, thd->mem_root); } return false; diff --git a/sql/sql_window.h b/sql/sql_window.h index 4c88a49b6b9..042d978431f 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -171,9 +171,11 @@ public: bool exec(JOIN *join); void cleanup() { delete filesort; } + + friend class Window_funcs_computation; }; - +struct st_join_table; /* This is a "window function computation phase": a single object of this class takes care of computing all window functions in a SELECT. @@ -187,7 +189,7 @@ class Window_funcs_computation : public Sql_alloc { List win_func_runners; public: - bool setup(THD *thd, List *window_funcs); + bool setup(THD *thd, List *window_funcs, st_join_table *tab); bool exec(JOIN *join); void cleanup(); };