Skip to content

Commit a81e711

Browse files
committed
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.
1 parent d29e147 commit a81e711

File tree

3 files changed

+123
-47
lines changed

3 files changed

+123
-47
lines changed

mysql-test/r/win.result

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,3 +1940,13 @@ NULL NULL 1 0.1666666667
19401940
2 b 4 0.6666666667
19411941
-1 2 1.0000000000
19421942
drop table t1;
1943+
#
1944+
# MDEV-9925: Wrong result with aggregate function as a window function
1945+
#
1946+
create table t1 (i int);
1947+
insert into t1 values (1),(2);
1948+
select i, sum(i) over (partition by i) from t1;
1949+
i sum(i) over (partition by i)
1950+
1 1
1951+
2 2
1952+
drop table t1;

mysql-test/t/win.test

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,3 +1183,13 @@ select
11831183
from t1;
11841184

11851185
drop table t1;
1186+
1187+
1188+
--echo #
1189+
--echo # MDEV-9925: Wrong result with aggregate function as a window function
1190+
--echo #
1191+
create table t1 (i int);
1192+
insert into t1 values (1),(2);
1193+
select i, sum(i) over (partition by i) from t1;
1194+
drop table t1;
1195+

sql/sql_window.cc

Lines changed: 103 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -668,11 +668,68 @@ class Table_read_cursor : public Rowid_seq_cursor
668668
// todo: should move_to() also read row here?
669669
};
670670

671+
671672
/*
672-
TODO: We should also have a cursor that reads table rows and
673-
stays within the current partition.
673+
A cursor which only moves within a partition. The scan stops at the partition
674+
end, and it needs an explicit command to move to the next partition.
674675
*/
675676

677+
class Partition_read_cursor
678+
{
679+
Table_read_cursor tbl_cursor;
680+
Group_bound_tracker bound_tracker;
681+
bool end_of_partition;
682+
public:
683+
void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list)
684+
{
685+
tbl_cursor.init(info);
686+
bound_tracker.init(thd, partition_list);
687+
end_of_partition= false;
688+
}
689+
690+
/*
691+
Informs the cursor that we need to move into the next partition.
692+
The next partition is provided in two ways:
693+
- in table->record[0]..
694+
- rownum parameter has the row number.
695+
*/
696+
void on_next_partition(int rownum)
697+
{
698+
/* Remember the sort key value from the new partition */
699+
bound_tracker.check_if_next_group();
700+
end_of_partition= false;
701+
}
702+
703+
/*
704+
Moves to a new row. The row is assumed to be within the current partition
705+
*/
706+
void move_to(int rownum) { tbl_cursor.move_to(rownum); }
707+
708+
/*
709+
This returns -1 when end of partition was reached.
710+
*/
711+
int get_next()
712+
{
713+
int res;
714+
if (end_of_partition)
715+
return -1;
716+
if ((res= tbl_cursor.get_next()))
717+
return res;
718+
719+
if (bound_tracker.compare_with_cache())
720+
{
721+
end_of_partition= true;
722+
return -1;
723+
}
724+
return 0;
725+
}
726+
727+
bool restore_last_row()
728+
{
729+
return tbl_cursor.restore_last_row();
730+
}
731+
};
732+
676733
/////////////////////////////////////////////////////////////////////////////
677734

678735

@@ -686,6 +743,27 @@ class Table_read_cursor : public Rowid_seq_cursor
686743
The cursor also assumes that the current row moves forward through the
687744
partition and will move to the next adjacent partition after this one.
688745
746+
List of all cursor classes:
747+
Frame_cursor
748+
Frame_range_n_top
749+
Frame_range_n_bottom
750+
751+
Frame_range_current_row_top
752+
Frame_range_current_row_bottom
753+
754+
Frame_n_rows_preceding
755+
Frame_n_rows_following
756+
757+
Frame_rows_current_row_top = Frame_n_rows_preceding(0)
758+
Frame_rows_current_row_bottom
759+
760+
// These handle both RANGE and ROWS-type bounds
761+
Frame_unbounded_preceding
762+
Frame_unbounded_following
763+
764+
// This is not used as a frame bound, it counts rows in the partition:
765+
Frame_unbounded_following_set_count : public Frame_unbounded_following
766+
689767
@todo
690768
- if we want to allocate this on the MEM_ROOT we should make sure
691769
it is not re-allocated for every subquery execution.
@@ -852,7 +930,7 @@ class Frame_range_n_top : public Frame_cursor
852930

853931
class Frame_range_n_bottom: public Frame_cursor
854932
{
855-
Table_read_cursor cursor;
933+
Partition_read_cursor cursor;
856934

857935
Cached_item_item *range_expr;
858936

@@ -861,7 +939,6 @@ class Frame_range_n_bottom: public Frame_cursor
861939

862940
const bool is_preceding;
863941

864-
Group_bound_tracker bound_tracker;
865942
bool end_of_partition;
866943

867944
/*
@@ -878,7 +955,7 @@ class Frame_range_n_bottom: public Frame_cursor
878955
SQL_I_List<ORDER> *partition_list,
879956
SQL_I_List<ORDER> *order_list)
880957
{
881-
cursor.init(info);
958+
cursor.init(thd, info, partition_list);
882959

883960
DBUG_ASSERT(order_list->elements == 1);
884961
Item *src_expr= order_list->first->item[0];
@@ -900,16 +977,14 @@ class Frame_range_n_bottom: public Frame_cursor
900977
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
901978

902979
item_add->fix_fields(thd, &item_add);
903-
904-
bound_tracker.init(thd, partition_list);
905980
}
906981

907982
void pre_next_partition(longlong rownum, Item_sum* item)
908983
{
909984
// Save the value of FUNC(current_row)
910985
range_expr->fetch_value_from(item_add);
911986

912-
bound_tracker.check_if_next_group();
987+
cursor.on_next_partition(rownum);
913988
end_of_partition= false;
914989
}
915990

@@ -950,11 +1025,6 @@ class Frame_range_n_bottom: public Frame_cursor
9501025
int res;
9511026
while (!(res= cursor.get_next()))
9521027
{
953-
if (bound_tracker.check_if_next_group())
954-
{
955-
end_of_partition= true;
956-
break;
957-
}
9581028
if (order_direction * range_expr->cmp_read_only() < 0)
9591029
break;
9601030
item->add();
@@ -981,7 +1051,8 @@ class Frame_range_n_bottom: public Frame_cursor
9811051

9821052
class Frame_range_current_row_bottom: public Frame_cursor
9831053
{
984-
Table_read_cursor cursor;
1054+
Partition_read_cursor cursor;
1055+
9851056
Group_bound_tracker peer_tracker;
9861057

9871058
bool dont_move;
@@ -990,14 +1061,15 @@ class Frame_range_current_row_bottom: public Frame_cursor
9901061
SQL_I_List<ORDER> *partition_list,
9911062
SQL_I_List<ORDER> *order_list)
9921063
{
993-
cursor.init(info);
1064+
cursor.init(thd, info, partition_list);
9941065
peer_tracker.init(thd, order_list);
9951066
}
9961067

9971068
void pre_next_partition(longlong rownum, Item_sum* item)
9981069
{
9991070
// Save the value of the current_row
10001071
peer_tracker.check_if_next_group();
1072+
cursor.on_next_partition(rownum);
10011073
if (rownum != 0)
10021074
{
10031075
// Add the current row now because our cursor has already seen it
@@ -1160,17 +1232,19 @@ class Frame_unbounded_preceding : public Frame_cursor
11601232

11611233
class Frame_unbounded_following : public Frame_cursor
11621234
{
1163-
11641235
protected:
1165-
Table_read_cursor cursor;
1166-
Group_bound_tracker bound_tracker;
1236+
Partition_read_cursor cursor;
11671237

11681238
public:
11691239
void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
11701240
SQL_I_List<ORDER> *order_list)
11711241
{
1172-
cursor.init(info);
1173-
bound_tracker.init(thd, partition_list);
1242+
cursor.init(thd, info, partition_list);
1243+
}
1244+
1245+
void pre_next_partition(longlong rownum, Item_sum* item)
1246+
{
1247+
cursor.on_next_partition(rownum);
11741248
}
11751249

11761250
void next_partition(longlong rownum, Item_sum* item)
@@ -1181,15 +1255,11 @@ class Frame_unbounded_following : public Frame_cursor
11811255
if (cursor.get_next())
11821256
return;
11831257
}
1184-
/* Remember which partition we are in */
1185-
bound_tracker.check_if_next_group();
11861258
item->add();
11871259

11881260
/* Walk to the end of the partition, updating the SUM function */
11891261
while (!cursor.get_next())
11901262
{
1191-
if (bound_tracker.check_if_next_group())
1192-
break;
11931263
item->add();
11941264
}
11951265
}
@@ -1203,6 +1273,9 @@ class Frame_unbounded_following : public Frame_cursor
12031273

12041274
class Frame_unbounded_following_set_count : public Frame_unbounded_following
12051275
{
1276+
public:
1277+
// pre_next_partition is inherited
1278+
12061279
void next_partition(longlong rownum, Item_sum* item)
12071280
{
12081281
ulonglong num_rows_in_partition= 0;
@@ -1214,13 +1287,9 @@ class Frame_unbounded_following_set_count : public Frame_unbounded_following
12141287
}
12151288
num_rows_in_partition++;
12161289

1217-
/* Remember which partition we are in */
1218-
bound_tracker.check_if_next_group();
12191290
/* Walk to the end of the partition, find how many rows there are. */
12201291
while (!cursor.get_next())
12211292
{
1222-
if (bound_tracker.check_if_next_group())
1223-
break;
12241293
num_rows_in_partition++;
12251294
}
12261295

@@ -1368,14 +1437,8 @@ class Frame_n_rows_following : public Frame_cursor
13681437
const bool is_top_bound;
13691438
const ha_rows n_rows;
13701439

1371-
Table_read_cursor cursor;
1440+
Partition_read_cursor cursor;
13721441
bool at_partition_end;
1373-
1374-
/*
1375-
This cursor reaches partition end before the main cursor has reached it.
1376-
bound_tracker is used to detect partition end.
1377-
*/
1378-
Group_bound_tracker bound_tracker;
13791442
public:
13801443
Frame_n_rows_following(bool is_top_bound_arg, ha_rows n_rows_arg) :
13811444
is_top_bound(is_top_bound_arg), n_rows(n_rows_arg)
@@ -1386,17 +1449,15 @@ class Frame_n_rows_following : public Frame_cursor
13861449
void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
13871450
SQL_I_List<ORDER> *order_list)
13881451
{
1389-
cursor.init(info);
1452+
cursor.init(thd, info, partition_list);
13901453
at_partition_end= false;
1391-
bound_tracker.init(thd, partition_list);
13921454
}
13931455

13941456
void pre_next_partition(longlong rownum, Item_sum* item)
13951457
{
13961458
at_partition_end= false;
13971459

1398-
// Fetch current partition value
1399-
bound_tracker.check_if_next_group();
1460+
cursor.on_next_partition(rownum);
14001461

14011462
if (rownum != 0)
14021463
{
@@ -1434,15 +1495,10 @@ class Frame_n_rows_following : public Frame_cursor
14341495
{
14351496
if (!cursor.get_next())
14361497
{
1437-
if (bound_tracker.check_if_next_group())
1438-
at_partition_end= true;
1498+
if (is_top_bound) // this is frame start endpoint
1499+
item->remove();
14391500
else
1440-
{
1441-
if (is_top_bound) // this is frame start endpoint
1442-
item->remove();
1443-
else
1444-
item->add();
1445-
}
1501+
item->add();
14461502
}
14471503
else
14481504
at_partition_end= true;

0 commit comments

Comments
 (0)