Commit c17f1df8 authored by Sergei Petrunia's avatar Sergei Petrunia

Moved window function computation code from JOIN::exec_inner() into

a separate function, JOIN::process_window_functions().
parent 373cd9f5
......@@ -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));
......
......@@ -1518,6 +1518,9 @@ class JOIN :public Sql_alloc
int reinit();
int init_execution();
void exec();
void process_window_functions(List<Item> *curr_fields_list);
void exec_inner();
bool prepare_result(List<Item> **columns_list);
int destroy();
......@@ -2291,4 +2294,5 @@ class Pushdown_query: public Sql_alloc
int execute(JOIN *join);
};
bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b);
#endif /* SQL_SELECT_INCLUDED */
#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<Item> *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<Item_window_func> window_functions;
SQL_I_List<ORDER> largest_partition;
SQL_I_List<ORDER> largest_order_by;
List_iterator_fast<Item> 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);
}
}
......@@ -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
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment