diff --git a/presto-main/src/main/java/com/facebook/presto/operator/window/WindowPartition.java b/presto-main/src/main/java/com/facebook/presto/operator/window/WindowPartition.java index c882a5fa9c0a7..448faf9633f3b 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/window/WindowPartition.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/window/WindowPartition.java @@ -20,6 +20,7 @@ import com.facebook.presto.sql.tree.FrameBound; import com.google.common.collect.ImmutableList; +import java.util.ArrayList; import java.util.List; import static com.facebook.presto.spi.StandardErrorCode.INVALID_WINDOW_FRAME; @@ -28,6 +29,7 @@ import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_FOLLOWING; import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_PRECEDING; import static com.facebook.presto.sql.tree.WindowFrame.Type.RANGE; +import static com.facebook.presto.sql.tree.WindowFrame.Type.ROWS; import static com.facebook.presto.util.Failures.checkCondition; import static com.google.common.base.Preconditions.checkState; import static java.lang.Math.toIntExact; @@ -46,6 +48,8 @@ public final class WindowPartition private int peerGroupEnd; private int currentPosition; + private List peerGroupStartIndices = new ArrayList<>(); + private List peerGroupEndIndices = new ArrayList<>(); public WindowPartition(PagesIndex pagesIndex, int partitionStart, @@ -142,6 +146,8 @@ private void updatePeerGroup() while ((peerGroupEnd < partitionEnd) && pagesIndex.positionEqualsPosition(peerGroupHashStrategy, peerGroupStart, peerGroupEnd)) { peerGroupEnd++; } + peerGroupStartIndices.add(peerGroupStart); + peerGroupEndIndices.add(peerGroupEnd); } private Range getFrameRange(FrameInfo frameInfo) @@ -150,7 +156,11 @@ private Range getFrameRange(FrameInfo frameInfo) int endPosition = partitionEnd - partitionStart - 1; // handle empty frame - if (emptyFrame(frameInfo, rowPosition, endPosition)) { + if (frameInfo.getType() == ROWS && emptyFrame(frameInfo, rowPosition, endPosition - rowPosition)) { + return new Range(-1, -1); + } + + if (frameInfo.getType() == RANGE && emptyFrame(frameInfo, peerGroupStart, endPosition - (peerGroupEnd - 1))) { return new Range(-1, -1); } @@ -161,6 +171,12 @@ private Range getFrameRange(FrameInfo frameInfo) if (frameInfo.getStartType() == UNBOUNDED_PRECEDING) { frameStart = 0; } + else if (frameInfo.getType() == RANGE && frameInfo.getStartType() == PRECEDING) { + frameStart = precedingStartRange(getStartValue(frameInfo)); + } + else if (frameInfo.getType() == RANGE && frameInfo.getStartType() == FOLLOWING) { + frameStart = followingRange(rowPosition, endPosition, getStartValue(frameInfo)); + } else if (frameInfo.getStartType() == PRECEDING) { frameStart = preceding(rowPosition, getStartValue(frameInfo)); } @@ -178,6 +194,12 @@ else if (frameInfo.getType() == RANGE) { if (frameInfo.getEndType() == UNBOUNDED_FOLLOWING) { frameEnd = endPosition; } + else if (frameInfo.getType() == RANGE && frameInfo.getEndType() == PRECEDING) { + frameEnd = precedingEndRange(getEndValue(frameInfo)); + } + else if (frameInfo.getType() == RANGE && frameInfo.getEndType() == FOLLOWING) { + frameEnd = followingRange(peerGroupEnd, endPosition + 1, getEndValue(frameInfo)) - 1; + } else if (frameInfo.getEndType() == PRECEDING) { frameEnd = preceding(rowPosition, getEndValue(frameInfo)); } @@ -194,19 +216,62 @@ else if (frameInfo.getType() == RANGE) { return new Range(frameStart, frameEnd); } - private boolean emptyFrame(FrameInfo frameInfo, int rowPosition, int endPosition) + private int precedingEndRange(long endValue) + { + int peerGroupEndIndex = peerGroupEndIndices.indexOf(peerGroupEnd); + if (peerGroupEndIndex < endValue) { + return peerGroupEnd - 1; + } + return peerGroupEndIndices.get(toIntExact(peerGroupEndIndex - endValue)) - 1; + } + + private int precedingStartRange(long startValue) + { + int peerGroupStartIndex = peerGroupStartIndices.indexOf(peerGroupStart); + if (peerGroupStartIndex < startValue) { + return 0; + } + return peerGroupStartIndices.get(toIntExact(peerGroupStartIndex - startValue)); + } + + private int followingRange(int followingPeerGroupStart, int endPosition, long value) + { + if (value == 0) { + return followingPeerGroupStart; + } + // TODO: Optimize this to *not* look for peers often, probably have pageIndex keep the peer groups + int followingPeerGroupEnd = 0; + int currentValue = 0; + while (currentValue < value) { + boolean peerFound = false; + followingPeerGroupEnd = followingPeerGroupStart + 1; + while ((followingPeerGroupEnd < partitionEnd) && pagesIndex.positionEqualsPosition(peerGroupHashStrategy, followingPeerGroupStart, followingPeerGroupEnd)) { + followingPeerGroupEnd++; + peerFound = true; + } + if (followingPeerGroupEnd >= partitionEnd) { + return endPosition; + } + + if (!peerFound) { + currentValue++; + } + followingPeerGroupStart++; + } + return followingPeerGroupEnd; + } + + private boolean emptyFrame(FrameInfo frameInfo, int rowPosition, int position) { FrameBound.Type startType = frameInfo.getStartType(); FrameBound.Type endType = frameInfo.getEndType(); - int positions = endPosition - rowPosition; - if ((startType == UNBOUNDED_PRECEDING) && (endType == PRECEDING)) { return getEndValue(frameInfo) > rowPosition; } if ((startType == FOLLOWING) && (endType == UNBOUNDED_FOLLOWING)) { - return getStartValue(frameInfo) > positions; + return getStartValue(frameInfo) > position; } if (startType != endType) { @@ -225,7 +290,7 @@ private boolean emptyFrame(FrameInfo frameInfo, int rowPosition, int endPosition return (start < end) || ((start > rowPosition) && (end > rowPosition)); } - return (start > end) || ((start > positions) && (end > positions)); + return (start > end) || ((start > position) && (end > position)); } private static int preceding(int rowPosition, long value) diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java index dff69f7443e73..034a56098ad87 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java @@ -187,7 +187,6 @@ import static com.facebook.presto.sql.tree.FrameBound.Type.PRECEDING; import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_FOLLOWING; import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_PRECEDING; -import static com.facebook.presto.sql.tree.WindowFrame.Type.RANGE; import static com.facebook.presto.type.UnknownType.UNKNOWN; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -1260,12 +1259,6 @@ private void analyzeWindowFrame(WindowFrame frame) if ((startType == FOLLOWING) && (endType == CURRENT_ROW)) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame starting from FOLLOWING cannot end with CURRENT ROW"); } - if ((frame.getType() == RANGE) && ((startType == PRECEDING) || (endType == PRECEDING))) { - throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame RANGE PRECEDING is only supported with UNBOUNDED"); - } - if ((frame.getType() == RANGE) && ((startType == FOLLOWING) || (endType == FOLLOWING))) { - throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame RANGE FOLLOWING is only supported with UNBOUNDED"); - } } private void analyzeHaving(QuerySpecification node, Scope scope) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/window/TestAggregateWindowFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/window/TestAggregateWindowFunction.java index 3dcaf6b6dd157..028a34b18eaf0 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/window/TestAggregateWindowFunction.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/window/TestAggregateWindowFunction.java @@ -532,4 +532,237 @@ public void testSumAllNulls() .row(null, null, null) .build()); } + + @Test + public void testSumRangePrecedingBounded() + { + assertWindowQueryWithNulls("sum(orderkey) OVER (ORDER BY orderstatus " + + "RANGE 2 PRECEDING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", 14L) + .row(5L, "F", 14L) + .row(6L, "F", 14L) + .row(null, "F", 14L) + .row(34L, "O", 48L) + .row(null, "O", 48L) + .row(1L, null, 56L) + .row(7L, null, 56L) + .row(null, null, 56L) + .row(null, null, 56L) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE 2 PRECEDING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", 3L) + .row(5L, "F", 8L) + .row(6L, "F", 14L) + .row(null, "F", 11L) + .row(34L, "O", 34L) + .row(null, "O", 34L) + .row(1L, null, 1L) + .row(7L, null, 8L) + .row(null, null, 8L) + .row(null, null, 8L) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", 3L) + .row(6L, "F", 8L) + .row(null, "F", 11L) + .row(34L, "O", null) + .row(null, "O", 34L) + .row(1L, null, null) + .row(7L, null, 1L) + .row(null, null, 8L) + .row(null, null, 8L) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", 3L) + .row(5L, "F", 8L) + .row(6L, "F", 14L) + .row(null, "F", 14L) + .row(34L, "O", 34L) + .row(null, "O", 34L) + .row(1L, null, 1L) + .row(7L, null, 8L) + .row(null, null, 8L) + .row(null, null, 8L) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN UNBOUNDED PRECEDING AND 2 PRECEDING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", null) + .row(6L, "F", 3L) + .row(null, "F", 8L) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, null) + .row(7L, null, null) + .row(null, null, 1L) + .row(null, null, 1L) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN UNBOUNDED PRECEDING AND 4 PRECEDING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", null) + .row(6L, "F", null) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, null) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 PRECEDING AND 3 PRECEDING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", null) + .row(6L, "F", null) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, null) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); + } + + @Test + public void testSumRangeFollowingBounded() + { + assertWindowQueryWithNulls("sum(orderkey) OVER (ORDER BY orderstatus " + + "RANGE BETWEEN current row AND 1 FOLLOWING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", 48L) + .row(5L, "F", 48L) + .row(6L, "F", 48L) + .row(null, "F", 48L) + .row(34L, "O", 42L) + .row(null, "O", 42L) + .row(1L, null, 8L) + .row(7L, null, 8L) + .row(null, null, 8L) + .row(null, null, 8L) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN current row AND 1 FOLLOWING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", 8L) + .row(5L, "F", 11L) + .row(6L, "F", 6L) + .row(null, "F", null) + .row(34L, "O", 34L) + .row(null, "O", null) + .row(1L, null, 8L) + .row(7L, null, 7L) + .row(null, null, null) + .row(null, null, null) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN current row AND 0 FOLLOWING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", 3L) + .row(5L, "F", 5L) + .row(6L, "F", 6L) + .row(null, "F", null) + .row(34L, "O", 34L) + .row(null, "O", null) + .row(1L, null, 1L) + .row(7L, null, 7L) + .row(null, null, null) + .row(null, null, null) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 1 FOLLOWING AND 2 FOLLOWING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", 11L) + .row(5L, "F", 6L) + .row(6L, "F", null) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, 7L) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 0 FOLLOWING AND UNBOUNDED FOLLOWING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", 14L) + .row(5L, "F", 11L) + .row(6L, "F", 6L) + .row(null, "F", null) + .row(34L, "O", 34L) + .row(null, "O", null) + .row(1L, null, 8L) + .row(7L, null, 7L) + .row(null, null, null) + .row(null, null, null) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 FOLLOWING AND UNBOUNDED FOLLOWING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", 6L) + .row(5L, "F", null) + .row(6L, "F", null) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, null) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 4 FOLLOWING AND UNBOUNDED FOLLOWING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", null) + .row(6L, "F", null) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, null) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); + + assertWindowQueryWithNulls("sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 3 FOLLOWING AND 2 FOLLOWING)", + resultBuilder(TEST_SESSION, INTEGER, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", null) + .row(6L, "F", null) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, null) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/window/TestFirstValueFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/window/TestFirstValueFunction.java index 12d665885a080..2c2ab5bb08f7c 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/window/TestFirstValueFunction.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/window/TestFirstValueFunction.java @@ -128,5 +128,33 @@ public void testFirstValueBounded() .row(null, null, 1L) .row(null, null, 7L) .build()); + assertWindowQueryWithNulls("first_value(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", 3L) + .row(6L, "F", 3L) + .row(null, "F", 5L) + .row(34L, "O", null) + .row(null, "O", 34L) + .row(1L, null, null) + .row(7L, null, 1L) + .row(null, null, 1L) + .row(null, null, 1L) + .build()); + assertWindowQueryWithNulls("first_value(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", 3L) + .row(5L, "F", 3L) + .row(6L, "F", 3L) + .row(null, "F", 5L) + .row(34L, "O", 34L) + .row(null, "O", 34L) + .row(1L, null, 1L) + .row(7L, null, 1L) + .row(null, null, 1L) + .row(null, null, 1L) + .build()); } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/window/TestLastValueFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/window/TestLastValueFunction.java index 8a6818cfae05c..57cbcdc8b9dd3 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/window/TestLastValueFunction.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/window/TestLastValueFunction.java @@ -127,5 +127,33 @@ public void testLastValueBounded() .row(null, null, null) .row(null, null, null) .build()); + assertWindowQueryWithNulls("last_value(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 PRECEDING AND 1 PRECEDING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", 3L) + .row(6L, "F", 5L) + .row(null, "F", 6L) + .row(34L, "O", null) + .row(null, "O", 34L) + .row(1L, null, null) + .row(7L, null, 1L) + .row(null, null, 7L) + .row(null, null, 7L) + .build()); + assertWindowQueryWithNulls("last_value(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", 5L) + .row(5L, "F", 6L) + .row(6L, "F", null) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, 7L) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/window/TestMultipleWindowSpecifications.java b/presto-main/src/test/java/com/facebook/presto/operator/window/TestMultipleWindowSpecifications.java index c208cd9cff74a..fafbad350ada4 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/window/TestMultipleWindowSpecifications.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/window/TestMultipleWindowSpecifications.java @@ -217,4 +217,39 @@ public void testDisjointWindowSpecifications() .row(null, null, 2L, null) .build()); } + + @Test + public void testMultipleWindowSpecificationsWithRange() + { + // Intersection previous to current row + assertWindowQueryWithNulls("count(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 3 PRECEDING AND 1 FOLLOWING), " + + "sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT, BIGINT) + .row(3L, "F", 2L, 3L) + .row(5L, "F", 3L, 8L) + .row(6L, "F", 3L, 14L) + .row(null, "F", 3L, 11L) + .row(34L, "O", 1L, 34L) + .row(null, "O", 1L, 34L) + .row(1L, null, 2L, 1L) + .row(7L, null, 2L, 8L) + .row(null, null, 2L, 8L) + .row(null, null, 2L, 8L) + .build()); + // Disjoint + assertWindowQueryWithNulls("count(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 3 PRECEDING AND 2 FOLLOWING), " + + "sum(orderkey) OVER (PARTITION BY orderstatus ORDER BY orderkey RANGE BETWEEN 1 PRECEDING AND CURRENT ROW)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT, BIGINT) + .row(3L, "F", 3L, 3L) + .row(5L, "F", 3L, 8L) + .row(6L, "F", 3L, 11L) + .row(null, "F", 3L, 6L) + .row(34L, "O", 1L, 34L) + .row(null, "O", 1L, 34L) + .row(1L, null, 2L, 1L) + .row(7L, null, 2L, 8L) + .row(null, null, 2L, 7L) + .row(null, null, 2L, 7L) + .build()); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/window/TestNthValueFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/window/TestNthValueFunction.java index 748998afc0f1e..90df84df2b702 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/window/TestNthValueFunction.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/window/TestNthValueFunction.java @@ -147,6 +147,20 @@ public void testNthValueBounded() .row(null, null, null) .build()); + assertWindowQueryWithNulls("nth_value(orderkey, 3) OVER (PARTITION BY orderstatus ORDER BY orderkey " + + "RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING)", + resultBuilder(TEST_SESSION, BIGINT, VARCHAR, BIGINT) + .row(3L, "F", null) + .row(5L, "F", 6L) + .row(6L, "F", 6L) + .row(null, "F", null) + .row(34L, "O", null) + .row(null, "O", null) + .row(1L, null, null) + .row(7L, null, null) + .row(null, null, null) + .row(null, null, null) + .build()); // Timestamp assertWindowQuery("date_format(nth_value(cast(orderdate as TIMESTAMP), 2) OVER (PARTITION BY orderstatus ORDER BY orderkey), '%Y-%m-%d')", resultBuilder(TEST_SESSION, INTEGER, VARCHAR, VARCHAR) diff --git a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java index f738d464cff41..6b4445d3c72be 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java @@ -645,10 +645,6 @@ public void testInvalidWindowFrame() assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN CURRENT ROW AND 5 PRECEDING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN 2 FOLLOWING AND 5 PRECEDING)"); assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (ROWS BETWEEN 2 FOLLOWING AND CURRENT ROW)"); - assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE 2 PRECEDING)"); - assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)"); - assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN CURRENT ROW AND 5 FOLLOWING)"); - assertFails(INVALID_WINDOW_FRAME, "SELECT rank() OVER (RANGE BETWEEN 2 PRECEDING AND 5 FOLLOWING)"); assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS 0.5 PRECEDING)"); assertFails(TYPE_MISMATCH, "SELECT rank() OVER (ROWS 'foo' PRECEDING)");