diff --git a/api/src/test/java/org/opensearch/sql/api/UnifiedQueryPlannerTest.java b/api/src/test/java/org/opensearch/sql/api/UnifiedQueryPlannerTest.java index f8cc834dee8..00bceba9fe2 100644 --- a/api/src/test/java/org/opensearch/sql/api/UnifiedQueryPlannerTest.java +++ b/api/src/test/java/org/opensearch/sql/api/UnifiedQueryPlannerTest.java @@ -171,7 +171,8 @@ public void unsupportedWindowFunctionIsRethrownAsSemanticCheckException() { givenInvalidQuery("source = catalog.employees | eventstats percent_rank()") .assertErrorType(SemanticCheckException.class) .assertCauseType(CalciteUnsupportedException.class) - .assertErrorMessageContains("Unexpected window function: percent_rank"); + .assertErrorMessageContains( + "Window function 'percent_rank' is not supported in eventstats/streamstats"); } @Test diff --git a/core/src/main/java/org/opensearch/sql/calcite/CalciteRexNodeVisitor.java b/core/src/main/java/org/opensearch/sql/calcite/CalciteRexNodeVisitor.java index b6500dd8087..c0944616a01 100644 --- a/core/src/main/java/org/opensearch/sql/calcite/CalciteRexNodeVisitor.java +++ b/core/src/main/java/org/opensearch/sql/calcite/CalciteRexNodeVisitor.java @@ -95,6 +95,7 @@ import org.opensearch.sql.exception.CalciteUnsupportedException; import org.opensearch.sql.exception.ExpressionEvaluationException; import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.executor.QueryType; import org.opensearch.sql.expression.function.BuiltinFunctionName; import org.opensearch.sql.expression.function.CoercionUtils; import org.opensearch.sql.expression.function.PPLBuiltinOperators; @@ -763,6 +764,17 @@ public RexNode visitWindowFunction(WindowFunction node, CalcitePlanContext conte .toList(); List orderKeys = translateOrderKeys(node.getSortList(), context); return BuiltinFunctionName.ofWindowFunction(funcName) + // rank/dense_rank were added to WINDOW_FUNC_MAPPING to support SQL's RANK()/DENSE_RANK() + // OVER (...), but PPL only ever reaches this visitor through eventstats/streamstats + // (there's no other PPL syntax that builds a WindowFunction node), which don't support + // them - PPL has no ORDER BY syntax for eventstats/streamstats, so ranking would be + // meaningless there. Excluding them here for PPL keeps that restriction despite the + // shared mapping, without needing a PPL-specific grammar change. + .filter( + functionName -> + !(context.queryType == QueryType.PPL + && (functionName == BuiltinFunctionName.RANK + || functionName == BuiltinFunctionName.DENSE_RANK))) .map( functionName -> { RexNode field = arguments.isEmpty() ? null : arguments.getFirst(); @@ -808,7 +820,14 @@ public RexNode visitWindowFunction(WindowFunction node, CalcitePlanContext conte node.getWindowFrame()); }) .orElseThrow( - () -> new CalciteUnsupportedException("Unexpected window function: " + funcName)); + () -> + new CalciteUnsupportedException( + "Window function '" + + funcName + + "' is not supported in eventstats/streamstats." + + " Supported functions: avg, count, dc, distinct_count, earliest," + + " latest, max, min, row_number, stddev_pop, stddev_samp, sum," + + " var_pop, var_samp.")); } private List translateOrderKeys( diff --git a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLEventstatsIT.java b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLEventstatsIT.java index a2d1e4af086..36f870f4a4e 100644 --- a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLEventstatsIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalcitePPLEventstatsIT.java @@ -329,7 +329,22 @@ public void testUnsupportedWindowFunctions() { executeQuery( String.format( "source=%s | eventstats %s(age)", TEST_INDEX_STATE_COUNTRY, u))); - verifyErrorMessageContains(e, "Unexpected window function: " + u); + verifyErrorMessageContains(e, "is not supported in eventstats/streamstats"); + } + } + + @Test + public void testRankingWindowFunctionsUnsupportedInEventstats() { + for (String func : List.of("rank", "dense_rank")) { + Throwable e = + assertThrowsWithReplace( + UnsupportedOperationException.class, + () -> + executeQuery( + String.format( + "source=%s | eventstats %s() by state", TEST_INDEX_STATE_COUNTRY, func))); + verifyErrorMessageContains( + e, "Window function '" + func + "' is not supported in eventstats/streamstats"); } } diff --git a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStreamstatsCommandIT.java b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStreamstatsCommandIT.java index e70812e3c3b..26407b08dfc 100644 --- a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStreamstatsCommandIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteStreamstatsCommandIT.java @@ -846,7 +846,23 @@ public void testUnsupportedWindowFunctions() { executeQuery( String.format( "source=%s | streamstats %s(age)", TEST_INDEX_STATE_COUNTRY, u))); - verifyErrorMessageContains(e, "Unexpected window function: " + u); + verifyErrorMessageContains(e, "is not supported in eventstats/streamstats"); + } + } + + @Test + public void testRankingWindowFunctionsUnsupportedInStreamstats() { + for (String func : List.of("rank", "dense_rank")) { + Throwable e = + assertThrowsWithReplace( + UnsupportedOperationException.class, + () -> + executeQuery( + String.format( + "source=%s | streamstats %s() by state", + TEST_INDEX_STATE_COUNTRY, func))); + verifyErrorMessageContains( + e, "Window function '" + func + "' is not supported in eventstats/streamstats"); } }