Skip to content

Commit c17f1df

Browse files
committed
Moved window function computation code from JOIN::exec_inner() into
a separate function, JOIN::process_window_functions().
1 parent 373cd9f commit c17f1df

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

sql/sql_select.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,6 +3235,7 @@ void JOIN::exec_inner()
32353235
error= thd->is_error();
32363236
DBUG_VOID_RETURN;
32373237
}
3238+
process_window_functions(curr_fields_list);
32383239

32393240
THD_STAGE_INFO(thd, stage_sending_data);
32403241
DBUG_PRINT("info", ("%s", thd->proc_info));

sql/sql_select.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,9 @@ class JOIN :public Sql_alloc
15181518
int reinit();
15191519
int init_execution();
15201520
void exec();
1521+
1522+
void process_window_functions(List<Item> *curr_fields_list);
1523+
15211524
void exec_inner();
15221525
bool prepare_result(List<Item> **columns_list);
15231526
int destroy();
@@ -2291,4 +2294,5 @@ class Pushdown_query: public Sql_alloc
22912294
int execute(JOIN *join);
22922295
};
22932296

2297+
bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b);
22942298
#endif /* SQL_SELECT_INCLUDED */

sql/sql_window.cc

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "sql_select.h"
2+
#include "item_windowfunc.h"
3+
#include "filesort.h"
24
#include "sql_window.h"
35

46

@@ -77,3 +79,140 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
7779
}
7880
DBUG_RETURN(0);
7981
}
82+
83+
84+
/*
85+
@brief
86+
This function is called by JOIN::exec to compute window function values
87+
88+
@detail
89+
JOIN::exec calls this after it has filled the temporary table with query
90+
output. The temporary table has fields to store window function values.
91+
*/
92+
93+
void JOIN::process_window_functions(List<Item> *curr_fields_list)
94+
{
95+
/*
96+
TODO Get this code to set can_compute_window_function during preparation,
97+
not during execution.
98+
99+
The reason for this is the following:
100+
Our single scan optimization for window functions without tmp table,
101+
is valid, if and only if, we only need to perform one sorting operation,
102+
via filesort. The cases where we need to perform one sorting operation only:
103+
104+
* A select with only one window function.
105+
* A select with multiple window functions, but they must have their
106+
partition and order by clauses compatible. This means that one ordering
107+
is acceptable for both window functions.
108+
109+
For example:
110+
partition by a, b, c; order by d, e results in sorting by a b c d e.
111+
partition by a; order by d results in sorting by a d.
112+
113+
This kind of sorting is compatible. The less specific partition does
114+
not care for the order of b and c columns so it is valid if we sort
115+
by those in case of equality over a.
116+
117+
partition by a, b; order by d, e results in sorting by a b d e
118+
partition by a; order by e results in sorting by a e
119+
120+
This sorting is incompatible due to the order by clause. The partition by
121+
clause is compatible, (partition by a) is a prefix for (partition by a, b)
122+
However, order by e is not a prefix for order by d, e, thus it is not
123+
compatible.
124+
125+
The rule for having compatible sorting is thus:
126+
Each partition order must contain the other window functions partitions
127+
prefixes, or be a prefix itself. This must hold true for all partitions.
128+
Analog for the order by clause.
129+
130+
*/
131+
132+
List<Item_window_func> window_functions;
133+
SQL_I_List<ORDER> largest_partition;
134+
SQL_I_List<ORDER> largest_order_by;
135+
List_iterator_fast<Item> it(*curr_fields_list);
136+
bool can_compute_window_live = !need_tmp;
137+
138+
Item *item;
139+
// Construct the window_functions item list and check if they can be
140+
// computed using only one sorting.
141+
//
142+
// TODO: Perhaps group functions into compatible sorting bins
143+
// to minimize the number of sorting passes required to compute all of them.
144+
while ((item= it++))
145+
{
146+
if (item->type() == Item::WINDOW_FUNC_ITEM)
147+
{
148+
Item_window_func *item_win = (Item_window_func *) item;
149+
window_functions.push_back(item_win);
150+
if (!can_compute_window_live)
151+
continue; // No point checking since we have to perform multiple sorts.
152+
Window_spec *spec = item_win->window_spec;
153+
// Having an empty partition list on one window function and a
154+
// not empty list on a separate window function causes the sorting
155+
// to be incompatible.
156+
//
157+
// Example:
158+
// over (partition by a, order by x) && over (order by x).
159+
//
160+
// The first function requires an ordering by a first and then by x,
161+
// while the seond function requires an ordering by x first.
162+
// The same restriction is not required for the order by clause.
163+
if (largest_partition.elements && !spec->partition_list.elements)
164+
{
165+
can_compute_window_live= FALSE;
166+
continue;
167+
}
168+
can_compute_window_live= test_if_order_compatible(largest_partition,
169+
spec->partition_list);
170+
if (!can_compute_window_live)
171+
continue;
172+
173+
can_compute_window_live= test_if_order_compatible(largest_order_by,
174+
spec->order_list);
175+
if (!can_compute_window_live)
176+
continue;
177+
178+
if (largest_partition.elements < spec->partition_list.elements)
179+
largest_partition = spec->partition_list;
180+
if (largest_order_by.elements < spec->order_list.elements)
181+
largest_order_by = spec->order_list;
182+
}
183+
}
184+
185+
if (can_compute_window_live && window_functions.elements && table_count == 1)
186+
{
187+
ha_rows examined_rows = 0;
188+
ha_rows found_rows = 0;
189+
ha_rows filesort_retval;
190+
SORT_FIELD *s_order= (SORT_FIELD *) my_malloc(sizeof(SORT_FIELD) *
191+
(largest_partition.elements + largest_order_by.elements) + 1,
192+
MYF(MY_WME | MY_ZEROFILL | MY_THREAD_SPECIFIC));
193+
194+
size_t pos= 0;
195+
for (ORDER* curr = largest_partition.first; curr; curr=curr->next, pos++)
196+
s_order[pos].item = *curr->item;
197+
198+
for (ORDER* curr = largest_order_by.first; curr; curr=curr->next, pos++)
199+
s_order[pos].item = *curr->item;
200+
201+
table[0]->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
202+
MYF(MY_WME | MY_ZEROFILL|
203+
MY_THREAD_SPECIFIC));
204+
205+
206+
filesort_retval= filesort(thd, table[0], s_order,
207+
(largest_partition.elements + largest_order_by.elements),
208+
this->select, HA_POS_ERROR, FALSE,
209+
&examined_rows, &found_rows,
210+
this->explain->ops_tracker.report_sorting(thd));
211+
table[0]->sort.found_records= filesort_retval;
212+
213+
join_tab->read_first_record = join_init_read_record;
214+
join_tab->records= found_rows;
215+
216+
my_free(s_order);
217+
}
218+
}

sql/sql_window.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
#include "my_global.h"
66
#include "item.h"
77

8+
/*
9+
Window functions module.
10+
11+
Each instance of window function has its own element in SELECT_LEX::window_specs.
12+
*/
13+
14+
815
class Window_frame_bound : public Sql_alloc
916
{
1017

0 commit comments

Comments
 (0)