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>