Skip to content

Commit

Permalink
Analyze components of window frame with pattern recognition
Browse files Browse the repository at this point in the history
  • Loading branch information
kasiafi committed Jun 21, 2021
1 parent f98fe48 commit 9d97193
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@
import static io.trino.spi.StandardErrorCode.INVALID_PROCESSING_MODE;
import static io.trino.spi.StandardErrorCode.INVALID_WINDOW_FRAME;
import static io.trino.spi.StandardErrorCode.MISSING_ORDER_BY;
import static io.trino.spi.StandardErrorCode.MISSING_ROW_PATTERN;
import static io.trino.spi.StandardErrorCode.MISSING_VARIABLE_DEFINITIONS;
import static io.trino.spi.StandardErrorCode.NESTED_WINDOW;
import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED;
import static io.trino.spi.StandardErrorCode.NUMERIC_VALUE_OUT_OF_RANGE;
Expand Down Expand Up @@ -1206,13 +1208,33 @@ private void analyzeWindow(ResolvedWindow window, StackableAstVisitorContext<Con
if (window.getFrame().isPresent() && !window.isFrameInherited()) {
WindowFrame frame = window.getFrame().get();

if (!frame.getMeasures().isEmpty() ||
frame.getAfterMatchSkipTo().isPresent() ||
frame.getPatternSearchMode().isPresent() ||
frame.getPattern().isPresent() ||
!frame.getSubsets().isEmpty() ||
!frame.getVariableDefinitions().isEmpty()) {
throw semanticException(NOT_SUPPORTED, frame, "Pattern recognition in window not yet supported");
if (frame.getPattern().isPresent()) {
if (frame.getVariableDefinitions().isEmpty()) {
throw semanticException(MISSING_VARIABLE_DEFINITIONS, frame, "Pattern recognition requires DEFINE clause");
}
if (frame.getType() != ROWS) {
throw semanticException(INVALID_WINDOW_FRAME, frame, "Pattern recognition requires ROWS frame type");
}
if (frame.getStart().getType() != CURRENT_ROW || frame.getEnd().isEmpty()) {
throw semanticException(INVALID_WINDOW_FRAME, frame, "Pattern recognition requires frame specified as BETWEEN CURRENT ROW AND ...");
}
}
else {
if (!frame.getMeasures().isEmpty()) {
throw semanticException(MISSING_ROW_PATTERN, frame, "Row pattern measures require PATTERN clause");
}
if (frame.getAfterMatchSkipTo().isPresent()) {
throw semanticException(MISSING_ROW_PATTERN, frame.getAfterMatchSkipTo().get(), "AFTER MATCH SKIP clause requires PATTERN clause");
}
if (frame.getPatternSearchMode().isPresent()) {
throw semanticException(MISSING_ROW_PATTERN, frame.getPatternSearchMode().get(), "%s modifier requires PATTERN clause", frame.getPatternSearchMode().get().getMode().name());
}
if (!frame.getSubsets().isEmpty()) {
throw semanticException(MISSING_ROW_PATTERN, frame.getSubsets().get(0), "Union variable definitions require PATTERN clause");
}
if (!frame.getVariableDefinitions().isEmpty()) {
throw semanticException(MISSING_ROW_PATTERN, frame.getVariableDefinitions().get(0), "Primary pattern variable definitions require PATTERN clause");
}
}

// validate frame start and end types
Expand Down
101 changes: 93 additions & 8 deletions core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@
import static io.trino.spi.StandardErrorCode.MISSING_GROUP_BY;
import static io.trino.spi.StandardErrorCode.MISSING_ORDER_BY;
import static io.trino.spi.StandardErrorCode.MISSING_OVER;
import static io.trino.spi.StandardErrorCode.MISSING_ROW_PATTERN;
import static io.trino.spi.StandardErrorCode.MISSING_SCHEMA_NAME;
import static io.trino.spi.StandardErrorCode.MISSING_VARIABLE_DEFINITIONS;
import static io.trino.spi.StandardErrorCode.NESTED_AGGREGATION;
import static io.trino.spi.StandardErrorCode.NESTED_RECURSIVE;
import static io.trino.spi.StandardErrorCode.NESTED_ROW_PATTERN_RECOGNITION;
Expand Down Expand Up @@ -1348,7 +1350,7 @@ public void testWindowFrameTypeGroups()
public void testWindowFrameWithPatternRecognition()
{
// in-line window specification
assertFails("SELECT rank() OVER (" +
analyze("SELECT rank() OVER (" +
" PARTITION BY x " +
" ORDER BY y " +
" MEASURES A.z AS last_z " +
Expand All @@ -1361,12 +1363,10 @@ public void testWindowFrameWithPatternRecognition()
" B AS false, " +
" C AS true " +
" ) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(NOT_SUPPORTED)
.hasMessage("line 1:128: Pattern recognition in window not yet supported");
" FROM (VALUES (1, 2, 3)) t(x, y, z)");

// window clause
assertFails("SELECT rank() OVER w FROM (VALUES (1, 2, 3)) t(x, y, z) " +
analyze("SELECT rank() OVER w FROM (VALUES (1, 2, 3)) t(x, y, z) " +
" WINDOW w AS (" +
" PARTITION BY x " +
" ORDER BY y " +
Expand All @@ -1379,9 +1379,94 @@ public void testWindowFrameWithPatternRecognition()
" DEFINE " +
" B AS false, " +
" C AS true " +
" ) ")
.hasErrorCode(NOT_SUPPORTED)
.hasMessage("line 1:200: Pattern recognition in window not yet supported");
" ) ");
}

@Test
public void testInvalidWindowFrameWithPatternRecognition()
{
assertFails("SELECT rank() OVER (" +
" PARTITION BY x " +
" ORDER BY y " +
" MEASURES A.z AS last_z " +
" ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING " +
" AFTER MATCH SKIP TO NEXT ROW " +
" SEEK " +
" PATTERN (A B C) " +
" SUBSET U = (A, B) " +
" ) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(MISSING_VARIABLE_DEFINITIONS)
.hasMessage("line 1:128: Pattern recognition requires DEFINE clause");

assertFails("SELECT rank() OVER (" +
" PARTITION BY x " +
" ORDER BY y " +
" MEASURES A.z AS last_z " +
" RANGE BETWEEN CURRENT ROW AND 5 FOLLOWING " +
" AFTER MATCH SKIP TO NEXT ROW " +
" SEEK " +
" PATTERN (A B C) " +
" SUBSET U = (A, B) " +
" DEFINE " +
" B AS false, " +
" C AS true " +
" ) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(INVALID_WINDOW_FRAME)
.hasMessage("line 1:128: Pattern recognition requires ROWS frame type");

assertFails("SELECT rank() OVER (" +
" PARTITION BY x " +
" ORDER BY y " +
" MEASURES A.z AS last_z " +
" ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING " +
" AFTER MATCH SKIP TO NEXT ROW " +
" SEEK " +
" PATTERN (A B C) " +
" SUBSET U = (A, B) " +
" DEFINE " +
" B AS false, " +
" C AS true " +
" ) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(INVALID_WINDOW_FRAME)
.hasMessage("line 1:128: Pattern recognition requires frame specified as BETWEEN CURRENT ROW AND ...");

assertFails("SELECT rank() OVER ( " +
" MEASURES A.z AS last_z " +
" ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(MISSING_ROW_PATTERN)
.hasMessage("line 1:53: Row pattern measures require PATTERN clause");

assertFails("SELECT rank() OVER (" +
" ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING " +
" AFTER MATCH SKIP TO NEXT ROW) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(MISSING_ROW_PATTERN)
.hasMessage("line 1:136: AFTER MATCH SKIP clause requires PATTERN clause");

assertFails("SELECT rank() OVER (" +
" ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING " +
" SEEK) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(MISSING_ROW_PATTERN)
.hasMessage("line 1:124: SEEK modifier requires PATTERN clause");

assertFails("SELECT rank() OVER (" +
" ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING " +
" SUBSET U = (A, B)) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(MISSING_ROW_PATTERN)
.hasMessage("line 1:131: Union variable definitions require PATTERN clause");

assertFails("SELECT rank() OVER (" +
" ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING " +
" DEFINE B AS false) " +
" FROM (VALUES (1, 2, 3)) t(x, y, z)")
.hasErrorCode(MISSING_ROW_PATTERN)
.hasMessage("line 1:131: Primary pattern variable definitions require PATTERN clause");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ public enum StandardErrorCode
INVALID_RANGE(102, USER_ERROR),
INVALID_PATTERN_RECOGNITION_FUNCTION(103, USER_ERROR),
TABLE_REDIRECTION_ERROR(104, USER_ERROR),
MISSING_VARIABLE_DEFINITIONS(105, USER_ERROR),
MISSING_ROW_PATTERN(106, USER_ERROR),

GENERIC_INTERNAL_ERROR(65536, INTERNAL_ERROR),
TOO_MANY_REQUESTS_FAILED(65537, INTERNAL_ERROR),
Expand Down

0 comments on commit 9d97193

Please sign in to comment.