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
...@@ -41,7 +41,7 @@ Item_window_func::resolve_window_name(THD *thd) ...@@ -41,7 +41,7 @@ Item_window_func::resolve_window_name(THD *thd)
return true; return true;
} }
return false; return false;
} }
...@@ -154,7 +154,7 @@ void Item_window_func::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, ...@@ -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 @detail
If we attempt to do it in fix_fields(), partition_fields will refer 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, ...@@ -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. 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) void Item_sum_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{ {
/* TODO: move this into Item_window_func? */ /* 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(); clear();
} }
void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec) void Item_sum_dense_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{ {
/* TODO: consider moving this && Item_sum_rank's implementation */ /* 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(); clear();
} }
bool Item_sum_dense_rank::add() 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; first_add= false;
dense_rank++; dense_rank++;
...@@ -198,7 +193,7 @@ bool Item_sum_dense_rank::add() ...@@ -198,7 +193,7 @@ bool Item_sum_dense_rank::add()
bool Item_sum_rank::add() bool Item_sum_rank::add()
{ {
row_number++; row_number++;
if (peer_tracker.check_if_next_group()) if (peer_tracker->check_if_next_group())
{ {
/* Row value changed */ /* Row value changed */
cur_rank= row_number; cur_rank= row_number;
...@@ -206,25 +201,10 @@ bool Item_sum_rank::add() ...@@ -206,25 +201,10 @@ bool Item_sum_rank::add()
return false; 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() bool Item_sum_percent_rank::add()
{ {
row_number++; row_number++;
if (peer_tracker.check_if_next_group()) if (peer_tracker->check_if_next_group())
{ {
/* Row value changed. */ /* Row value changed. */
cur_rank= row_number; cur_rank= row_number;
...@@ -235,8 +215,7 @@ bool Item_sum_percent_rank::add() ...@@ -235,8 +215,7 @@ bool Item_sum_percent_rank::add()
void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec) void Item_sum_percent_rank::setup_window_func(THD *thd, Window_spec *window_spec)
{ {
/* TODO: move this into Item_window_func? */ /* 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(); clear();
} }
...@@ -12,25 +12,19 @@ int test_if_group_changed(List<Cached_item> &list); ...@@ -12,25 +12,19 @@ int test_if_group_changed(List<Cached_item> &list);
/* A wrapper around test_if_group_changed */ /* A wrapper around test_if_group_changed */
class Group_bound_tracker 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: 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) for (ORDER *curr = list->first; curr; curr=curr->next)
{ {
Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE); Cached_item *tmp= new_Cached_item(thd, curr->item[0], TRUE);
group_fields.push_back(tmp); group_fields.push_back(tmp);
} }
}
void init()
{
first_check= true; first_check= true;
} }
...@@ -76,6 +70,19 @@ class Group_bound_tracker ...@@ -76,6 +70,19 @@ class Group_bound_tracker
} }
return 0; 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 ...@@ -92,19 +99,22 @@ class Item_sum_row_number: public Item_sum_int
longlong count; longlong count;
public: public:
Item_sum_row_number(THD *thd)
: Item_sum_int(thd), count(0) {}
void clear() void clear()
{ {
count= 0; count= 0;
} }
bool add()
bool add()
{ {
count++; count++;
return false; return false;
} }
void update_field() {}
Item_sum_row_number(THD *thd) void update_field() {}
: Item_sum_int(thd), count(0) {}
enum Sumfunctype sum_func() const enum Sumfunctype sum_func() const
{ {
...@@ -119,6 +129,7 @@ class Item_sum_row_number: public Item_sum_int ...@@ -119,6 +129,7 @@ class Item_sum_row_number: public Item_sum_int
{ {
return "row_number("; return "row_number(";
} }
Item *get_copy(THD *thd, MEM_ROOT *mem_root) Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_sum_row_number>(thd, mem_root, this); } { return get_item_copy<Item_sum_row_number>(thd, mem_root, this); }
}; };
...@@ -146,9 +157,12 @@ class Item_sum_rank: public Item_sum_int ...@@ -146,9 +157,12 @@ class Item_sum_rank: public Item_sum_int
protected: protected:
longlong row_number; // just ROW_NUMBER() longlong row_number; // just ROW_NUMBER()
longlong cur_rank; // current value longlong cur_rank; // current value
Group_bound_tracker peer_tracker; Group_bound_tracker *peer_tracker;
public: public:
Item_sum_rank(THD *thd) : Item_sum_int(thd), peer_tracker(NULL) {}
void clear() void clear()
{ {
/* This is called on partition start */ /* This is called on partition start */
...@@ -169,10 +183,6 @@ class Item_sum_rank: public Item_sum_int ...@@ -169,10 +183,6 @@ class Item_sum_rank: public Item_sum_int
TODO: ^^ what does this do ? It is not called ever? 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 enum Sumfunctype sum_func () const
{ {
return RANK_FUNC; return RANK_FUNC;
...@@ -184,9 +194,12 @@ class Item_sum_rank: public Item_sum_int ...@@ -184,9 +194,12 @@ class Item_sum_rank: public Item_sum_int
} }
void setup_window_func(THD *thd, Window_spec *window_spec); void setup_window_func(THD *thd, Window_spec *window_spec);
void cleanup() void cleanup()
{ {
peer_tracker.cleanup(); if (peer_tracker)
peer_tracker->cleanup();
delete peer_tracker;
Item_sum_int::cleanup(); Item_sum_int::cleanup();
} }
Item *get_copy(THD *thd, MEM_ROOT *mem_root) Item *get_copy(THD *thd, MEM_ROOT *mem_root)
...@@ -217,7 +230,7 @@ class Item_sum_dense_rank: public Item_sum_int ...@@ -217,7 +230,7 @@ class Item_sum_dense_rank: public Item_sum_int
{ {
longlong dense_rank; longlong dense_rank;
bool first_add; bool first_add;
Group_bound_tracker peer_tracker; Group_bound_tracker *peer_tracker;
public: public:
/* /*
XXX(cvicentiu) This class could potentially be implemented in the rank XXX(cvicentiu) This class could potentially be implemented in the rank
...@@ -236,7 +249,7 @@ class Item_sum_dense_rank: public Item_sum_int ...@@ -236,7 +249,7 @@ class Item_sum_dense_rank: public Item_sum_int
} }
Item_sum_dense_rank(THD *thd) 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 enum Sumfunctype sum_func () const
{ {
return DENSE_RANK_FUNC; return DENSE_RANK_FUNC;
...@@ -251,7 +264,11 @@ class Item_sum_dense_rank: public Item_sum_int ...@@ -251,7 +264,11 @@ class Item_sum_dense_rank: public Item_sum_int
void cleanup() void cleanup()
{ {
peer_tracker.cleanup(); if (peer_tracker)
{
peer_tracker->cleanup();
delete peer_tracker;
}
Item_sum_int::cleanup(); Item_sum_int::cleanup();
} }
Item *get_copy(THD *thd, MEM_ROOT *mem_root) 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 ...@@ -294,7 +311,7 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count
{ {
public: public:
Item_sum_percent_rank(THD *thd) 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() longlong val_int()
{ {
...@@ -354,11 +371,15 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count ...@@ -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 cur_rank; // Current rank of the current row.
longlong row_number; // Value if this were ROW_NUMBER() function. longlong row_number; // Value if this were ROW_NUMBER() function.
Group_bound_tracker peer_tracker; Group_bound_tracker *peer_tracker;
void cleanup() void cleanup()
{ {
peer_tracker.cleanup(); if (peer_tracker)
{
peer_tracker->cleanup();
delete peer_tracker;
}
Item_sum_num::cleanup(); Item_sum_num::cleanup();
} }
}; };
...@@ -515,12 +536,6 @@ class Item_window_func : public Item_func_or_sum ...@@ -515,12 +536,6 @@ class Item_window_func : public Item_func_or_sum
public: public:
Window_spec *window_spec; 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: public:
Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name) Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name)
: Item_func_or_sum(thd, (Item *) win_func), : Item_func_or_sum(thd, (Item *) win_func),
...@@ -613,9 +628,6 @@ class Item_window_func : public Item_func_or_sum ...@@ -613,9 +628,6 @@ class Item_window_func : public Item_func_or_sum
*/ */
void setup_partition_border_check(THD *thd); void setup_partition_border_check(THD *thd);
void advance_window();
bool check_if_partition_changed();
enum_field_types field_type() const enum_field_types field_type() const
{ {
return ((Item_sum *) args[0])->field_type(); 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, ...@@ -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 // Classes that make window functions computation a part of SELECT's query plan
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
typedef bool (*window_compute_func_t)(Item_window_func *item_win, class Frame_cursor;
TABLE *tbl, READ_RECORD *info);
/* /*
This handles computation of one window function. This handles computation of one window function.
...@@ -165,21 +163,17 @@ typedef bool (*window_compute_func_t)(Item_window_func *item_win, ...@@ -165,21 +163,17 @@ typedef bool (*window_compute_func_t)(Item_window_func *item_win,
class Window_func_runner : public Sql_alloc 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: public:
Window_func_runner(Item_window_func *win_func_arg) : /* Add the function to be computed during the execution pass */
win_func(win_func_arg) bool add_function_to_run(Item_window_func *win_func);
{}
// Set things up. Create filesort structures, etc /* Compute and fill the fields in the table. */
bool setup(THD *thd); bool exec(THD *thd, TABLE *tbl, SORT_INFO *filesort_result);
// This sorts and runs the window function. private:
bool exec(TABLE *tbl, SORT_INFO *filesort_result); /* 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 ...@@ -191,21 +185,24 @@ class Window_func_runner : public Sql_alloc
class Window_funcs_sort : 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: public:
bool setup(THD *thd, SQL_SELECT *sel, List_iterator<Item_window_func> &it); bool setup(THD *thd, SQL_SELECT *sel, List_iterator<Item_window_func> &it);
bool exec(JOIN *join); bool exec(JOIN *join);
void cleanup() { delete filesort; } void cleanup() { delete filesort; }
friend class Window_funcs_computation; friend class Window_funcs_computation;
private:
Window_func_runner runner;
/* Window functions can be computed over this sorting */
Filesort *filesort;
}; };
struct st_join_table; struct st_join_table;
class Explain_aggr_window_funcs; class Explain_aggr_window_funcs;
/* /*
This is a "window function computation phase": a single object of this class This is a "window function computation phase": a single object of this class
takes care of computing all window functions in a SELECT. 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