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>