ROWS OFFSET frame end

This commit is contained in:
Alexander Kuzmenkov 2021-02-04 10:41:09 +03:00
parent 4abbbae583
commit c1c71fc8e9
5 changed files with 181 additions and 7 deletions

View File

@ -109,6 +109,21 @@ void WindowFrame::checkValid() const
return;
}
if (end_type == BoundaryType::Offset
&& begin_type == BoundaryType::Offset)
{
if (type == FrameType::Rows)
{
if (end_offset >= begin_offset)
{
return;
}
}
// For RANGE and GROUPS, we must check that end follows begin if sorted
// according to ORDER BY (we don't support them yet).
}
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Window frame '{}' is invalid",
toString());

View File

@ -271,16 +271,22 @@ void WindowTransform::advanceFrameStartRowsOffset()
return;
}
assert(frame_start <= partition_end);
if (frame_start == partition_end && partition_ended)
if (partition_end <= frame_start)
{
// A FOLLOWING frame start ran into the end of partition.
frame_started = true;
frame_start = partition_end;
frame_started = partition_ended;
return;
}
// Handled the equality case above. Now the frame start is inside the
// partition, if we walked all the offset, it's final.
assert(partition_start < frame_start);
frame_started = offset_left == 0;
// If we ran into the start of data (offset left is negative), we won't be
// able to make progress. Should have handled this case above.
assert(offset_left >= 0);
}
void WindowTransform::advanceFrameStartChoose()
@ -463,6 +469,39 @@ void WindowTransform::advanceFrameEndUnbounded()
frame_ended = partition_ended;
}
void WindowTransform::advanceFrameEndRowsOffset()
{
// Walk the specified offset from the current row. The "+1" is needed
// because the frame_end is a past-the-end pointer.
const auto [moved_row, offset_left] = moveRowNumber(current_row,
window_description.frame.end_offset + 1);
if (partition_end <= moved_row)
{
// Clamp to the end of partition. It might not have ended yet, in which
// case wait for more data.
frame_end = partition_end;
frame_ended = partition_ended;
return;
}
if (moved_row <= partition_start)
{
// Clamp to the start of partition.
frame_end = partition_start;
frame_ended = true;
return;
}
// Frame end inside partition, if we walked all the offset, it's final.
frame_end = moved_row;
frame_ended = offset_left == 0;
// If we ran into the start of data (offset left is negative), we won't be
// able to make progress. Should have handled this case above.
assert(offset_left >= 0);
}
void WindowTransform::advanceFrameEnd()
{
// No reason for this function to be called again after it succeeded.
@ -479,10 +518,18 @@ void WindowTransform::advanceFrameEnd()
advanceFrameEndUnbounded();
break;
case WindowFrame::BoundaryType::Offset:
switch (window_description.frame.type)
{
case WindowFrame::FrameType::Rows:
advanceFrameEndRowsOffset();
break;
default:
throw Exception(ErrorCodes::NOT_IMPLEMENTED,
"The frame end type '{}' is not implemented",
WindowFrame::toString(window_description.frame.end_type));
}
break;
}
// fmt::print(stderr, "frame_end {} -> {}\n", frame_end_before, frame_end);

View File

@ -109,9 +109,10 @@ private:
void advanceFrameStart();
void advanceFrameStartChoose();
void advanceFrameStartRowsOffset();
void advanceFrameEnd();
void advanceFrameEndCurrentRow();
void advanceFrameEndUnbounded();
void advanceFrameEndRowsOffset();
void advanceFrameEnd();
bool arePeers(const RowNumber & x, const RowNumber & y) const;
void updateAggregationState();
void writeOutCurrentRow();

View File

@ -558,11 +558,106 @@ settings max_block_size = 2;
28 5 3 2 1
29 5 2 1 0
30 6 1 1 0
-- ROWS offset frame start and end
select number, p,
count(*) over (partition by p order by number
rows between 2 preceding and 2 following)
from (select number, intDiv(number, 7) p from numbers(71))
order by p, number
settings max_block_size = 2;
0 0 3
1 0 4
2 0 5
3 0 5
4 0 5
5 0 4
6 0 3
7 1 3
8 1 4
9 1 5
10 1 5
11 1 5
12 1 4
13 1 3
14 2 3
15 2 4
16 2 5
17 2 5
18 2 5
19 2 4
20 2 3
21 3 3
22 3 4
23 3 5
24 3 5
25 3 5
26 3 4
27 3 3
28 4 3
29 4 4
30 4 5
31 4 5
32 4 5
33 4 4
34 4 3
35 5 3
36 5 4
37 5 5
38 5 5
39 5 5
40 5 4
41 5 3
42 6 3
43 6 4
44 6 5
45 6 5
46 6 5
47 6 4
48 6 3
49 7 3
50 7 4
51 7 5
52 7 5
53 7 5
54 7 4
55 7 3
56 8 3
57 8 4
58 8 5
59 8 5
60 8 5
61 8 4
62 8 3
63 9 3
64 9 4
65 9 5
66 9 5
67 9 5
68 9 4
69 9 3
70 10 1
SELECT count(*) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) FROM numbers(4);
1
2
3
3
-- frame boundaries that runs into the partition end
select
count() over (partition by intDiv(number, 3)
rows between 100 following and unbounded following),
count() over (partition by intDiv(number, 3)
rows between current row and 100 following)
from numbers(10);
0 3
0 2
0 1
0 3
0 2
0 1
0 3
0 2
0 1
0 1
-- seen a use-after-free under MSan in this query once
SELECT number, max(number) OVER (PARTITION BY intDiv(number, 7) ORDER BY number ASC NULLS LAST ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM numbers(1024) SETTINGS max_block_size = 2 FORMAT Null;
-- a corner case

View File

@ -175,8 +175,24 @@ from (select number, intDiv(number, 5) p from numbers(31))
order by p, number
settings max_block_size = 2;
-- ROWS offset frame start and end
select number, p,
count(*) over (partition by p order by number
rows between 2 preceding and 2 following)
from (select number, intDiv(number, 7) p from numbers(71))
order by p, number
settings max_block_size = 2;
SELECT count(*) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) FROM numbers(4);
-- frame boundaries that runs into the partition end
select
count() over (partition by intDiv(number, 3)
rows between 100 following and unbounded following),
count() over (partition by intDiv(number, 3)
rows between current row and 100 following)
from numbers(10);
-- seen a use-after-free under MSan in this query once
SELECT number, max(number) OVER (PARTITION BY intDiv(number, 7) ORDER BY number ASC NULLS LAST ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM numbers(1024) SETTINGS max_block_size = 2 FORMAT Null;