Commit a81e711a authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-9925: Wrong result with aggregate function as a window function

Make Frame_range_current_row_bottom to take into account partition bounds.

Other partition bounds that could potentially hit the end of partition are
Frame_range_n_bottom, Frame_n_rows_following, Frame_unbounded_following,
and they all had end-of-partition protection.

To simplify the code, factored out end-of-partition checks into
class Partition_read_cursor.
parent d29e1472
...@@ -1940,3 +1940,13 @@ NULL NULL 1 0.1666666667 ...@@ -1940,3 +1940,13 @@ NULL NULL 1 0.1666666667
2 b 4 0.6666666667 2 b 4 0.6666666667
-1 2 1.0000000000 -1 2 1.0000000000
drop table t1; drop table t1;
#
# MDEV-9925: Wrong result with aggregate function as a window function
#
create table t1 (i int);
insert into t1 values (1),(2);
select i, sum(i) over (partition by i) from t1;
i sum(i) over (partition by i)
1 1
2 2
drop table t1;
...@@ -1183,3 +1183,13 @@ select ...@@ -1183,3 +1183,13 @@ select
from t1; from t1;
drop table t1; drop table t1;
--echo #
--echo # MDEV-9925: Wrong result with aggregate function as a window function
--echo #
create table t1 (i int);
insert into t1 values (1),(2);
select i, sum(i) over (partition by i) from t1;
drop table t1;
...@@ -668,11 +668,68 @@ class Table_read_cursor : public Rowid_seq_cursor ...@@ -668,11 +668,68 @@ class Table_read_cursor : public Rowid_seq_cursor
// todo: should move_to() also read row here? // todo: should move_to() also read row here?
}; };
/* /*
TODO: We should also have a cursor that reads table rows and A cursor which only moves within a partition. The scan stops at the partition
stays within the current partition. end, and it needs an explicit command to move to the next partition.
*/ */
class Partition_read_cursor
{
Table_read_cursor tbl_cursor;
Group_bound_tracker bound_tracker;
bool end_of_partition;
public:
void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list)
{
tbl_cursor.init(info);
bound_tracker.init(thd, partition_list);
end_of_partition= false;
}
/*
Informs the cursor that we need to move into the next partition.
The next partition is provided in two ways:
- in table->record[0]..
- rownum parameter has the row number.
*/
void on_next_partition(int rownum)
{
/* Remember the sort key value from the new partition */
bound_tracker.check_if_next_group();
end_of_partition= false;
}
/*
Moves to a new row. The row is assumed to be within the current partition
*/
void move_to(int rownum) { tbl_cursor.move_to(rownum); }
/*
This returns -1 when end of partition was reached.
*/
int get_next()
{
int res;
if (end_of_partition)
return -1;
if ((res= tbl_cursor.get_next()))
return res;
if (bound_tracker.compare_with_cache())
{
end_of_partition= true;
return -1;
}
return 0;
}
bool restore_last_row()
{
return tbl_cursor.restore_last_row();
}
};
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
...@@ -686,6 +743,27 @@ class Table_read_cursor : public Rowid_seq_cursor ...@@ -686,6 +743,27 @@ class Table_read_cursor : public Rowid_seq_cursor
The cursor also assumes that the current row moves forward through the The cursor also assumes that the current row moves forward through the
partition and will move to the next adjacent partition after this one. partition and will move to the next adjacent partition after this one.
List of all cursor classes:
Frame_cursor
Frame_range_n_top
Frame_range_n_bottom
Frame_range_current_row_top
Frame_range_current_row_bottom
Frame_n_rows_preceding
Frame_n_rows_following
Frame_rows_current_row_top = Frame_n_rows_preceding(0)
Frame_rows_current_row_bottom
// These handle both RANGE and ROWS-type bounds
Frame_unbounded_preceding
Frame_unbounded_following
// This is not used as a frame bound, it counts rows in the partition:
Frame_unbounded_following_set_count : public Frame_unbounded_following
@todo @todo
- if we want to allocate this on the MEM_ROOT we should make sure - if we want to allocate this on the MEM_ROOT we should make sure
it is not re-allocated for every subquery execution. it is not re-allocated for every subquery execution.
...@@ -852,7 +930,7 @@ class Frame_range_n_top : public Frame_cursor ...@@ -852,7 +930,7 @@ class Frame_range_n_top : public Frame_cursor
class Frame_range_n_bottom: public Frame_cursor class Frame_range_n_bottom: public Frame_cursor
{ {
Table_read_cursor cursor; Partition_read_cursor cursor;
Cached_item_item *range_expr; Cached_item_item *range_expr;
...@@ -861,7 +939,6 @@ class Frame_range_n_bottom: public Frame_cursor ...@@ -861,7 +939,6 @@ class Frame_range_n_bottom: public Frame_cursor
const bool is_preceding; const bool is_preceding;
Group_bound_tracker bound_tracker;
bool end_of_partition; bool end_of_partition;
/* /*
...@@ -878,7 +955,7 @@ class Frame_range_n_bottom: public Frame_cursor ...@@ -878,7 +955,7 @@ class Frame_range_n_bottom: public Frame_cursor
SQL_I_List<ORDER> *partition_list, SQL_I_List<ORDER> *partition_list,
SQL_I_List<ORDER> *order_list) SQL_I_List<ORDER> *order_list)
{ {
cursor.init(info); cursor.init(thd, info, partition_list);
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];
...@@ -900,8 +977,6 @@ class Frame_range_n_bottom: public Frame_cursor ...@@ -900,8 +977,6 @@ class Frame_range_n_bottom: public Frame_cursor
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);
item_add->fix_fields(thd, &item_add); item_add->fix_fields(thd, &item_add);
bound_tracker.init(thd, partition_list);
} }
void pre_next_partition(longlong rownum, Item_sum* item) void pre_next_partition(longlong rownum, Item_sum* item)
...@@ -909,7 +984,7 @@ class Frame_range_n_bottom: public Frame_cursor ...@@ -909,7 +984,7 @@ class Frame_range_n_bottom: public Frame_cursor
// Save the value of FUNC(current_row) // Save the value of FUNC(current_row)
range_expr->fetch_value_from(item_add); range_expr->fetch_value_from(item_add);
bound_tracker.check_if_next_group(); cursor.on_next_partition(rownum);
end_of_partition= false; end_of_partition= false;
} }
...@@ -950,11 +1025,6 @@ class Frame_range_n_bottom: public Frame_cursor ...@@ -950,11 +1025,6 @@ class Frame_range_n_bottom: public Frame_cursor
int res; int res;
while (!(res= cursor.get_next())) while (!(res= cursor.get_next()))
{ {
if (bound_tracker.check_if_next_group())
{
end_of_partition= true;
break;
}
if (order_direction * range_expr->cmp_read_only() < 0) if (order_direction * range_expr->cmp_read_only() < 0)
break; break;
item->add(); item->add();
...@@ -981,7 +1051,8 @@ class Frame_range_n_bottom: public Frame_cursor ...@@ -981,7 +1051,8 @@ class Frame_range_n_bottom: public Frame_cursor
class Frame_range_current_row_bottom: public Frame_cursor class Frame_range_current_row_bottom: public Frame_cursor
{ {
Table_read_cursor cursor; Partition_read_cursor cursor;
Group_bound_tracker peer_tracker; Group_bound_tracker peer_tracker;
bool dont_move; bool dont_move;
...@@ -990,7 +1061,7 @@ class Frame_range_current_row_bottom: public Frame_cursor ...@@ -990,7 +1061,7 @@ class Frame_range_current_row_bottom: public Frame_cursor
SQL_I_List<ORDER> *partition_list, SQL_I_List<ORDER> *partition_list,
SQL_I_List<ORDER> *order_list) SQL_I_List<ORDER> *order_list)
{ {
cursor.init(info); cursor.init(thd, info, partition_list);
peer_tracker.init(thd, order_list); peer_tracker.init(thd, order_list);
} }
...@@ -998,6 +1069,7 @@ class Frame_range_current_row_bottom: public Frame_cursor ...@@ -998,6 +1069,7 @@ class Frame_range_current_row_bottom: public Frame_cursor
{ {
// Save the value of the current_row // Save the value of the current_row
peer_tracker.check_if_next_group(); peer_tracker.check_if_next_group();
cursor.on_next_partition(rownum);
if (rownum != 0) if (rownum != 0)
{ {
// Add the current row now because our cursor has already seen it // Add the current row now because our cursor has already seen it
...@@ -1160,17 +1232,19 @@ class Frame_unbounded_preceding : public Frame_cursor ...@@ -1160,17 +1232,19 @@ class Frame_unbounded_preceding : public Frame_cursor
class Frame_unbounded_following : public Frame_cursor class Frame_unbounded_following : public Frame_cursor
{ {
protected: protected:
Table_read_cursor cursor; Partition_read_cursor cursor;
Group_bound_tracker bound_tracker;
public: public:
void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list, void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
SQL_I_List<ORDER> *order_list) SQL_I_List<ORDER> *order_list)
{ {
cursor.init(info); cursor.init(thd, info, partition_list);
bound_tracker.init(thd, partition_list); }
void pre_next_partition(longlong rownum, Item_sum* item)
{
cursor.on_next_partition(rownum);
} }
void next_partition(longlong rownum, Item_sum* item) void next_partition(longlong rownum, Item_sum* item)
...@@ -1181,15 +1255,11 @@ class Frame_unbounded_following : public Frame_cursor ...@@ -1181,15 +1255,11 @@ class Frame_unbounded_following : public Frame_cursor
if (cursor.get_next()) if (cursor.get_next())
return; return;
} }
/* Remember which partition we are in */
bound_tracker.check_if_next_group();
item->add(); item->add();
/* Walk to the end of the partition, updating the SUM function */ /* Walk to the end of the partition, updating the SUM function */
while (!cursor.get_next()) while (!cursor.get_next())
{ {
if (bound_tracker.check_if_next_group())
break;
item->add(); item->add();
} }
} }
...@@ -1203,6 +1273,9 @@ class Frame_unbounded_following : public Frame_cursor ...@@ -1203,6 +1273,9 @@ class Frame_unbounded_following : public Frame_cursor
class Frame_unbounded_following_set_count : public Frame_unbounded_following class Frame_unbounded_following_set_count : public Frame_unbounded_following
{ {
public:
// pre_next_partition is inherited
void next_partition(longlong rownum, Item_sum* item) void next_partition(longlong rownum, Item_sum* item)
{ {
ulonglong num_rows_in_partition= 0; ulonglong num_rows_in_partition= 0;
...@@ -1214,13 +1287,9 @@ class Frame_unbounded_following_set_count : public Frame_unbounded_following ...@@ -1214,13 +1287,9 @@ class Frame_unbounded_following_set_count : public Frame_unbounded_following
} }
num_rows_in_partition++; num_rows_in_partition++;
/* Remember which partition we are in */
bound_tracker.check_if_next_group();
/* Walk to the end of the partition, find how many rows there are. */ /* Walk to the end of the partition, find how many rows there are. */
while (!cursor.get_next()) while (!cursor.get_next())
{ {
if (bound_tracker.check_if_next_group())
break;
num_rows_in_partition++; num_rows_in_partition++;
} }
...@@ -1368,14 +1437,8 @@ class Frame_n_rows_following : public Frame_cursor ...@@ -1368,14 +1437,8 @@ class Frame_n_rows_following : public Frame_cursor
const bool is_top_bound; const bool is_top_bound;
const ha_rows n_rows; const ha_rows n_rows;
Table_read_cursor cursor; Partition_read_cursor cursor;
bool at_partition_end; bool at_partition_end;
/*
This cursor reaches partition end before the main cursor has reached it.
bound_tracker is used to detect partition end.
*/
Group_bound_tracker bound_tracker;
public: public:
Frame_n_rows_following(bool is_top_bound_arg, ha_rows n_rows_arg) : Frame_n_rows_following(bool is_top_bound_arg, ha_rows n_rows_arg) :
is_top_bound(is_top_bound_arg), n_rows(n_rows_arg) is_top_bound(is_top_bound_arg), n_rows(n_rows_arg)
...@@ -1386,17 +1449,15 @@ class Frame_n_rows_following : public Frame_cursor ...@@ -1386,17 +1449,15 @@ class Frame_n_rows_following : public Frame_cursor
void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list, void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
SQL_I_List<ORDER> *order_list) SQL_I_List<ORDER> *order_list)
{ {
cursor.init(info); cursor.init(thd, info, partition_list);
at_partition_end= false; at_partition_end= false;
bound_tracker.init(thd, partition_list);
} }
void pre_next_partition(longlong rownum, Item_sum* item) void pre_next_partition(longlong rownum, Item_sum* item)
{ {
at_partition_end= false; at_partition_end= false;
// Fetch current partition value cursor.on_next_partition(rownum);
bound_tracker.check_if_next_group();
if (rownum != 0) if (rownum != 0)
{ {
...@@ -1434,15 +1495,10 @@ class Frame_n_rows_following : public Frame_cursor ...@@ -1434,15 +1495,10 @@ class Frame_n_rows_following : public Frame_cursor
{ {
if (!cursor.get_next()) if (!cursor.get_next())
{ {
if (bound_tracker.check_if_next_group()) if (is_top_bound) // this is frame start endpoint
at_partition_end= true; item->remove();
else else
{ item->add();
if (is_top_bound) // this is frame start endpoint
item->remove();
else
item->add();
}
} }
else else
at_partition_end= true; at_partition_end= true;
......
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