Skip to content

feat(sql): Support RANK/DENSE_RANK in unified SQL - #5720

Draft
dai-chen wants to merge 1 commit into
opensearch-project:mainfrom
dai-chen:support-rank-dense-rank-calcite
Draft

feat(sql): Support RANK/DENSE_RANK in unified SQL#5720
dai-chen wants to merge 1 commit into
opensearch-project:mainfrom
dai-chen:support-rank-dense-rank-calcite

Conversation

@dai-chen

@dai-chen dai-chen commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Description

RANK and DENSE_RANK were already accepted by the SQL grammar and declared as BuiltinFunctionName constants, but were missing from WINDOW_FUNC_MAPPING, so visitWindowFunction rejected them with "Unexpected window function".

Register both in the window function mapping, extend the ROW_NUMBER bypass of aggregate signature validation since they likewise take no arguments, and lower them to SqlStdOperatorTable.RANK and DENSE_RANK. Calcite rank operators disallow framing, so the ROWS/RANGE flag is normalized away.

The mapping is shared with PPL, so eventstats/streamstats now resolve these functions instead of erroring. They return rank 1 for every row there, since neither command can express an ORDER BY.

Related Issues

Part of #5248

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • New functionality has javadoc added.
  • New functionality has a user manual doc added.
  • New PPL command checklist all confirmed.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff or -s.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@dai-chen dai-chen self-assigned this Aug 24, 2026
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit 77ae6e0)

Here are some key observations to aid the review process:

🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ No major issues detected

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Latest suggestions up to 77ae6e0

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Explicitly pass null for frame bounds

Since RANK and DENSE_RANK disallow framing, passing lowerBound and upperBound
parameters may cause unexpected behavior or errors. Consider explicitly passing null
for both bounds to ensure proper handling of these ranking functions.

core/src/main/java/org/opensearch/sql/calcite/utils/PlanUtils.java [236-251]

 case RANK:
   return withOver(
       context.relBuilder.aggregateCall(SqlStdOperatorTable.RANK),
       partitions,
       orderKeys,
       false,
-      lowerBound,
-      upperBound);
+      null,
+      null);
 case DENSE_RANK:
   return withOver(
       context.relBuilder.aggregateCall(SqlStdOperatorTable.DENSE_RANK),
       partitions,
       orderKeys,
       false,
-      lowerBound,
-      upperBound);
+      null,
+      null);
Suggestion importance[1-10]: 4

__

Why: While the suggestion correctly identifies that RANK and DENSE_RANK disallow framing, the comment in the PR already acknowledges this ("Calcite rank operators disallow framing, so the ROWS/RANGE flag below is normalized away"). The current implementation passes lowerBound and upperBound which are likely normalized by Calcite. Explicitly passing null would be slightly clearer but is not critical since the normalization handles this.

Low

Previous suggestions

Suggestions up to commit 747d1b6
CategorySuggestion                                                                                                                                    Impact
Possible issue
Validate ORDER BY clause presence

The RANK and DENSE_RANK window functions require an ORDER BY clause to function
correctly. Consider validating that orderKeys is not empty before creating the
aggregate call to prevent runtime errors or unexpected behavior when no ordering is
specified.

core/src/main/java/org/opensearch/sql/calcite/utils/PlanUtils.java [236-251]

 case RANK:
+  if (orderKeys.isEmpty()) {
+    throw new IllegalArgumentException("RANK requires ORDER BY clause");
+  }
   return withOver(
       context.relBuilder.aggregateCall(SqlStdOperatorTable.RANK),
       partitions,
       orderKeys,
       false,
       lowerBound,
       upperBound);
 case DENSE_RANK:
+  if (orderKeys.isEmpty()) {
+    throw new IllegalArgumentException("DENSE_RANK requires ORDER BY clause");
+  }
   return withOver(
       context.relBuilder.aggregateCall(SqlStdOperatorTable.DENSE_RANK),
       partitions,
       orderKeys,
       false,
       lowerBound,
       upperBound);
Suggestion importance[1-10]: 5

__

Why: While RANK and DENSE_RANK do require an ORDER BY clause semantically, this validation may already be handled at the SQL parsing/validation layer. Adding redundant validation here could be useful for defensive programming, but without evidence of missing validation upstream, this is a moderate improvement for robustness rather than fixing a critical bug.

Low
Suggestions up to commit 352ffbd
CategorySuggestion                                                                                                                                    Impact
General
Use Set for multiple condition checks

Consider using a Set or EnumSet for checking multiple function names instead of
chained OR conditions. This improves readability and makes it easier to add more
functions in the future.

core/src/main/java/org/opensearch/sql/calcite/CalciteRexNodeVisitor.java [775-777]

-if (functionName == BuiltinFunctionName.ROW_NUMBER
-    || functionName == BuiltinFunctionName.RANK
-    || functionName == BuiltinFunctionName.DENSE_RANK) {
+private static final Set<BuiltinFunctionName> NO_FIELD_WINDOW_FUNCTIONS = 
+    EnumSet.of(BuiltinFunctionName.ROW_NUMBER, BuiltinFunctionName.RANK, BuiltinFunctionName.DENSE_RANK);
 
+if (NO_FIELD_WINDOW_FUNCTIONS.contains(functionName)) {
+
Suggestion importance[1-10]: 4

__

Why: While using an EnumSet would improve maintainability and readability, the current chained OR condition with three items is still acceptable and clear. The suggestion is valid but offers only a moderate improvement in code style rather than fixing a critical issue.

Low

@dai-chen
dai-chen force-pushed the support-rank-dense-rank-calcite branch from 352ffbd to 747d1b6 Compare August 25, 2026 00:21
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 747d1b6

Both were accepted by the SQL grammar and declared as
BuiltinFunctionName constants, but missing from WINDOW_FUNC_MAPPING, so
visitWindowFunction rejected them with "Unexpected window function".

Register both, extend the ROW_NUMBER bypass of aggregate signature
validation since they likewise take no arguments, and lower them to
SqlStdOperatorTable.RANK and DENSE_RANK.

WINDOW_FUNC_MAPPING is shared, so PPL eventstats/streamstats now resolve
these functions as well.

Related to opensearch-project#5168

Signed-off-by: Chen Dai <daichen@amazon.com>
@dai-chen
dai-chen force-pushed the support-rank-dense-rank-calcite branch from 747d1b6 to 77ae6e0 Compare August 25, 2026 18:17
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 77ae6e0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant