diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 57a130ff6b9..94d50335eff 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3235,6 +3235,7 @@ void JOIN::exec_inner() error= thd->is_error(); DBUG_VOID_RETURN; } + process_window_functions(curr_fields_list); THD_STAGE_INFO(thd, stage_sending_data); DBUG_PRINT("info", ("%s", thd->proc_info)); diff --git a/sql/sql_select.h b/sql/sql_select.h index 59f29239f5e..5aa1924adfa 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1518,6 +1518,9 @@ public: int reinit(); int init_execution(); void exec(); + + void process_window_functions(List *curr_fields_list); + void exec_inner(); bool prepare_result(List **columns_list); int destroy(); @@ -2291,4 +2294,5 @@ public: int execute(JOIN *join); }; +bool test_if_order_compatible(SQL_I_List &a, SQL_I_List &b); #endif /* SQL_SELECT_INCLUDED */ diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 493a9686ff8..b5be3db15ef 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1,4 +1,6 @@ #include "sql_select.h" +#include "item_windowfunc.h" +#include "filesort.h" #include "sql_window.h" @@ -77,3 +79,140 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, } DBUG_RETURN(0); } + + +/* + @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. +*/ + +void JOIN::process_window_functions(List *curr_fields_list) +{ + /* + TODO Get this code to set can_compute_window_function during preparation, + not during execution. + + The reason for this is the following: + Our single scan optimization for window functions without tmp table, + is valid, if and only if, we only need to perform one sorting operation, + via filesort. The cases where we need to perform one sorting operation only: + + * A select with only one window function. + * A select with multiple window functions, but they must have their + partition and order by clauses compatible. This means that one ordering + is acceptable for both window functions. + + For example: + partition by a, b, c; order by d, e results in sorting by a b c d e. + partition by a; order by d results in sorting by a d. + + This kind of sorting is compatible. The less specific partition does + not care for the order of b and c columns so it is valid if we sort + by those in case of equality over a. + + partition by a, b; order by d, e results in sorting by a b d e + partition by a; order by e results in sorting by a e + + This sorting is incompatible due to the order by clause. The partition by + clause is compatible, (partition by a) is a prefix for (partition by a, b) + However, order by e is not a prefix for order by d, e, thus it is not + compatible. + + The rule for having compatible sorting is thus: + Each partition order must contain the other window functions partitions + prefixes, or be a prefix itself. This must hold true for all partitions. + Analog for the order by clause. + + */ + + List window_functions; + SQL_I_List largest_partition; + SQL_I_List largest_order_by; + List_iterator_fast it(*curr_fields_list); + bool can_compute_window_live = !need_tmp; + + Item *item; + // Construct the window_functions item list and check if they can be + // computed using only one sorting. + // + // TODO: Perhaps group functions into compatible sorting bins + // to minimize the number of sorting passes required to compute all of them. + while ((item= it++)) + { + if (item->type() == Item::WINDOW_FUNC_ITEM) + { + Item_window_func *item_win = (Item_window_func *) item; + window_functions.push_back(item_win); + if (!can_compute_window_live) + continue; // No point checking since we have to perform multiple sorts. + Window_spec *spec = item_win->window_spec; + // Having an empty partition list on one window function and a + // not empty list on a separate window function causes the sorting + // to be incompatible. + // + // Example: + // over (partition by a, order by x) && over (order by x). + // + // The first function requires an ordering by a first and then by x, + // while the seond function requires an ordering by x first. + // The same restriction is not required for the order by clause. + if (largest_partition.elements && !spec->partition_list.elements) + { + can_compute_window_live= FALSE; + continue; + } + can_compute_window_live= test_if_order_compatible(largest_partition, + spec->partition_list); + if (!can_compute_window_live) + continue; + + can_compute_window_live= test_if_order_compatible(largest_order_by, + spec->order_list); + if (!can_compute_window_live) + continue; + + if (largest_partition.elements < spec->partition_list.elements) + largest_partition = spec->partition_list; + if (largest_order_by.elements < spec->order_list.elements) + largest_order_by = spec->order_list; + } + } + + if (can_compute_window_live && window_functions.elements && table_count == 1) + { + ha_rows examined_rows = 0; + ha_rows found_rows = 0; + ha_rows filesort_retval; + SORT_FIELD *s_order= (SORT_FIELD *) my_malloc(sizeof(SORT_FIELD) * + (largest_partition.elements + largest_order_by.elements) + 1, + MYF(MY_WME | MY_ZEROFILL | MY_THREAD_SPECIFIC)); + + size_t pos= 0; + for (ORDER* curr = largest_partition.first; curr; curr=curr->next, pos++) + s_order[pos].item = *curr->item; + + for (ORDER* curr = largest_order_by.first; curr; curr=curr->next, pos++) + s_order[pos].item = *curr->item; + + table[0]->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_WME | MY_ZEROFILL| + MY_THREAD_SPECIFIC)); + + + filesort_retval= filesort(thd, table[0], s_order, + (largest_partition.elements + largest_order_by.elements), + this->select, HA_POS_ERROR, FALSE, + &examined_rows, &found_rows, + this->explain->ops_tracker.report_sorting(thd)); + table[0]->sort.found_records= filesort_retval; + + join_tab->read_first_record = join_init_read_record; + join_tab->records= found_rows; + + my_free(s_order); + } +} diff --git a/sql/sql_window.h b/sql/sql_window.h index 680dbcfd63f..9f98290c227 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -5,6 +5,13 @@ #include "my_global.h" #include "item.h" +/* + Window functions module. + + Each instance of window function has its own element in SELECT_LEX::window_specs. +*/ + + class Window_frame_bound : public Sql_alloc {