[CALCITE-939] Variant of SortUnionTransposeRule for order-preserving Union (Maryann Xue)
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/1a491b7f Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/1a491b7f Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/1a491b7f Branch: refs/heads/branch-release Commit: 1a491b7f9fbe8ae2f2a88b2fe40945f0fe526cbc Parents: d02305e Author: maryannxue <[email protected]> Authored: Tue Oct 27 16:20:54 2015 -0700 Committer: Julian Hyde <[email protected]> Committed: Tue Oct 27 16:20:54 2015 -0700 ---------------------------------------------------------------------- .../rel/rules/SortUnionTransposeRule.java | 54 ++++++++++++++++---- .../apache/calcite/test/RelOptRulesTest.java | 16 ++++++ .../org/apache/calcite/test/RelOptRulesTest.xml | 33 ++++++++++++ 3 files changed, 93 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/1a491b7f/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java index a403ffd..b195e3f 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SortUnionTransposeRule.java @@ -19,9 +19,11 @@ package org.apache.calcite.rel.rules; import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.RelFactories; import org.apache.calcite.rel.core.Sort; import org.apache.calcite.rel.core.Union; import org.apache.calcite.rel.metadata.RelMdUtil; +import org.apache.calcite.tools.RelBuilderFactory; import java.util.ArrayList; import java.util.List; @@ -32,29 +34,61 @@ import java.util.List; * */ public class SortUnionTransposeRule extends RelOptRule { - public static final SortUnionTransposeRule INSTANCE = new SortUnionTransposeRule(); + + /** Rule instance for Union implementation that does not preserve the + * ordering of its inputs. Thus, it makes no sense to match this rule + * if the Sort does not have a limit, i.e., {@link Sort#fetch} is null. */ + public static final SortUnionTransposeRule INSTANCE = new SortUnionTransposeRule(false); + + /** Rule instance for Union implementation that preserves the ordering + * of its inputs. It is still worth applying this rule even if the Sort + * does not have a limit, for the merge of already sorted inputs that + * the Union can do is usually cheap. */ + public static final SortUnionTransposeRule MATCH_NULL_FETCH = new SortUnionTransposeRule(true); + + /** Whether to match a Sort whose {@link Sort#fetch} is null. Generally + * this only makes sense if the Union preserves order (and merges). */ + private final boolean matchNullFetch; // ~ Constructors ----------------------------------------------------------- + private SortUnionTransposeRule(boolean matchNullFetch) { + this(Sort.class, Union.class, matchNullFetch, RelFactories.LOGICAL_BUILDER, + "SortUnionTransposeRule:default"); + } + /** * Creates a SortUnionTransposeRule. */ - private SortUnionTransposeRule() { - super(operand(Sort.class, operand(Union.class, any()))); + public SortUnionTransposeRule( + Class<? extends Sort> sortClass, + Class<? extends Union> unionClass, + boolean matchNullFetch, + RelBuilderFactory relBuilderFactory, + String description) { + super( + operand(sortClass, + operand(unionClass, any())), + relBuilderFactory, description); + this.matchNullFetch = matchNullFetch; } // ~ Methods ---------------------------------------------------------------- + @Override public boolean matches(RelOptRuleCall call) { + final Sort sort = call.rel(0); + final Union union = call.rel(1); + // We only apply this rule if Union.all is true and Sort.offset is null. + // There is a flag indicating if this rule should be applied when + // Sort.fetch is null. + return union.all + && sort.offset == null + && (matchNullFetch || sort.fetch != null); + } + public void onMatch(RelOptRuleCall call) { final Sort sort = call.rel(0); final Union union = call.rel(1); - // It is not valid to apply the rule if - // Union.all is false; - // or Sort.offset is not null - // or Sort.fetch is null. - if (!union.all || sort.offset != null || sort.fetch == null) { - return; - } List<RelNode> inputs = new ArrayList<>(); // Thus we use 'ret' as a flag to identify if we have finished pushing the // sort past a union. http://git-wip-us.apache.org/repos/asf/calcite/blob/1a491b7f/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java ---------------------------------------------------------------------- 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 53c3f63..244871d 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -395,6 +395,22 @@ public class RelOptRulesTest extends RelOptTestBase { checkPlanning(program, sql); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-889">[CALCITE-889] + * Implement SortUnionTransposeRule</a>. */ + @Test public void testSortUnionTranspose2() { + final HepProgram program = + HepProgram.builder() + .addRuleInstance(ProjectSetOpTransposeRule.INSTANCE) + .addRuleInstance(SortUnionTransposeRule.MATCH_NULL_FETCH) + .build(); + final String sql = "select a.name from dept a\n" + + "union all\n" + + "select b.name from dept b\n" + + "order by name"; + checkPlanning(program, sql); + } + @Test public void testSemiJoinRule() { final HepProgram preProgram = HepProgram.builder() http://git-wip-us.apache.org/repos/asf/calcite/blob/1a491b7f/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml ---------------------------------------------------------------------- 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 b1be89c..06e70f7 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -4263,4 +4263,37 @@ LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$ ]]> </Resource> </TestCase> + <TestCase name="testSortUnionTranspose2"> + <Resource name="sql"> + <![CDATA[select a.name from dept a +union all +select b.name from dept b +order by name]]> + </Resource> + <Resource name="planBefore"> + <![CDATA[ +LogicalSort(sort0=[$0], dir0=[ASC]) + LogicalProject(NAME=[$0]) + LogicalUnion(all=[true]) + LogicalProject(NAME=[$1]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) + LogicalProject(NAME=[$1]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) +]]> + </Resource> + <Resource name="planAfter"> + <![CDATA[ +LogicalSort(sort0=[$0], dir0=[ASC]) + LogicalUnion(all=[true]) + LogicalSort(sort0=[$0], dir0=[ASC]) + LogicalProject(NAME=[$0]) + LogicalProject(NAME=[$1]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) + LogicalSort(sort0=[$0], dir0=[ASC]) + LogicalProject(NAME=[$0]) + LogicalProject(NAME=[$1]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) +]]> + </Resource> + </TestCase> </Root>
