This is an automated email from the ASF dual-hosted git repository.

xiong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 7d632a8404 [CALCITE-7047] Improve Volcano planner selection of sort 
conversion rules
7d632a8404 is described below

commit 7d632a8404f3510f98e9758745202e28f90729e7
Author: Zhen Chen <[email protected]>
AuthorDate: Wed Jun 11 12:20:32 2025 +0800

    [CALCITE-7047] Improve Volcano planner selection of sort conversion rules
---
 .../enumerable/EnumerableLimitSortRule.java        |   8 +-
 .../org/apache/calcite/test/RelMetadataTest.java   |   2 +-
 .../org/apache/calcite/test/RelOptRulesTest.java   |  96 +++++++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 114 +++++++++++++++++++++
 4 files changed, 216 insertions(+), 4 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSortRule.java
 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSortRule.java
index 1a03338001..0cc1154166 100644
--- 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSortRule.java
+++ 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSortRule.java
@@ -25,8 +25,9 @@
 import org.immutables.value.Value;
 
 /**
- * Rule to convert an {@link EnumerableLimit} of on
- * {@link EnumerableSort} into an {@link EnumerableLimitSort}.
+ * Rule to convert an {@link LogicalSort} with ({@link LogicalSort#fetch}
+ * or {@link LogicalSort#offset}) and {@link LogicalSort#collation}(Order By)
+ * into an {@link EnumerableLimitSort}.
  */
 @Value.Enclosing
 public class EnumerableLimitSortRule extends 
RelRule<EnumerableLimitSortRule.Config> {
@@ -57,7 +58,8 @@ public interface Config extends RelRule.Config {
         ImmutableEnumerableLimitSortRule.Config.of()
             .withOperandSupplier(b0 ->
                 b0.operand(LogicalSort.class)
-                    .predicate(sort -> sort.fetch != null)
+                    .predicate(sort -> (sort.fetch != null || sort.offset != 
null)
+                        && !sort.collation.getFieldCollations().isEmpty())
                     .anyInputs());
 
     @Override default EnumerableLimitSortRule toRule() {
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java 
b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index 798e750e11..0bd87a9c37 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -732,7 +732,7 @@ void testColumnOriginsUnion() {
           planner.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
           planner.addRule(EnumerableRules.ENUMERABLE_FILTER_RULE);
           planner.addRule(EnumerableRules.ENUMERABLE_JOIN_RULE);
-          planner.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+          planner.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
           planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
           return RelOptCluster.create(planner, cluster.getRexBuilder());
         })
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java 
b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 50e85a5c5b..f6885722d8 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -10798,4 +10798,100 @@ private void 
checkLoptOptimizeJoinRule(LoptOptimizeJoinRule rule) {
     sql(sql).withRule(CoreRules.AGGREGATE_MIN_MAX_TO_LIMIT)
         .check();
   }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7047";>[CALCITE-7047]
+   * Improve Volcano planner selection of sort conversion rules</a>. */
+  @Test void testLimitSortOnlyLimit() {
+    String sql = "select * from emp limit 10";
+    sql(sql)
+        .withVolcanoPlanner(false, p -> {
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+        })
+        .check();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7047";>[CALCITE-7047]
+   * Improve Volcano planner selection of sort conversion rules</a>. */
+  @Test void testLimitSortOnlyOffset() {
+    String sql = "select * from emp offset 10";
+    sql(sql)
+        .withVolcanoPlanner(false, p -> {
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+        })
+        .check();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7047";>[CALCITE-7047]
+   * Improve Volcano planner selection of sort conversion rules</a>. */
+  @Test void testLimitSortOnlyOrderBy() {
+    String sql = "select * from emp order by deptno";
+    sql(sql)
+        .withVolcanoPlanner(false, p -> {
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+        })
+        .check();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7047";>[CALCITE-7047]
+   * Improve Volcano planner selection of sort conversion rules</a>. */
+  @Test void testLimitSortOrderByLimit() {
+    String sql = "select * from emp order by deptno limit 10";
+    sql(sql)
+        .withVolcanoPlanner(false, p -> {
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+        })
+        .check();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7047";>[CALCITE-7047]
+   * Improve Volcano planner selection of sort conversion rules</a>. */
+  @Test void testLimitSortOrderByOffsetLimit() {
+    String sql = "select * from emp order by deptno limit 10 offset 5";
+    sql(sql)
+        .withVolcanoPlanner(false, p -> {
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+        })
+        .check();
+  }
+
+  /** Test case of
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7047";>[CALCITE-7047]
+   * Improve Volcano planner selection of sort conversion rules</a>. */
+  @Test void testLimitSortOrderByOffset() {
+    String sql = "select * from emp order by deptno offset 5";
+    sql(sql)
+        .withVolcanoPlanner(false, p -> {
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
+          p.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
+        })
+        .check();
+  }
 }
diff --git 
a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml 
b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 3de3b1a495..9af18bbb62 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -8355,6 +8355,120 @@ EnumerableLimitSort(sort0=[$0], dir0=[ASC], offset=[5], 
fetch=[10])
         EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
       EnumerableProject(MGR=[$3])
         EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testLimitSortOnlyLimit">
+    <Resource name="sql">
+      <![CDATA[select * from emp limit 10]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalSort(fetch=[10])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+EnumerableLimit(fetch=[10])
+  EnumerableProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testLimitSortOnlyOffset">
+    <Resource name="sql">
+      <![CDATA[select * from emp offset 10]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalSort(offset=[10])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+EnumerableLimit(offset=[10])
+  EnumerableProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testLimitSortOnlyOrderBy">
+    <Resource name="sql">
+      <![CDATA[select * from emp order by deptno]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalSort(sort0=[$7], dir0=[ASC])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+EnumerableSort(sort0=[$7], dir0=[ASC])
+  EnumerableProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testLimitSortOrderByLimit">
+    <Resource name="sql">
+      <![CDATA[select * from emp order by deptno limit 10]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalSort(sort0=[$7], dir0=[ASC], fetch=[10])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+EnumerableLimitSort(sort0=[$7], dir0=[ASC], fetch=[10])
+  EnumerableProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testLimitSortOrderByOffset">
+    <Resource name="sql">
+      <![CDATA[select * from emp order by deptno offset 5]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalSort(sort0=[$7], dir0=[ASC], offset=[5])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+EnumerableLimitSort(sort0=[$7], dir0=[ASC], offset=[5])
+  EnumerableProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testLimitSortOrderByOffsetLimit">
+    <Resource name="sql">
+      <![CDATA[select * from emp order by deptno limit 10 offset 5]]>
+    </Resource>
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalSort(sort0=[$7], dir0=[ASC], offset=[5], fetch=[10])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+EnumerableLimitSort(sort0=[$7], dir0=[ASC], offset=[5], fetch=[10])
+  EnumerableProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+    EnumerableTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
   </TestCase>

Reply via email to