mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-9676: RANGE-type frames for window functions
Add support for "RANGE n PRECEDING|FOLLOWING" frame bounds. - n is currently limited to whatever Item and Item_sum_plus/minus can handle (i.e. no DATETIME intervals). - Didn't check NULL value handling yet.
This commit is contained in:
@ -299,7 +299,7 @@ public:
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -388,6 +388,206 @@ public:
|
||||
// RANGE-type frames
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
Frame_range_n_top handles the top end of RANGE-type frame.
|
||||
|
||||
That is, it handles:
|
||||
RANGE BETWEEN n PRECEDING AND ...
|
||||
RANGE BETWEEN n FOLLOWING AND ...
|
||||
|
||||
Top of the frame doesn't need to check for partition end, since bottom will
|
||||
reach it before.
|
||||
*/
|
||||
|
||||
class Frame_range_n_top : public Frame_cursor
|
||||
{
|
||||
Table_read_cursor cursor;
|
||||
|
||||
Cached_item_item *range_expr;
|
||||
|
||||
Item *n_val;
|
||||
Item *item_add;
|
||||
|
||||
const bool is_preceding;
|
||||
public:
|
||||
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)
|
||||
{}
|
||||
|
||||
void init(THD *thd, READ_RECORD *info,
|
||||
SQL_I_List<ORDER> *partition_list,
|
||||
SQL_I_List<ORDER> *order_list)
|
||||
{
|
||||
cursor.init(info);
|
||||
|
||||
DBUG_ASSERT(order_list->elements == 1);
|
||||
Item *src_expr= order_list->first->item[0];
|
||||
|
||||
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
|
||||
if (is_preceding)
|
||||
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
|
||||
else
|
||||
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
|
||||
|
||||
item_add->fix_fields(thd, &item_add);
|
||||
}
|
||||
|
||||
void pre_next_partition(longlong rownum, Item_sum* item)
|
||||
{
|
||||
// Save the value of FUNC(current_row)
|
||||
range_expr->fetch_value_from(item_add);
|
||||
}
|
||||
|
||||
void next_partition(longlong rownum, Item_sum* item)
|
||||
{
|
||||
cursor.move_to(rownum);
|
||||
walk_till_non_peer(item);
|
||||
}
|
||||
|
||||
void pre_next_row(Item_sum* item)
|
||||
{
|
||||
range_expr->fetch_value_from(item_add);
|
||||
}
|
||||
|
||||
void next_row(Item_sum* item)
|
||||
{
|
||||
/*
|
||||
Ok, our cursor is at the first row R where
|
||||
(prev_row + n) >= R
|
||||
We need to check about the current row.
|
||||
*/
|
||||
if (cursor.restore_last_row())
|
||||
{
|
||||
if (range_expr->cmp_read_only() <= 0)
|
||||
return;
|
||||
item->remove();
|
||||
}
|
||||
walk_till_non_peer(item);
|
||||
}
|
||||
|
||||
private:
|
||||
void walk_till_non_peer(Item_sum* item)
|
||||
{
|
||||
while (!cursor.get_next())
|
||||
{
|
||||
if (range_expr->cmp_read_only() <= 0)
|
||||
break;
|
||||
item->remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Frame_range_n_bottom handles bottom end of RANGE-type frame.
|
||||
|
||||
That is, it handles frame bounds in form:
|
||||
RANGE BETWEEN ... AND n PRECEDING
|
||||
RANGE BETWEEN ... AND n FOLLOWING
|
||||
|
||||
Bottom end moves first so it needs to check for partition end
|
||||
(todo: unless it's PRECEDING and in that case it doesnt)
|
||||
*/
|
||||
|
||||
class Frame_range_n_bottom: public Frame_cursor
|
||||
{
|
||||
Table_read_cursor cursor;
|
||||
|
||||
Cached_item_item *range_expr;
|
||||
|
||||
Item *n_val;
|
||||
Item *item_add;
|
||||
|
||||
const bool is_preceding;
|
||||
|
||||
Group_bound_tracker bound_tracker;
|
||||
bool end_of_partition;
|
||||
public:
|
||||
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)
|
||||
{}
|
||||
|
||||
void init(THD *thd, READ_RECORD *info,
|
||||
SQL_I_List<ORDER> *partition_list,
|
||||
SQL_I_List<ORDER> *order_list)
|
||||
{
|
||||
cursor.init(info);
|
||||
|
||||
DBUG_ASSERT(order_list->elements == 1);
|
||||
Item *src_expr= order_list->first->item[0];
|
||||
|
||||
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
|
||||
if (is_preceding)
|
||||
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
|
||||
else
|
||||
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
|
||||
|
||||
item_add->fix_fields(thd, &item_add);
|
||||
|
||||
bound_tracker.init(thd, partition_list);
|
||||
}
|
||||
|
||||
void pre_next_partition(longlong rownum, Item_sum* item)
|
||||
{
|
||||
// Save the value of FUNC(current_row)
|
||||
range_expr->fetch_value_from(item_add);
|
||||
|
||||
bound_tracker.check_if_next_group();
|
||||
end_of_partition= false;
|
||||
}
|
||||
|
||||
void next_partition(longlong rownum, Item_sum* item)
|
||||
{
|
||||
cursor.move_to(rownum);
|
||||
walk_till_non_peer(item);
|
||||
}
|
||||
|
||||
void pre_next_row(Item_sum* item)
|
||||
{
|
||||
if (end_of_partition)
|
||||
return;
|
||||
range_expr->fetch_value_from(item_add);
|
||||
}
|
||||
|
||||
void next_row(Item_sum* item)
|
||||
{
|
||||
if (end_of_partition)
|
||||
return;
|
||||
/*
|
||||
Ok, our cursor is at the first row R where
|
||||
(prev_row + n) >= R
|
||||
We need to check about the current row.
|
||||
*/
|
||||
if (cursor.restore_last_row())
|
||||
{
|
||||
if (range_expr->cmp_read_only() < 0)
|
||||
return;
|
||||
item->add();
|
||||
}
|
||||
walk_till_non_peer(item);
|
||||
}
|
||||
|
||||
private:
|
||||
void walk_till_non_peer(Item_sum* item)
|
||||
{
|
||||
int res;
|
||||
while (!(res= cursor.get_next()))
|
||||
{
|
||||
if (bound_tracker.check_if_next_group())
|
||||
{
|
||||
end_of_partition= true;
|
||||
break;
|
||||
}
|
||||
if (range_expr->cmp_read_only() < 0)
|
||||
break;
|
||||
item->add();
|
||||
}
|
||||
if (res)
|
||||
end_of_partition= true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
RANGE BETWEEN ... AND CURRENT ROW, bottom frame bound for CURRENT ROW
|
||||
...
|
||||
@ -469,7 +669,6 @@ private:
|
||||
item->add();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -893,8 +1092,10 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: Frame_range_n_rows here .
|
||||
DBUG_ASSERT(0);
|
||||
if (is_top_bound)
|
||||
return new Frame_range_n_top(is_preceding, bound->offset);
|
||||
else
|
||||
return new Frame_range_n_bottom(is_preceding, bound->offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user