Commit 23e8b508 authored by Vicențiu Ciorbaru's avatar Vicențiu Ciorbaru

MDEV-10059: Compute window functions with same sorting criteria simultaneously

Perform only one table scan for each window function present. We do this
by keeping keeping cursors for each window function frame bound and
running them for each function for every row.
parent 19d24f01
......@@ -154,7 +154,7 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
/*
This must be called before advance_window() can be called.
This must be called before attempting to compute the window function values.
@detail
If we attempt to do it in fix_fields(), partition_fields will refer
......@@ -162,30 +162,25 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array,
We need it to refer to temp.table columns.
*/
void Item_window_func::setup_partition_border_check(THD *thd)
{
partition_tracker.init(thd, window_spec->partition_list);
window_func()->setup_window_func(thd, window_spec);
}
void Item_sum_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{
/* TODO: move this into Item_window_func? */
peer_tracker.init(thd, window_spec->order_list);
peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
peer_tracker->init();
clear();
}
void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{
/* TODO: consider moving this && Item_sum_rank's implementation */
peer_tracker.init(thd, window_spec->order_list);
peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
peer_tracker->init();
clear();
}
bool Item_sum_dense_rank::add()
{
if (peer_tracker.check_if_next_group() || first_add)
if (peer_tracker->check_if_next_group() || first_add)
{
first_add= false;
dense_rank++;
......@@ -198,7 +193,7 @@ bool Item_sum_dense_rank::add()
bool Item_sum_rank::add()
{
row_number++;
if (peer_tracker.check_if_next_group())
if (peer_tracker->check_if_next_group())
{
/* Row value changed */
cur_rank= row_number;
......@@ -206,25 +201,10 @@ bool Item_sum_rank::add()
return false;
}
bool Item_window_func::check_if_partition_changed()
{
return partition_tracker.check_if_next_group();
}
void Item_window_func::advance_window()
{
if (check_if_partition_changed())
{
/* Next partition */
window_func()->clear();
}
window_func()->add();
}
bool Item_sum_percent_rank::add()
{
row_number++;
if (peer_tracker.check_if_next_group())
if (peer_tracker->check_if_next_group())
{
/* Row value changed. */
cur_rank= row_number;
......@@ -235,8 +215,7 @@ bool Item_sum_percent_rank::add()
void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{
/* TODO: move this into Item_window_func? */
peer_tracker.init(thd, window_spec->order_list);
peer_tracker = new Group_bound_tracker(thd, window_spec->order_list);
peer_tracker->init();
clear();
}
......@@ -12,25 +12,19 @@ int test_if_group_changed(List<Cached_item> &list);
/* A wrapper around test_if_group_changed */
class Group_bound_tracker
{
List<Cached_item> group_fields;
/*
During the first check_if_next_group, the list of cached_items is not
initialized. The compare function will return that the items match if
the field's value is the same as the Cached_item's default value (0).
This flag makes sure that we always return true during the first check.
XXX This is better to be implemented within test_if_group_changed, but
since it is used in other parts of the codebase, we keep it here for now.
*/
bool first_check;
public:
void init(THD *thd, SQL_I_List<ORDER> *list)
Group_bound_tracker(THD *thd, SQL_I_List<ORDER> *list)
{
for (ORDER *curr = list->first; curr; curr=curr->next)
{
Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE);
group_fields.push_back(tmp);
}
}
void init()
{
first_check= true;
}
......@@ -76,6 +70,19 @@ class Group_bound_tracker
}
return 0;
}
private:
List<Cached_item> group_fields;
/*
During the first check_if_next_group, the list of cached_items is not
initialized. The compare function will return that the items match if
the field's value is the same as the Cached_item's default value (0).
This flag makes sure that we always return true during the first check.
XXX This is better to be implemented within test_if_group_changed, but
since it is used in other parts of the codebase, we keep it here for now.
*/
bool first_check;
};
/*
......@@ -92,19 +99,22 @@ class Item_sum_row_number: public Item_sum_int
longlong count;
public:
Item_sum_row_number(THD *thd)
: Item_sum_int(thd), count(0) {}
void clear()
{
count= 0;
}
bool add()
{
count++;
return false;
}
void update_field() {}
Item_sum_row_number(THD *thd)
: Item_sum_int(thd), count(0) {}
void update_field() {}
enum Sumfunctype sum_func() const
{
......@@ -119,6 +129,7 @@ class Item_sum_row_number: public Item_sum_int
{
return "row_number(";
}
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_sum_row_number>(thd, mem_root, this); }
};
......@@ -147,8 +158,11 @@ class Item_sum_rank: public Item_sum_int
longlong row_number; // just ROW_NUMBER()
longlong cur_rank; // current value
Group_bound_tracker peer_tracker;
Group_bound_tracker *peer_tracker;
public:
Item_sum_rank(THD *thd) : Item_sum_int(thd), peer_tracker(NULL) {}
void clear()
{
/* This is called on partition start */
......@@ -169,10 +183,6 @@ class Item_sum_rank: public Item_sum_int
TODO: ^^ what does this do ? It is not called ever?
*/
public:
Item_sum_rank(THD *thd)
: Item_sum_int(thd) {}
enum Sumfunctype sum_func () const
{
return RANK_FUNC;
......@@ -184,9 +194,12 @@ class Item_sum_rank: public Item_sum_int
}
void setup_window_func(THD *thd, Window_spec *window_spec);
void cleanup()
{
peer_tracker.cleanup();
if (peer_tracker)
peer_tracker->cleanup();
delete peer_tracker;
Item_sum_int::cleanup();
}
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
......@@ -217,7 +230,7 @@ class Item_sum_dense_rank: public Item_sum_int
{
longlong dense_rank;
bool first_add;
Group_bound_tracker peer_tracker;
Group_bound_tracker *peer_tracker;
public:
/*
XXX(cvicentiu) This class could potentially be implemented in the rank
......@@ -236,7 +249,7 @@ class Item_sum_dense_rank: public Item_sum_int
}
Item_sum_dense_rank(THD *thd)
: Item_sum_int(thd), dense_rank(0), first_add(true) {}
: Item_sum_int(thd), dense_rank(0), first_add(true), peer_tracker(NULL) {}
enum Sumfunctype sum_func () const
{
return DENSE_RANK_FUNC;
......@@ -251,7 +264,11 @@ class Item_sum_dense_rank: public Item_sum_int
void cleanup()
{
peer_tracker.cleanup();
if (peer_tracker)
{
peer_tracker->cleanup();
delete peer_tracker;
}
Item_sum_int::cleanup();
}
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
......@@ -294,7 +311,7 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
{
public:
Item_sum_percent_rank(THD *thd)
: Item_sum_window_with_row_count(thd), cur_rank(1) {}
: Item_sum_window_with_row_count(thd), cur_rank(1), peer_tracker(NULL) {}
longlong val_int()
{
......@@ -354,11 +371,15 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
longlong cur_rank; // Current rank of the current row.
longlong row_number; // Value if this were ROW_NUMBER() function.
Group_bound_tracker peer_tracker;
Group_bound_tracker *peer_tracker;
void cleanup()
{
peer_tracker.cleanup();
if (peer_tracker)
{
peer_tracker->cleanup();
delete peer_tracker;
}
Item_sum_num::cleanup();
}
};
......@@ -515,12 +536,6 @@ class Item_window_func : public Item_func_or_sum
public:
Window_spec *window_spec;
/*
This stores the data about the partition we're currently in.
advance_window() uses this to tell when we've left one partition and
entered another
*/
Group_bound_tracker partition_tracker;
public:
Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name)
: Item_func_or_sum(thd, (Item *) win_func),
......@@ -613,9 +628,6 @@ class Item_window_func : public Item_func_or_sum
*/
void setup_partition_border_check(THD *thd);
void advance_window();
bool check_if_partition_changed();
enum_field_types field_type() const
{
return ((Item_sum *) args[0])->field_type();
......
This diff is collapsed.
......@@ -154,9 +154,7 @@ int setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
// 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);
class Frame_cursor;
/*
This handles computation of one window function.
......@@ -165,21 +163,17 @@ typedef bool (*window_compute_func_t)(Item_window_func *item_win,
class Window_func_runner : public Sql_alloc
{
Item_window_func *win_func;
/* The function to use for computation*/
window_compute_func_t compute_func;
public:
Window_func_runner(Item_window_func *win_func_arg) :
win_func(win_func_arg)
{}
/* Add the function to be computed during the execution pass */
bool add_function_to_run(Item_window_func *win_func);
// Set things up. Create filesort structures, etc
bool setup(THD *thd);
/* Compute and fill the fields in the table. */
bool exec(THD *thd, TABLE *tbl, SORT_INFO *filesort_result);
// This sorts and runs the window function.
bool exec(TABLE *tbl, SORT_INFO *filesort_result);
private:
/* A list of window functions for which this Window_func_runner will compute
values during the execution phase. */
List<Item_window_func> window_functions;
};
......@@ -191,21 +185,24 @@ class Window_func_runner : public Sql_alloc
class Window_funcs_sort : public Sql_alloc
{
List<Window_func_runner> runners;
/* Window functions can be computed over this sorting */
Filesort *filesort;
public:
bool setup(THD *thd, SQL_SELECT *sel, List_iterator<Item_window_func> &it);
bool exec(JOIN *join);
void cleanup() { delete filesort; }
friend class Window_funcs_computation;
private:
Window_func_runner runner;
/* Window functions can be computed over this sorting */
Filesort *filesort;
};
struct st_join_table;
class Explain_aggr_window_funcs;
/*
This is a "window function computation phase": a single object of this class
takes care of computing all window functions in a SELECT.
......
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