Commit ce8a0d8e authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-9676: RANGE-type frames for window functions

- Handle ORDER BY DESC in window definitions.
- Fix an issue in Frame_range_current_row_top
parent b8d8d9b8
...@@ -91,6 +91,18 @@ pk a rank() over (order by a) ...@@ -91,6 +91,18 @@ pk a rank() over (order by a)
7 2 5 7 2 5
9 4 9 9 4 9
10 4 9 10 4 9
select pk, a, rank() over (order by a desc) from t2;
pk a rank() over (order by a desc)
1 0 9
2 0 9
3 1 7
4 1 7
8 2 3
5 2 3
6 2 3
7 2 3
9 4 1
10 4 1
drop table t2; drop table t2;
# #
# Try DENSE_RANK() function # Try DENSE_RANK() function
...@@ -252,6 +264,23 @@ pk c CNT ...@@ -252,6 +264,23 @@ pk c CNT
8 2 2 8 2 2
9 2 2 9 2 2
10 2 1 10 2 1
# Check ORDER BY DESC
select
pk, c,
count(*) over (partition by c order by pk desc
rows between 2 preceding and 2 following) as CNT
from t1;
pk c CNT
1 1 3
2 1 4
3 1 4
4 1 3
5 2 3
6 2 4
7 2 5
8 2 5
9 2 4
10 2 3
drop table t0,t1; drop table t0,t1;
# #
# Resolution of window names # Resolution of window names
...@@ -817,6 +846,22 @@ pk a cnt ...@@ -817,6 +846,22 @@ pk a cnt
9 72 9 9 72 9
select select
pk, a, pk, a,
count(a) over (ORDER BY a DESC
RANGE BETWEEN UNBOUNDED PRECEDING
AND 10 FOLLOWING) as cnt
from t1;
pk a cnt
1 1 9
2 2 9
3 4 9
4 8 9
5 26 5
6 27 5
7 40 3
8 71 2
9 72 2
select
pk, a,
count(a) over (ORDER BY a count(a) over (ORDER BY a
RANGE BETWEEN UNBOUNDED PRECEDING RANGE BETWEEN UNBOUNDED PRECEDING
AND 1 FOLLOWING) as cnt AND 1 FOLLOWING) as cnt
...@@ -849,6 +894,22 @@ pk a cnt ...@@ -849,6 +894,22 @@ pk a cnt
9 72 7 9 72 7
select select
pk, a, pk, a,
count(a) over (ORDER BY a DESC
RANGE BETWEEN UNBOUNDED PRECEDING
AND 10 PRECEDING) as cnt
from t1;
pk a cnt
1 1 5
2 2 5
3 4 5
4 8 5
5 26 3
6 27 3
7 40 2
8 71 0
9 72 0
select
pk, a,
count(a) over (ORDER BY a count(a) over (ORDER BY a
RANGE BETWEEN UNBOUNDED PRECEDING RANGE BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING) as cnt AND 1 PRECEDING) as cnt
...@@ -881,6 +942,22 @@ pk a cnt ...@@ -881,6 +942,22 @@ pk a cnt
9 72 2 9 72 2
select select
pk, a, pk, a,
count(a) over (ORDER BY a DESC
RANGE BETWEEN 1 PRECEDING
AND CURRENT ROW) as cnt
from t1;
pk a cnt
1 1 2
2 2 1
3 4 1
4 8 1
5 26 2
6 27 1
7 40 1
8 71 2
9 72 1
select
pk, a,
count(a) over (ORDER BY a count(a) over (ORDER BY a
RANGE BETWEEN 1 FOLLOWING RANGE BETWEEN 1 FOLLOWING
AND 3 FOLLOWING) as cnt AND 3 FOLLOWING) as cnt
...@@ -895,6 +972,39 @@ pk a cnt ...@@ -895,6 +972,39 @@ pk a cnt
7 40 0 7 40 0
8 71 1 8 71 1
9 72 0 9 72 0
# Try CURRENT ROW with[out] DESC
select
pk, a,
count(a) over (ORDER BY a
RANGE BETWEEN CURRENT ROW
AND 1 FOLLOWING) as cnt
from t1;
pk a cnt
1 1 2
2 2 1
3 4 1
4 8 1
5 26 2
6 27 1
7 40 1
8 71 2
9 72 1
select
pk, a,
count(a) over (order by a desc
range between current row
and 1 following) as cnt
from t1;
pk a cnt
1 1 1
2 2 2
3 4 1
4 8 1
5 26 1
6 27 2
7 40 1
8 71 1
9 72 2
insert into t1 select 22, pk, a from t1; insert into t1 select 22, pk, a from t1;
select select
part_id, pk, a, part_id, pk, a,
......
...@@ -82,6 +82,7 @@ insert into t2 values ...@@ -82,6 +82,7 @@ insert into t2 values
(10 , 4); (10 , 4);
select pk, a, rank() over (order by a) from t2; select pk, a, rank() over (order by a) from t2;
select pk, a, rank() over (order by a desc) from t2;
drop table t2; drop table t2;
...@@ -158,6 +159,13 @@ select ...@@ -158,6 +159,13 @@ select
rows between current row and 1 following) as CNT rows between current row and 1 following) as CNT
from t1; from t1;
--echo # Check ORDER BY DESC
select
pk, c,
count(*) over (partition by c order by pk desc
rows between 2 preceding and 2 following) as CNT
from t1;
drop table t0,t1; drop table t0,t1;
--echo # --echo #
...@@ -514,6 +522,13 @@ select ...@@ -514,6 +522,13 @@ select
AND 10 FOLLOWING) as cnt AND 10 FOLLOWING) as cnt
from t1; from t1;
select
pk, a,
count(a) over (ORDER BY a DESC
RANGE BETWEEN UNBOUNDED PRECEDING
AND 10 FOLLOWING) as cnt
from t1;
select select
pk, a, pk, a,
count(a) over (ORDER BY a count(a) over (ORDER BY a
...@@ -528,6 +543,13 @@ select ...@@ -528,6 +543,13 @@ select
AND 10 PRECEDING) as cnt AND 10 PRECEDING) as cnt
from t1; from t1;
select
pk, a,
count(a) over (ORDER BY a DESC
RANGE BETWEEN UNBOUNDED PRECEDING
AND 10 PRECEDING) as cnt
from t1;
select select
pk, a, pk, a,
count(a) over (ORDER BY a count(a) over (ORDER BY a
...@@ -543,6 +565,13 @@ select ...@@ -543,6 +565,13 @@ select
AND CURRENT ROW) as cnt AND CURRENT ROW) as cnt
from t1; from t1;
select
pk, a,
count(a) over (ORDER BY a DESC
RANGE BETWEEN 1 PRECEDING
AND CURRENT ROW) as cnt
from t1;
select select
pk, a, pk, a,
count(a) over (ORDER BY a count(a) over (ORDER BY a
...@@ -550,6 +579,22 @@ select ...@@ -550,6 +579,22 @@ select
AND 3 FOLLOWING) as cnt AND 3 FOLLOWING) as cnt
from t1; from t1;
--echo # Try CURRENT ROW with[out] DESC
select
pk, a,
count(a) over (ORDER BY a
RANGE BETWEEN CURRENT ROW
AND 1 FOLLOWING) as cnt
from t1;
select
pk, a,
count(a) over (order by a desc
range between current row
and 1 following) as cnt
from t1;
# Try with partitions # Try with partitions
insert into t1 select 22, pk, a from t1; insert into t1 select 22, pk, a from t1;
select select
......
...@@ -275,9 +275,9 @@ public: ...@@ -275,9 +275,9 @@ public:
{ {
int rc= read_record->table->file->ha_rnd_pos(read_record->record, p); int rc= read_record->table->file->ha_rnd_pos(read_record->record, p);
if (!rc) if (!rc)
return true; return true; // restored ok
} }
return false; return false; // didn't restore
} }
// todo: should move_to() also read row here? // todo: should move_to() also read row here?
...@@ -409,6 +409,11 @@ class Frame_range_n_top : public Frame_cursor ...@@ -409,6 +409,11 @@ class Frame_range_n_top : public Frame_cursor
Item *item_add; Item *item_add;
const bool is_preceding; const bool is_preceding;
/*
1 when order_list uses ASC ordering
-1 when order_list uses DESC ordering
*/
int order_direction;
public: public:
Frame_range_n_top(bool is_preceding_arg, Item *n_val_arg) : Frame_range_n_top(bool is_preceding_arg, Item *n_val_arg) :
n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg) n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
...@@ -422,9 +427,18 @@ public: ...@@ -422,9 +427,18 @@ public:
DBUG_ASSERT(order_list->elements == 1); DBUG_ASSERT(order_list->elements == 1);
Item *src_expr= order_list->first->item[0]; Item *src_expr= order_list->first->item[0];
if (order_list->first->direction == ORDER::ORDER_ASC)
order_direction= 1;
else
order_direction= -1;
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE); range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
if (is_preceding)
bool use_minus= is_preceding;
if (order_direction == -1)
use_minus= !use_minus;
if (use_minus)
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val); item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
else else
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val); item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
...@@ -458,7 +472,7 @@ public: ...@@ -458,7 +472,7 @@ public:
*/ */
if (cursor.restore_last_row()) if (cursor.restore_last_row())
{ {
if (range_expr->cmp_read_only() <= 0) if (order_direction * range_expr->cmp_read_only() <= 0)
return; return;
item->remove(); item->remove();
} }
...@@ -470,7 +484,7 @@ private: ...@@ -470,7 +484,7 @@ private:
{ {
while (!cursor.get_next()) while (!cursor.get_next())
{ {
if (range_expr->cmp_read_only() <= 0) if (order_direction * range_expr->cmp_read_only() <= 0)
break; break;
item->remove(); item->remove();
} }
...@@ -487,6 +501,8 @@ private: ...@@ -487,6 +501,8 @@ private:
Bottom end moves first so it needs to check for partition end Bottom end moves first so it needs to check for partition end
(todo: unless it's PRECEDING and in that case it doesnt) (todo: unless it's PRECEDING and in that case it doesnt)
(todo: factor out common parts with Frame_range_n_top into
a common ancestor)
*/ */
class Frame_range_n_bottom: public Frame_cursor class Frame_range_n_bottom: public Frame_cursor
...@@ -502,6 +518,12 @@ class Frame_range_n_bottom: public Frame_cursor ...@@ -502,6 +518,12 @@ class Frame_range_n_bottom: public Frame_cursor
Group_bound_tracker bound_tracker; Group_bound_tracker bound_tracker;
bool end_of_partition; bool end_of_partition;
/*
1 when order_list uses ASC ordering
-1 when order_list uses DESC ordering
*/
int order_direction;
public: public:
Frame_range_n_bottom(bool is_preceding_arg, Item *n_val_arg) : Frame_range_n_bottom(bool is_preceding_arg, Item *n_val_arg) :
n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg) n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
...@@ -516,8 +538,18 @@ public: ...@@ -516,8 +538,18 @@ public:
DBUG_ASSERT(order_list->elements == 1); DBUG_ASSERT(order_list->elements == 1);
Item *src_expr= order_list->first->item[0]; Item *src_expr= order_list->first->item[0];
if (order_list->first->direction == ORDER::ORDER_ASC)
order_direction= 1;
else
order_direction= -1;
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE); range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
if (is_preceding)
bool use_minus= is_preceding;
if (order_direction == -1)
use_minus= !use_minus;
if (use_minus)
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val); item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
else else
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val); item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
...@@ -560,7 +592,7 @@ public: ...@@ -560,7 +592,7 @@ public:
*/ */
if (cursor.restore_last_row()) if (cursor.restore_last_row())
{ {
if (range_expr->cmp_read_only() < 0) if (order_direction * range_expr->cmp_read_only() < 0)
return; return;
item->add(); item->add();
} }
...@@ -578,7 +610,7 @@ private: ...@@ -578,7 +610,7 @@ private:
end_of_partition= true; end_of_partition= true;
break; break;
} }
if (range_expr->cmp_read_only() < 0) if (order_direction * range_expr->cmp_read_only() < 0)
break; break;
item->add(); item->add();
} }
...@@ -695,7 +727,6 @@ class Frame_range_current_row_top : public Frame_cursor ...@@ -695,7 +727,6 @@ class Frame_range_current_row_top : public Frame_cursor
Group_bound_tracker peer_tracker; Group_bound_tracker peer_tracker;
bool move; bool move;
bool at_partition_start;
public: public:
void init(THD *thd, READ_RECORD *info, void init(THD *thd, READ_RECORD *info,
SQL_I_List<ORDER> *partition_list, SQL_I_List<ORDER> *partition_list,
...@@ -709,31 +740,33 @@ public: ...@@ -709,31 +740,33 @@ public:
void pre_next_partition(longlong rownum, Item_sum* item) void pre_next_partition(longlong rownum, Item_sum* item)
{ {
// fetch the value from the first row // Fetch the value from the first row
peer_tracker.check_if_next_group(); peer_tracker.check_if_next_group();
}
void next_partition(longlong rownum, Item_sum* item)
{
at_partition_start= true;
cursor.move_to(rownum+1); cursor.move_to(rownum+1);
} }
void next_partition(longlong rownum, Item_sum* item) {}
void pre_next_row(Item_sum* item) void pre_next_row(Item_sum* item)
{ {
// Check if our current row is pointing to a peer of the current row. // Check if the new current_row is a peer of the row that our cursor is
// If not, move forward until that becomes true. // pointing to.
move= peer_tracker.check_if_next_group(); move= peer_tracker.check_if_next_group();
} }
void next_row(Item_sum* item) void next_row(Item_sum* item)
{ {
bool was_at_partition_start= at_partition_start;
at_partition_start= false;
if (move) if (move)
{ {
if (!was_at_partition_start && cursor.restore_last_row()) /*
Our cursor is pointing at the first row that was a peer of the previous
current row. Or, it was the first row in the partition.
*/
if (cursor.restore_last_row())
{ {
// todo: need the following check ?
if (!peer_tracker.compare_with_cache())
return;
item->remove(); item->remove();
} }
......
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