Skip to content

Commit 671c16d

Browse files
author
anusudarsan
committed
Implement RANGE bounded FOLLOWING window frame
1 parent 5ec243c commit 671c16d

File tree

8 files changed

+212
-33
lines changed

8 files changed

+212
-33
lines changed

presto-main/src/main/java/com/facebook/presto/operator/window/WindowPartition.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ private Range getFrameRange(FrameInfo frameInfo)
160160
return new Range(-1, -1);
161161
}
162162

163-
if (frameInfo.getType() == RANGE && emptyFrame(frameInfo, peerGroupStart, peerGroupEnd - 1)) {
163+
if (frameInfo.getType() == RANGE && emptyFrame(frameInfo, peerGroupStart, endPosition - (peerGroupEnd - 1))) {
164164
return new Range(-1, -1);
165165
}
166166

@@ -174,6 +174,9 @@ private Range getFrameRange(FrameInfo frameInfo)
174174
else if (frameInfo.getType() == RANGE && frameInfo.getStartType() == PRECEDING) {
175175
frameStart = precedingStartRange(getStartValue(frameInfo));
176176
}
177+
else if (frameInfo.getType() == RANGE && frameInfo.getStartType() == FOLLOWING) {
178+
frameStart = followingRange(rowPosition, endPosition, getStartValue(frameInfo));
179+
}
177180
else if (frameInfo.getStartType() == PRECEDING) {
178181
frameStart = preceding(rowPosition, getStartValue(frameInfo));
179182
}
@@ -194,6 +197,9 @@ else if (frameInfo.getType() == RANGE) {
194197
else if (frameInfo.getType() == RANGE && frameInfo.getEndType() == PRECEDING) {
195198
frameEnd = precedingEndRange(getEndValue(frameInfo));
196199
}
200+
else if (frameInfo.getType() == RANGE && frameInfo.getEndType() == FOLLOWING) {
201+
frameEnd = followingRange(peerGroupEnd, endPosition + 1, getEndValue(frameInfo)) - 1;
202+
}
197203
else if (frameInfo.getEndType() == PRECEDING) {
198204
frameEnd = preceding(rowPosition, getEndValue(frameInfo));
199205
}
@@ -228,6 +234,33 @@ private int precedingStartRange(long startValue)
228234
return peerGroupStartIndices.get(toIntExact(peerGroupStartIndex - startValue));
229235
}
230236

237+
private int followingRange(int followingPeerGroupStart, int endPosition, long value)
238+
{
239+
if (value == 0) {
240+
return followingPeerGroupStart;
241+
}
242+
// TODO: Optimize this to *not* look for peers often, probably have pageIndex keep the peer groups
243+
int followingPeerGroupEnd = 0;
244+
int currentValue = 0;
245+
while (currentValue < value) {
246+
boolean peerFound = false;
247+
followingPeerGroupEnd = followingPeerGroupStart + 1;
248+
while ((followingPeerGroupEnd < partitionEnd) && pagesIndex.positionEqualsPosition(peerGroupHashStrategy, followingPeerGroupStart, followingPeerGroupEnd)) {
249+
followingPeerGroupEnd++;
250+
peerFound = true;
251+
}
252+
if (followingPeerGroupEnd >= partitionEnd) {
253+
return endPosition;
254+
}
255+
256+
if (!peerFound) {
257+
currentValue++;
258+
}
259+
followingPeerGroupStart++;
260+
}
261+
return followingPeerGroupEnd;
262+
}
263+
231264
private boolean emptyFrame(FrameInfo frameInfo, int rowPosition, int position)
232265
{
233266
FrameBound.Type startType = frameInfo.getStartType();

presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@
187187
import static com.facebook.presto.sql.tree.FrameBound.Type.PRECEDING;
188188
import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_FOLLOWING;
189189
import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_PRECEDING;
190-
import static com.facebook.presto.sql.tree.WindowFrame.Type.RANGE;
191190
import static com.facebook.presto.type.UnknownType.UNKNOWN;
192191
import static com.google.common.base.Preconditions.checkArgument;
193192
import static com.google.common.base.Preconditions.checkState;
@@ -1260,9 +1259,6 @@ private void analyzeWindowFrame(WindowFrame frame)
12601259
if ((startType == FOLLOWING) && (endType == CURRENT_ROW)) {
12611260
throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame starting from FOLLOWING cannot end with CURRENT ROW");
12621261
}
1263-
if ((frame.getType() == RANGE) && ((startType == FOLLOWING) || (endType == FOLLOWING))) {
1264-
throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame RANGE FOLLOWING is only supported with UNBOUNDED");
1265-
}
12661262
}
12671263

12681264
private void analyzeHaving(QuerySpecification node, Scope scope)

presto-main/src/test/java/com/facebook/presto/operator/window/TestAggregateWindowFunction.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,4 +641,128 @@ public void testSumRangePrecedingBounded()
641641
.row(null, null, null)
642642
.build());
643643
}
644+
645+
@Test
646+
public void testSumRangeFollowingBounded()
647+
{
648+
assertWindowQueryWithNulls("sum(orderkey) OVER (ORDER BY orderstatus " +
649+
"RANGE BETWEEN current row AND 1 FOLLOWING)",
650+
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT)
651+
.row(3L, "F", 48L)
652+
.row(5L, "F", 48L)
653+
.row(6L, "F", 48L)
654+
.row(null, "F", 48L)
655+
.row(34L, "O", 42L)
656+
.row(null, "O", 42L)
657+
.row(1L, null, 8L)
658+
.row(7L, null, 8L)
659+
.row(null, null, 8L)
660+
.row(null, null, 8L)
661+
.build());
662+
663+
assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
664+
"RANGE BETWEEN current row AND 1 FOLLOWING)",
665+
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT)
666+
.row(3L, "F", 8L)
667+
.row(5L, "F", 11L)
668+
.row(6L, "F", 6L)
669+
.row(null, "F", null)
670+
.row(34L, "O", 34L)
671+
.row(null, "O", null)
672+
.row(1L, null, 8L)
673+
.row(7L, null, 7L)
674+
.row(null, null, null)
675+
.row(null, null, null)
676+
.build());
677+
678+
assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
679+
"RANGE BETWEEN current row AND 0 FOLLOWING)",
680+
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT)
681+
.row(3L, "F", 3L)
682+
.row(5L, "F", 5L)
683+
.row(6L, "F", 6L)
684+
.row(null, "F", null)
685+
.row(34L, "O", 34L)
686+
.row(null, "O", null)
687+
.row(1L, null, 1L)
688+
.row(7L, null, 7L)
689+
.row(null, null, null)
690+
.row(null, null, null)
691+
.build());
692+
693+
assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
694+
"RANGE BETWEEN 1 FOLLOWING AND 2 FOLLOWING)",
695+
resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT)
696+
.row(3L, "F", 11L)
697+
.row(5L, "F", 6L)
698+
.row(6L, "F", null)
699+
.row(null, "F", null)
700+
.row(34L, "O", null)
701+
.row(null, "O", null)
702+
.row(1L, null, 7L)
703+
.row(7L, null, null)
704+
.row(null, null, null)
705+
.row(null, null, null)
706+
.build());
707+
708+
assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
709+
"RANGE BETWEEN 0 FOLLOWING AND UNBOUNDED FOLLOWING)",
710+
resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT)
711+
.row(3L, "F", 14L)
712+
.row(5L, "F", 11L)
713+
.row(6L, "F", 6L)
714+
.row(null, "F", null)
715+
.row(34L, "O", 34L)
716+
.row(null, "O", null)
717+
.row(1L, null, 8L)
718+
.row(7L, null, 7L)
719+
.row(null, null, null)
720+
.row(null, null, null)
721+
.build());
722+
723+
assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
724+
"RANGE BETWEEN 2 FOLLOWING AND UNBOUNDED FOLLOWING)",
725+
resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT)
726+
.row(3L, "F", 6L)
727+
.row(5L, "F", null)
728+
.row(6L, "F", null)
729+
.row(null, "F", null)
730+
.row(34L, "O", null)
731+
.row(null, "O", null)
732+
.row(1L, null, null)
733+
.row(7L, null, null)
734+
.row(null, null, null)
735+
.row(null, null, null)
736+
.build());
737+
738+
assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
739+
"RANGE BETWEEN 4 FOLLOWING AND UNBOUNDED FOLLOWING)",
740+
resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT)
741+
.row(3L, "F", null)
742+
.row(5L, "F", null)
743+
.row(6L, "F", null)
744+
.row(null, "F", null)
745+
.row(34L, "O", null)
746+
.row(null, "O", null)
747+
.row(1L, null, null)
748+
.row(7L, null, null)
749+
.row(null, null, null)
750+
.row(null, null, null)
751+
.build());
752+
753+
assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
754+
"RANGE BETWEEN 3 FOLLOWING AND 2 FOLLOWING)",
755+
resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT)
756+
.row(3L, "F", null)
757+
.row(5L, "F", null)
758+
.row(6L, "F", null)
759+
.row(null, "F", null)
760+
.row(34L, "O", null)
761+
.row(null, "O", null)
762+
.row(1L, null, null)
763+
.row(7L, null, null)
764+
.row(null, null, null)
765+
.row(null, null, null)
766+
.build());
767+
}
644768
}

presto-main/src/test/java/com/facebook/presto/operator/window/TestFirstValueFunction.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,19 @@ public void testFirstValueBounded()
142142
.row(null, null, 1L)
143143
.row(null, null, 1L)
144144
.build());
145+
assertWindowQueryWithNulls("first_value(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
146+
"RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING)",
147+
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT)
148+
.row(3L, "F", 3L)
149+
.row(5L, "F", 3L)
150+
.row(6L, "F", 3L)
151+
.row(null, "F", 5L)
152+
.row(34L, "O", 34L)
153+
.row(null, "O", 34L)
154+
.row(1L, null, 1L)
155+
.row(7L, null, 1L)
156+
.row(null, null, 1L)
157+
.row(null, null, 1L)
158+
.build());
145159
}
146160
}

presto-main/src/test/java/com/facebook/presto/operator/window/TestLastValueFunction.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,19 @@ public void testLastValueBounded()
141141
.row(null, null, 7L)
142142
.row(null, null, 7L)
143143
.build());
144+
assertWindowQueryWithNulls("last_value(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
145+
"RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING)",
146+
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT)
147+
.row(3L, "F", 5L)
148+
.row(5L, "F", 6L)
149+
.row(6L, "F", null)
150+
.row(null, "F", null)
151+
.row(34L, "O", null)
152+
.row(null, "O", null)
153+
.row(1L, null, 7L)
154+
.row(7L, null, null)
155+
.row(null, null, null)
156+
.row(null, null, null)
157+
.build());
144158
}
145159
}

presto-main/src/test/java/com/facebook/presto/operator/window/TestMultipleWindowSpecifications.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -222,34 +222,34 @@ public void testDisjointWindowSpecifications()
222222
public void testMultipleWindowSpecificationsWithRange()
223223
{
224224
// Intersection previous to current row
225-
assertWindowQueryWithNulls("count(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 3 PRECEDING AND 2 PRECEDING), " +
225+
assertWindowQueryWithNulls("count(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 3 PRECEDING AND 1 FOLLOWING), " +
226226
"sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)",
227227
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT, BIGINT)
228-
.row(3L, "F", 0L, 3L)
229-
.row(5L, "F", 0L, 8L)
230-
.row(6L, "F", 1L, 14L)
231-
.row(null, "F", 2L, 11L)
232-
.row(34L, "O", 0L, 34L)
233-
.row(null, "O", 0L, 34L)
234-
.row(1L, null, 0L, 1L)
235-
.row(7L, null, 0L, 8L)
236-
.row(null, null, 1L, 8L)
237-
.row(null, null, 1L, 8L)
228+
.row(3L, "F", 2L, 3L)
229+
.row(5L, "F", 3L, 8L)
230+
.row(6L, "F", 3L, 14L)
231+
.row(null, "F", 3L, 11L)
232+
.row(34L, "O", 1L, 34L)
233+
.row(null, "O", 1L, 34L)
234+
.row(1L, null, 2L, 1L)
235+
.row(7L, null, 2L, 8L)
236+
.row(null, null, 2L, 8L)
237+
.row(null, null, 2L, 8L)
238238
.build());
239239
// Disjoint
240-
assertWindowQueryWithNulls("count(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 3 PRECEDING AND 2 PRECEDING), " +
240+
assertWindowQueryWithNulls("count(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 3 PRECEDING AND 2 FOLLOWING), " +
241241
"sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 1 PRECEDING AND CURRENT ROW)",
242242
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT, BIGINT)
243-
.row(3L, "F", 0L, 3L)
244-
.row(5L, "F", 0L, 8L)
245-
.row(6L, "F", 1L, 11L)
246-
.row(null, "F", 2L, 6L)
247-
.row(34L, "O", 0L, 34L)
248-
.row(null, "O", 0L, 34L)
249-
.row(1L, null, 0L, 1L)
250-
.row(7L, null, 0L, 8L)
251-
.row(null, null, 1L, 7L)
252-
.row(null, null, 1L, 7L)
243+
.row(3L, "F", 3L, 3L)
244+
.row(5L, "F", 3L, 8L)
245+
.row(6L, "F", 3L, 11L)
246+
.row(null, "F", 3L, 6L)
247+
.row(34L, "O", 1L, 34L)
248+
.row(null, "O", 1L, 34L)
249+
.row(1L, null, 2L, 1L)
250+
.row(7L, null, 2L, 8L)
251+
.row(null, null, 2L, 7L)
252+
.row(null, null, 2L, 7L)
253253
.build());
254254
}
255255
}

presto-main/src/test/java/com/facebook/presto/operator/window/TestNthValueFunction.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,12 @@ public void testNthValueBounded()
147147
.row(null, null, null)
148148
.build());
149149

150-
assertWindowQueryWithNulls("nth_value(orderkey, 4) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
151-
"RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING)",
150+
assertWindowQueryWithNulls("nth_value(orderkey, 3) OVER (PARTITION BY orderstatus ORDER BY orderkey " +
151+
"RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING)",
152152
resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT)
153153
.row(3L, "F", null)
154-
.row(5L, "F", null)
155-
.row(6L, "F", null)
154+
.row(5L, "F", 6L)
155+
.row(6L, "F", 6L)
156156
.row(null, "F", null)
157157
.row(34L, "O", null)
158158
.row(null, "O", null)

presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,6 @@ public void testInvalidWindowFrame()
645645
assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN CURRENT ROW AND 5 PRECEDING)");
646646
assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN 2 FOLLOWING AND 5 PRECEDING)");
647647
assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN 2 FOLLOWING AND CURRENT ROW)");
648-
assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN CURRENT ROW AND 5 FOLLOWING)");
649-
assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN 2 PRECEDING AND 5 FOLLOWING)");
650648

651649
assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS 0.5 PRECEDING)");
652650
assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS 'foo' PRECEDING)");

0 commit comments

Comments
 (0)