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 02856ec340 [CALCITE-6878] Implement FilterSortTransposeRule
02856ec340 is described below

commit 02856ec340ca7314c787d57fd01f3e761d777278
Author: Zhen Chen <[email protected]>
AuthorDate: Mon Mar 10 16:25:45 2025 +0800

    [CALCITE-6878] Implement FilterSortTransposeRule
---
 .../org/apache/calcite/rel/rules/CoreRules.java    |  5 ++
 .../calcite/rel/rules/FilterSortTransposeRule.java | 71 ++++++++++++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.java   | 39 ++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 38 ++++++++++++
 4 files changed, 153 insertions(+)

diff --git a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java 
b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
index 57611ffee6..ee75345a7e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
@@ -294,6 +294,11 @@ private CoreRules() {}
   public static final FilterTableScanRule FILTER_SCAN =
       FilterTableScanRule.Config.DEFAULT.toRule();
 
+  /** Rule that transforms a {@link Filter} on top of a {@link Sort}
+   * into a {@link Sort} on top of a {@link Filter}. */
+  public static final FilterSortTransposeRule FILTER_SORT_TRANSPOSE =
+          FilterSortTransposeRule.Config.DEFAULT.toRule();
+
   /** Rule that matches a {@link Filter} on an
    * {@link org.apache.calcite.adapter.enumerable.EnumerableInterpreter} on a
    * {@link TableScan}. */
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/FilterSortTransposeRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/FilterSortTransposeRule.java
new file mode 100644
index 0000000000..ce519b7d5f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/FilterSortTransposeRule.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.tools.RelBuilder;
+
+import org.immutables.value.Value;
+
+/**
+ * Rule that transforms a {@link Filter} on top of a {@link Sort}
+ * into a {@link Sort} on top of a {@link Filter}.
+ */
[email protected]
+public class FilterSortTransposeRule
+    extends RelRule<FilterSortTransposeRule.Config>
+    implements TransformationRule {
+  protected FilterSortTransposeRule(FilterSortTransposeRule.Config config) {
+    super(config);
+  }
+
+  @Override public void onMatch(RelOptRuleCall call) {
+    final Filter filter = call.rel(0);
+    final Sort sort = call.rel(1);
+
+    final RelBuilder builder = call.builder();
+    final RexNode condition = filter.getCondition();
+    RelNode newSort = builder.push(sort.getInput())
+        .filter(condition)
+        .sort(sort.getCollation())
+        .build();
+
+    call.transformTo(newSort);
+  }
+
+  /** Rule configuration. */
+  @Value.Immutable
+  public interface Config extends RelRule.Config {
+    Config DEFAULT = ImmutableFilterSortTransposeRule.Config.of()
+        .withOperandSupplier(f ->
+            f.operand(Filter.class)
+                .oneInput(s ->
+                    s.operand(Sort.class)
+                        .predicate(RelOptUtil::isPureOrder)
+                        .anyInputs()));
+
+    @Override default FilterSortTransposeRule toRule() {
+      return new FilterSortTransposeRule(this);
+    }
+  }
+}
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 61fc66acab..e9a7227081 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -578,6 +578,45 @@ private static boolean skipItem(RexNode expr) {
         .check();
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6878";>[CALCITE-6878]
+   * Implement FilterSortTransposeRule</a>. */
+  @Test void testFilterSortTranspose() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+            .scan("EMP")
+            .project(b.field(0))
+            .sort(b.field(0))
+            .filter(b.lessThan(b.field(0), b.literal(10)))
+            .build();
+    relFn(relFn).withRule(CoreRules.FILTER_SORT_TRANSPOSE).check();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6878";>[CALCITE-6878]
+   * Implement FilterSortTransposeRule</a>. */
+  @Test void testFilterSortTransposeFetch() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .project(b.field(0))
+        .sortLimit(0, 1, b.field(0))
+        .filter(b.lessThan(b.field(0), b.literal(10)))
+        .build();
+    relFn(relFn).withRule(CoreRules.FILTER_SORT_TRANSPOSE).checkUnchanged();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6878";>[CALCITE-6878]
+   * Implement FilterSortTransposeRule</a>. */
+  @Test void testFilterSortTransposeOffset() {
+    final Function<RelBuilder, RelNode> relFn = b -> b
+        .scan("EMP")
+        .project(b.field(0))
+        .sortLimit(1, 0, b.field(0))
+        .filter(b.lessThan(b.field(0), b.literal(10)))
+        .build();
+    relFn(relFn).withRule(CoreRules.FILTER_SORT_TRANSPOSE).checkUnchanged();
+  }
+
   @Test void testReduceOrCaseWhen() {
     HepProgramBuilder builder = new HepProgramBuilder();
     builder.addRuleClass(ReduceExpressionsRule.class);
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 11ca9705cb..be8a8aeaad 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -5498,6 +5498,44 @@ LogicalProject(DEPTNO=[$7])
   Sample(mode=[system], rate=[0.5], repeatableSeed=[10])
     LogicalFilter(condition=[>($7, 10)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testFilterSortTranspose">
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalFilter(condition=[<($0, 10)])
+  LogicalSort(sort0=[$0], dir0=[ASC])
+    LogicalProject(EMPNO=[$0])
+      LogicalTableScan(table=[[scott, EMP]])
+]]>
+    </Resource>
+    <Resource name="planAfter">
+      <![CDATA[
+LogicalSort(sort0=[$0], dir0=[ASC])
+  LogicalFilter(condition=[<($0, 10)])
+    LogicalProject(EMPNO=[$0])
+      LogicalTableScan(table=[[scott, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testFilterSortTransposeFetch">
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalFilter(condition=[<($0, 10)])
+  LogicalSort(sort0=[$0], dir0=[ASC], fetch=[1])
+    LogicalProject(EMPNO=[$0])
+      LogicalTableScan(table=[[scott, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testFilterSortTransposeOffset">
+    <Resource name="planBefore">
+      <![CDATA[
+LogicalFilter(condition=[<($0, 10)])
+  LogicalSort(sort0=[$0], dir0=[ASC], offset=[1], fetch=[0])
+    LogicalProject(EMPNO=[$0])
+      LogicalTableScan(table=[[scott, EMP]])
 ]]>
     </Resource>
   </TestCase>

Reply via email to