mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
@ -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,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
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,
|
|||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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);
|
|||||||
/* 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 @@ public:
|
|||||||
}
|
}
|
||||||
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
|
|||||||
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 @@ public:
|
|||||||
{
|
{
|
||||||
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
|
|||||||
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 @@ public:
|
|||||||
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 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
{
|
{
|
||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
|
|
||||||
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
|
|||||||
{
|
{
|
||||||
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
|
|||||||
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 @@ public:
|
|||||||
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 @@ public:
|
|||||||
*/
|
*/
|
||||||
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();
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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,
|
|||||||
|
|
||||||
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 @@ public:
|
|||||||
|
|
||||||
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.
|
||||||
|
Reference in New Issue
Block a user