diff options
-rw-r--r-- | sql/sql_select.cc | 28 | ||||
-rw-r--r-- | sql/sql_select.h | 10 | ||||
-rw-r--r-- | sql/sql_window.cc | 300 | ||||
-rw-r--r-- | sql/sql_window.h | 60 |
4 files changed, 279 insertions, 119 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 42a9858d549..af27664a7be 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -233,7 +233,7 @@ static bool list_contains_unique_index(TABLE *table, bool (*find_func) (Field *, void *), void *data); static bool find_field_in_item_list (Field *field, void *data); static bool find_field_in_order_list (Field *field, void *data); -int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab); +int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort); static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field, Item *having); static int remove_dup_with_hash_index(THD *thd,TABLE *table, @@ -2706,6 +2706,14 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List<Item> *table_fields, tab->table= table; table->reginfo.join_tab= tab; + // psergey-todo: this is probably an incorrect place: + if (select_lex->window_funcs.elements) + { + tab->window_funcs= new Window_funcs_computation; + if (tab->window_funcs->setup(thd, &select_lex->window_funcs)) + goto err; + } + /* if group or order on first table, sort first */ if (group_list && simple_group) { @@ -19189,7 +19197,7 @@ JOIN_TAB::sort_table() DBUG_ASSERT(join->ordered_index_usage != (filesort->order == join->order ? JOIN::ordered_index_order_by : JOIN::ordered_index_group_by)); - rc= create_sort_index(join->thd, join, this); + rc= create_sort_index(join->thd, join, this, NULL); return (rc != 0); } @@ -21160,8 +21168,8 @@ use_filesort: thd Thread handler join Join with table to sort join_tab What table to sort - - + fsort Filesort object. NULL means "use tab->filesort". + IMPLEMENTATION - If there is an index that can be used, the first non-const join_tab in 'join' is modified to use this index. @@ -21177,17 +21185,19 @@ use_filesort: */ int -create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab) +create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort) { ha_rows examined_rows; ha_rows found_rows; ha_rows filesort_retval= HA_POS_ERROR; TABLE *table; SQL_SELECT *select; - Filesort *fsort= tab->filesort; bool quick_created= FALSE; DBUG_ENTER("create_sort_index"); + if (fsort == NULL) + fsort= tab->filesort; + // One row, no need to sort. make_tmp_tables_info should already handle this. DBUG_ASSERT(!join->only_const_tables() && fsort); table= tab->table; @@ -25898,7 +25908,11 @@ AGGR_OP::end_send() // Update ref array join_tab->join->set_items_ref_array(*join_tab->ref_array); - join->process_window_functions(&join->select_lex->window_funcs); + if (join_tab->window_funcs) + { + join_tab->window_funcs->exec(join); + } + table->reginfo.lock_type= TL_UNLOCK; bool in_first_read= true; diff --git a/sql/sql_select.h b/sql/sql_select.h index 502e28d2dbd..d2780a795d0 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -423,6 +423,12 @@ typedef struct st_join_table { /* Sorting related info */ Filesort *filesort; + + /* + Non-NULL value means this join_tab must do window function computation + before reading. + */ + Window_funcs_computation* window_funcs; bool used_for_window_func; @@ -1494,8 +1500,6 @@ public: int init_execution(); void exec(); - bool process_window_functions(List<Item_window_func> *window_funcs); - void exec_inner(); bool prepare_result(List<Item> **columns_list); int destroy(); @@ -2270,5 +2274,5 @@ public: bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b); int test_if_group_changed(List<Cached_item> &list); -int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab); +int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort); #endif /* SQL_SELECT_INCLUDED */ diff --git a/sql/sql_window.cc b/sql/sql_window.cc index aa2c87de894..7b5769a8bf5 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1439,6 +1439,197 @@ bool compute_two_pass_window_functions(Item_window_func *item_win, return is_error; } + +/* Make a list that is a concation of two lists of ORDER elements */ + +static ORDER* concat_order_lists(MEM_ROOT *mem_root, ORDER *list1, ORDER *list2) +{ + if (!list1) + { + list1= list2; + list2= NULL; + } + + ORDER *res= NULL; // first element in the new list + ORDER *prev= NULL; // last element in the new list + ORDER *cur_list= list1; // this goes through list1, list2 + while (cur_list) + { + for (ORDER *cur= cur_list; cur; cur= cur->next) + { + ORDER *copy= (ORDER*)alloc_root(mem_root, sizeof(ORDER)); + memcpy(copy, cur, sizeof(ORDER)); + if (prev) + prev->next= copy; + prev= copy; + if (!res) + res= copy; + } + + cur_list= (cur_list == list1)? list2: NULL; + } + + if (prev) + prev->next= NULL; + + return res; +} + + +bool Window_func_runner::setup(THD *thd) +{ + Window_spec *spec = win_func->window_spec; + + ORDER* sort_order= concat_order_lists(thd->mem_root, + spec->partition_list->first, + spec->order_list->first); + filesort= new (thd->mem_root) Filesort(sort_order, HA_POS_ERROR, NULL); + + Item_sum::Sumfunctype type= win_func->window_func()->sum_func(); + switch (type) + { + case Item_sum::ROW_NUMBER_FUNC: + case Item_sum::RANK_FUNC: + case Item_sum::DENSE_RANK_FUNC: + { + /* + One-pass window function computation, walk through the rows and + assign values. + */ + compute_func= compute_window_func_values; + break; + } + case Item_sum::PERCENT_RANK_FUNC: + case Item_sum::CUME_DIST_FUNC: + { + compute_func= compute_two_pass_window_functions; + break; + } + case Item_sum::COUNT_FUNC: + case Item_sum::SUM_BIT_FUNC: + case Item_sum::SUM_FUNC: + case Item_sum::AVG_FUNC: + { + /* + Frame-aware window function computation. It does one pass, but + uses three cursors -frame_start, current_row, and frame_end. + */ + compute_func= compute_window_func_with_frames; + break; + } + default: + DBUG_ASSERT(0); + } + + first_run= true; + return false; +} + + +/* + Compute the value of window function for all rows. +*/ +bool Window_func_runner::exec(JOIN *join) +{ + THD *thd= join->thd; + + /* + We have to call setup_partition_border_check here. + + The reason is as follows: + When computing the value of sorting criteria from OVER (PARTITION ... + ORDER BY ...) clauses, we need to read temp.table fields. + + This is achieved by ORDER::item being Item** object, which points into + ref_pointer_array. + Ref_pointer_array initially points to source table fields. + At execution phase, it is set to point to the temp.table fields. + + We need to use items after this step is done. + TODO: an alternative is to use something that points into + ref_pointer_array, too. Something like wrap order->item in an Item_ref + object. + */ + if (first_run) + { + win_func->setup_partition_border_check(thd); + first_run= false; + } + + if (create_sort_index(thd, join, &join->join_tab[join->top_join_tab_count], + filesort)); + + win_func->set_phase_to_computation(); + + /* + Go through the sorted array and compute the window function + */ + READ_RECORD info; + TABLE *tbl= join->join_tab[join->top_join_tab_count].table; + + if (init_read_record(&info, thd, tbl, NULL/*select*/, 0, 1, FALSE)) + return true; + + bool is_error= compute_func(win_func, tbl, &info); + + /* This calls filesort_free_buffers(): */ + end_read_record(&info); + + //TODO: should this be moved to cleanup: ? + free_io_cache(tbl); + + win_func->set_phase_to_retrieval(); + + return is_error; +} + + +bool Window_funcs_computation::setup(THD *thd, + List<Item_window_func> *window_funcs) +{ + List_iterator_fast<Item_window_func> it(*window_funcs); + Item_window_func *item_win; + Window_func_runner *runner; + // for each window function + while ((item_win= it++)) + { + // Create a runner and call setup for it + if (!(runner= new Window_func_runner(item_win)) || + runner->setup(thd)) + { + return true; + } + win_func_runners.push_back(runner, thd->mem_root); + } + return false; +} + + +bool Window_funcs_computation::exec(JOIN *join) +{ + List_iterator<Window_func_runner> it(win_func_runners); + Window_func_runner *runner; + /* Execute each runner */ + while ((runner = it++)) + { + if (runner->exec(join)) + return true; + } + return false; +} + + +void Window_funcs_computation::cleanup() +{ + List_iterator<Window_func_runner> it(win_func_runners); + Window_func_runner *runner; + while ((runner = it++)) + { + runner->cleanup(); + delete runner; + } +} + ///////////////////////////////////////////////////////////////////////////// // Unneeded comments (will be removed when we develop a replacement for // the feature that was attempted here @@ -1564,112 +1755,3 @@ bool compute_two_pass_window_functions(Item_window_func *item_win, else #endif - -/* - @brief - This function is called by JOIN::exec to compute window function values - - @detail - JOIN::exec calls this after it has filled the temporary table with query - output. The temporary table has fields to store window function values. - - @return - false OK - true Error -*/ - -bool JOIN::process_window_functions(List<Item_window_func> *window_funcs) -{ - List_iterator_fast<Item_window_func> it(*window_funcs); - Item_window_func *item_win; - - while ((item_win= it++)) - { - item_win->set_phase_to_computation(); - Window_spec *spec = item_win->window_spec; - /* - The sorting criteria should be - (spec->partition_list, spec->order_list) - - Connect the two lists for the duration of add_sorting_to_table() - call. - */ - DBUG_ASSERT(spec->partition_list->next[0] == NULL); - *(spec->partition_list->next)= spec->order_list->first; - - /* - join_tab[top_join_tab_count].table is the temp. table where join - output was stored. - */ - // CAUTION: The sorting criteria list is not yet connected - add_sorting_to_table(&join_tab[top_join_tab_count], - spec->partition_list->first); - join_tab[top_join_tab_count].used_for_window_func= true; - - create_sort_index(this->thd, this, &join_tab[top_join_tab_count]); - /* Disconnect order_list from partition_list */ - *(spec->partition_list->next)= NULL; - - /* - Go through the sorted array and compute the window function - */ - READ_RECORD info; - TABLE *tbl= join_tab[top_join_tab_count].table; - if (init_read_record(&info, thd, tbl, select, 0, 1, FALSE)) - return true; - bool is_error= false; - - item_win->setup_partition_border_check(thd); - - Item_sum::Sumfunctype type= item_win->window_func()->sum_func(); - switch (type) { - case Item_sum::ROW_NUMBER_FUNC: - case Item_sum::RANK_FUNC: - case Item_sum::DENSE_RANK_FUNC: - { - /* - One-pass window function computation, walk through the rows and - assign values. - */ - if (compute_window_func_values(item_win, tbl, &info)) - is_error= true; - break; - } - case Item_sum::PERCENT_RANK_FUNC: - case Item_sum::CUME_DIST_FUNC: - { - if (compute_two_pass_window_functions(item_win, tbl, &info)) - is_error= true; - break; - } - case Item_sum::COUNT_FUNC: - case Item_sum::SUM_BIT_FUNC: - case Item_sum::SUM_FUNC: - case Item_sum::AVG_FUNC: - { - /* - Frame-aware window function computation. It does one pass, but - uses three cursors -frame_start, current_row, and frame_end. - */ - if (compute_window_func_with_frames(item_win, tbl, &info)) - is_error= true; - break; - } - default: - DBUG_ASSERT(0); - } - - item_win->set_phase_to_retrieval(); - /* This calls filesort_free_buffers(): */ - end_read_record(&info); - - delete join_tab[top_join_tab_count].filesort; - join_tab[top_join_tab_count].filesort= NULL; - free_io_cache(tbl); - - if (is_error) - return true; - } - return false; -} - diff --git a/sql/sql_window.h b/sql/sql_window.h index 8a4dfb7630f..6c6bd0b9b71 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -4,6 +4,8 @@ #include "my_global.h" #include "item.h" +#include "filesort.h" +#include "records.h" class Item_window_func; @@ -134,4 +136,62 @@ int setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, List<Item> &fields, List<Item> &all_fields, List<Window_spec> &win_specs, List<Item_window_func> &win_funcs); + +////////////////////////////////////////////////////////////////////////////// +// Classes that make window functions computation a part of SELECT's query plan +////////////////////////////////////////////////////////////////////////////// + +typedef bool (*window_compute_func_t)(Item_window_func *item_win, + TABLE *tbl, READ_RECORD *info); + +/* + This handles computation of one window function. + + Currently, we make a spearate filesort() call for each window function. +*/ + +class Window_func_runner : public Sql_alloc +{ + Item_window_func *win_func; + /* Window function can be computed over this sorting */ + Filesort *filesort; + + /* The function to use for computation*/ + window_compute_func_t compute_func; + + bool first_run; +public: + Window_func_runner(Item_window_func *win_func_arg) : + win_func(win_func_arg) + {} + + // Set things up. Create filesort structures, etc + bool setup(THD *thd); + + // This sorts and runs the window function. + bool exec(JOIN *join); + + void cleanup() { delete filesort; } +}; + + +/* + This is a "window function computation phase": a single object of this class + takes care of computing all window functions in a SELECT. + + - JOIN optimizer is exected to call setup() during query optimization. + - JOIN::exec() should call exec() once it has collected join output in a + temporary table. +*/ + +class Window_funcs_computation : public Sql_alloc +{ + List<Window_func_runner> win_func_runners; +public: + bool setup(THD *thd, List<Item_window_func> *window_funcs); + bool exec(JOIN *join); + void cleanup(); +}; + + #endif /* SQL_WINDOW_INCLUDED */ |