This is an automated email from the ASF dual-hosted git repository. zabetak pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push: new 0cb51c6 [CALCITE-2822] Allow MultiJoin rules with any project/filter (Siddharth Teotia) 0cb51c6 is described below commit 0cb51c64715dd45f8bbcd533cc9096b4bb7b70af Author: siddharth <siddha...@dremio.com> AuthorDate: Tue Feb 5 11:01:37 2019 -0800 [CALCITE-2822] Allow MultiJoin rules with any project/filter (Siddharth Teotia) Close apache/calcite#1030 --- .../java/org/apache/calcite/plan/RelOptUtil.java | 6 +- .../rel/rules/FilterMultiJoinMergeRule.java | 27 +++- .../rel/rules/MultiJoinProjectTransposeRule.java | 5 +- .../rel/rules/ProjectMultiJoinMergeRule.java | 22 ++- .../apache/calcite/sql2rel/SqlToRelConverter.java | 4 +- .../org/apache/calcite/test/RelOptRulesTest.java | 170 ++++++++++++++++++++- .../org/apache/calcite/test/RelOptRulesTest.xml | 21 +++ 7 files changed, 234 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java index fc76890..452f528 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java @@ -2770,16 +2770,16 @@ public abstract class RelOptUtil { /** * Creates a new {@link org.apache.calcite.rel.rules.MultiJoin} to reflect * projection references from a - * {@link org.apache.calcite.rel.logical.LogicalProject} that is on top of the + * {@link Project} that is on top of the * {@link org.apache.calcite.rel.rules.MultiJoin}. * * @param multiJoin the original MultiJoin - * @param project the LogicalProject on top of the MultiJoin + * @param project the Project on top of the MultiJoin * @return the new MultiJoin */ public static MultiJoin projectMultiJoin( MultiJoin multiJoin, - LogicalProject project) { + Project project) { // Locate all input references in the projection expressions as well // the post-join filter. Since the filter effectively sits in // between the LogicalProject and the MultiJoin, the projection needs diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java index ee96b90..553c4ac 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterMultiJoinMergeRule.java @@ -18,6 +18,7 @@ package org.apache.calcite.rel.rules; import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.rel.core.Filter; import org.apache.calcite.rel.core.RelFactories; import org.apache.calcite.rel.logical.LogicalFilter; import org.apache.calcite.rex.RexBuilder; @@ -30,8 +31,7 @@ import java.util.List; /** * Planner rule that merges a - * {@link org.apache.calcite.rel.logical.LogicalFilter} - * into a {@link MultiJoin}, + * {@link Filter} into a {@link MultiJoin}, * creating a richer {@code MultiJoin}. * * @see org.apache.calcite.rel.rules.ProjectMultiJoinMergeRule @@ -43,19 +43,32 @@ public class FilterMultiJoinMergeRule extends RelOptRule { //~ Constructors ----------------------------------------------------------- /** - * Creates a FilterMultiJoinMergeRule. + * Creates a FilterMultiJoinMergeRule that uses {@link Filter} + * of type {@link LogicalFilter} + * @param relBuilderFactory builder factory for relational expressions */ public FilterMultiJoinMergeRule(RelBuilderFactory relBuilderFactory) { + this(LogicalFilter.class, relBuilderFactory); + } + + /** + * Creates a FilterMultiJoinMergeRule that uses a generic + * {@link Filter} + * @param filterClass filter class + * @param relBuilderFactory builder factory for relational expressions + */ + public FilterMultiJoinMergeRule(Class<? extends Filter> filterClass, + RelBuilderFactory relBuilderFactory) { super( - operand(LogicalFilter.class, - operand(MultiJoin.class, any())), - relBuilderFactory, null); + operand(filterClass, + operand(MultiJoin.class, any())), + relBuilderFactory, null); } //~ Methods ---------------------------------------------------------------- public void onMatch(RelOptRuleCall call) { - LogicalFilter filter = call.rel(0); + Filter filter = call.rel(0); MultiJoin multiJoin = call.rel(1); // Create a new post-join filter condition diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinProjectTransposeRule.java index 8579816..d87a600 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinProjectTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinProjectTransposeRule.java @@ -20,6 +20,7 @@ import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.plan.RelOptRuleOperand; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Project; import org.apache.calcite.rel.core.RelFactories; import org.apache.calcite.rel.logical.LogicalJoin; import org.apache.calcite.rel.logical.LogicalProject; @@ -117,7 +118,7 @@ public class MultiJoinProjectTransposeRule extends JoinProjectTransposeRule { } // override JoinProjectTransposeRule - protected LogicalProject getRightChild(RelOptRuleCall call) { + protected Project getRightChild(RelOptRuleCall call) { if (call.rels.length == 4) { return call.rel(2); } else { @@ -128,7 +129,7 @@ public class MultiJoinProjectTransposeRule extends JoinProjectTransposeRule { // override JoinProjectTransposeRule protected RelNode getProjectChild( RelOptRuleCall call, - LogicalProject project, + Project project, boolean leftChild) { // locate the appropriate MultiJoin based on which rule was fired // and which projection we're dealing with diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java index 95a568f..a36c1f6 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java @@ -19,6 +19,7 @@ package org.apache.calcite.rel.rules; import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.plan.RelOptUtil; +import org.apache.calcite.rel.core.Project; import org.apache.calcite.rel.core.RelFactories; import org.apache.calcite.rel.logical.LogicalProject; import org.apache.calcite.tools.RelBuilder; @@ -38,17 +39,34 @@ public class ProjectMultiJoinMergeRule extends RelOptRule { //~ Constructors ----------------------------------------------------------- - /** Creates a ProjectMultiJoinMergeRule. */ + /** + * Creates a ProjectMultiJoinMergeRule that uses {@link Project} + * of type {@link LogicalProject} + * @param relBuilderFactory builder factory for relational expressions + */ public ProjectMultiJoinMergeRule(RelBuilderFactory relBuilderFactory) { super( operand(LogicalProject.class, operand(MultiJoin.class, any())), relBuilderFactory, null); } + /** + * Creates a ProjectMultiJoinMergeRule that uses a generic + * {@link Project} + * @param projectClass project class + * @param relBuilderFactory builder factory for relational expressions + */ + public ProjectMultiJoinMergeRule(Class<? extends Project> projectClass, + RelBuilderFactory relBuilderFactory) { + super( + operand(projectClass, + operand(MultiJoin.class, any())), relBuilderFactory, null); + } + //~ Methods ---------------------------------------------------------------- public void onMatch(RelOptRuleCall call) { - LogicalProject project = call.rel(0); + Project project = call.rel(0); MultiJoin multiJoin = call.rel(1); // if all inputs have their projFields set, then projection information diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java index 6e1db91..7668ee8 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -987,9 +987,9 @@ public class SqlToRelConverter { return; } - final RelFactories.FilterFactory factory = + final RelFactories.FilterFactory filterFactory = RelFactories.DEFAULT_FILTER_FACTORY; - final RelNode filter = factory.createFilter(bb.root, convertedWhere2); + final RelNode filter = filterFactory.createFilter(bb.root, convertedWhere2); final RelNode r; final CorrelationUse p = getCorrelationUse(bb, filter); if (p != null) { 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 38719ce..1882ef6 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -21,10 +21,12 @@ import org.apache.calcite.adapter.enumerable.EnumerableRules; import org.apache.calcite.config.CalciteConnectionConfigImpl; import org.apache.calcite.plan.Context; import org.apache.calcite.plan.Contexts; +import org.apache.calcite.plan.Convention; import org.apache.calcite.plan.ConventionTraitDef; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.plan.RelOptRule; +import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.RelTraitDef; import org.apache.calcite.plan.RelTraitSet; @@ -37,6 +39,7 @@ import org.apache.calcite.prepare.Prepare; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelCollationTraitDef; import org.apache.calcite.rel.RelCollations; +import org.apache.calcite.rel.RelDistributionTraitDef; import org.apache.calcite.rel.RelDistributions; import org.apache.calcite.rel.RelFieldCollation; import org.apache.calcite.rel.RelNode; @@ -52,13 +55,17 @@ import org.apache.calcite.rel.core.RelFactories; import org.apache.calcite.rel.core.Union; import org.apache.calcite.rel.logical.LogicalAggregate; import org.apache.calcite.rel.logical.LogicalCorrelate; +import org.apache.calcite.rel.logical.LogicalFilter; import org.apache.calcite.rel.logical.LogicalProject; import org.apache.calcite.rel.logical.LogicalTableModify; import org.apache.calcite.rel.logical.LogicalTableScan; import org.apache.calcite.rel.metadata.CachingRelMetadataProvider; import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider; import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider; +import org.apache.calcite.rel.metadata.RelMdCollation; +import org.apache.calcite.rel.metadata.RelMdDistribution; import org.apache.calcite.rel.metadata.RelMetadataProvider; +import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rel.rules.AggregateExpandDistinctAggregatesRule; import org.apache.calcite.rel.rules.AggregateExtractProjectRule; import org.apache.calcite.rel.rules.AggregateFilterTransposeRule; @@ -128,6 +135,7 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexCall; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.rex.RexUtil; import org.apache.calcite.runtime.Hook; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlNode; @@ -136,12 +144,15 @@ import org.apache.calcite.sql.SqlSpecialOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.sql.validate.SqlValidator; +import org.apache.calcite.sql.validate.SqlValidatorUtil; import org.apache.calcite.sql2rel.SqlToRelConverter; import org.apache.calcite.test.catalog.MockCatalogReader; import org.apache.calcite.tools.Program; import org.apache.calcite.tools.Programs; import org.apache.calcite.tools.RelBuilder; +import org.apache.calcite.tools.RelBuilderFactory; import org.apache.calcite.tools.RuleSet; import org.apache.calcite.tools.RuleSets; import org.apache.calcite.util.ImmutableBitSet; @@ -1907,11 +1918,11 @@ public class RelOptRulesTest extends RelOptTestBase { .addMatchOrder(HepMatchOrder.BOTTOM_UP) .addRuleInstance(JoinToMultiJoinRule.INSTANCE) .addRuleCollection( - Arrays.asList(FilterMultiJoinMergeRule.INSTANCE, ProjectMultiJoinMergeRule.INSTANCE)) + Arrays.asList(FilterMultiJoinMergeRule.INSTANCE, ProjectMultiJoinMergeRule.INSTANCE)) .build(); checkPlanning(program, "select * from (select * from emp e1 left outer join dept d on e1.deptno = d.deptno " - + "where d.deptno > 3) where ename LIKE 'bar'"); + + "where d.deptno > 3) where ename LIKE 'bar'"); } @Test public void testReduceConstants() throws Exception { @@ -5404,12 +5415,12 @@ public class RelOptRulesTest extends RelOptTestBase { RelNode root = builder .scan("EMP") .filter( - builder.call(SqlStdOperatorTable.EQUALS, - builder.field("EMPNO"), builder.literal(10))) + builder.call(SqlStdOperatorTable.EQUALS, + builder.field("EMPNO"), builder.literal(10))) .exchange(RelDistributions.hash(ImmutableList.of(0))) .project(builder.field(0), builder.field(1)) .sortExchange(RelDistributions.hash(ImmutableList.of(0, 1)), - RelCollations.of(new RelFieldCollation(0), new RelFieldCollation(1))) + RelCollations.of(new RelFieldCollation(0), new RelFieldCollation(1))) .build(); HepProgram preProgram = new HepProgramBuilder().build(); @@ -5553,6 +5564,155 @@ public class RelOptRulesTest extends RelOptTestBase { String planAfter = NL + RelOptUtil.toString(relAfter); getDiffRepos().assertEquals("planAfter", "${planAfter}", planAfter); } + + @Test public void testFilterAndProjectWithMultiJoin() throws Exception { + final Tester tester = new TesterImpl(getDiffRepos(), + false, false, true, false, + null, null, SqlToRelConverter.Config.DEFAULT, + SqlConformanceEnum.DEFAULT, Contexts.empty()); + + final HepProgram preProgram = new HepProgramBuilder() + .addMatchOrder(HepMatchOrder.BOTTOM_UP) + .addRuleCollection(Arrays.asList(MyFilterRule.INSTANCE, MyProjectRule.INSTANCE)) + .build(); + + final FilterMultiJoinMergeRule filterMultiJoinMergeRule = + new FilterMultiJoinMergeRule(MyFilter.class, RelFactories.LOGICAL_BUILDER); + + final ProjectMultiJoinMergeRule projectMultiJoinMergeRule = + new ProjectMultiJoinMergeRule(MyProject.class, RelFactories.LOGICAL_BUILDER); + + HepProgram program = new HepProgramBuilder() + .addMatchOrder(HepMatchOrder.BOTTOM_UP) + .addRuleInstance(JoinToMultiJoinRule.INSTANCE) + .addRuleCollection(Arrays.asList(filterMultiJoinMergeRule, projectMultiJoinMergeRule)) + .build(); + + checkPlanning(tester, preProgram, new HepPlanner(program), + "select * from (select * from emp e1 left outer join dept d on e1.deptno = d.deptno " + + "where d.deptno > 3)", false); + } +} + +/** + * Custom implementation of {@link Filter} for use + * in test case to verify that {@link FilterMultiJoinMergeRule} + * can be created with any {@link Filter} and not limited to + * {@link org.apache.calcite.rel.logical.LogicalFilter} + */ +class MyFilter extends Filter { + + MyFilter( + RelOptCluster cluster, + RelTraitSet traitSet, + RelNode child, + RexNode condition) { + super(cluster, traitSet, child, condition); + } + + public MyFilter copy(RelTraitSet traitSet, RelNode input, + RexNode condition) { + return new MyFilter(getCluster(), traitSet, input, condition); + } + + /** Creates a MyFilter. */ + public static MyFilter create(final RelNode input, RexNode condition) { + final RelOptCluster cluster = input.getCluster(); + final RelMetadataQuery mq = cluster.getMetadataQuery(); + final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE) + .replaceIfs(RelCollationTraitDef.INSTANCE, + () -> RelMdCollation.filter(mq, input)) + .replaceIf(RelDistributionTraitDef.INSTANCE, + () -> RelMdDistribution.filter(mq, input)); + return new MyFilter(cluster, traitSet, input, condition); + } +} + +/** + * Rule to transform {@link LogicalFilter} into + * custom MyFilter + */ +class MyFilterRule extends RelOptRule { + static final MyFilterRule INSTANCE = + new MyFilterRule(LogicalFilter.class, RelFactories.LOGICAL_BUILDER); + + private MyFilterRule(Class<? extends Filter> clazz, + RelBuilderFactory relBuilderFactory) { + super(RelOptRule.operand(clazz, RelOptRule.any()), relBuilderFactory, null); + } + + @Override public void onMatch(RelOptRuleCall call) { + final LogicalFilter logicalFilter = call.rel(0); + final MyFilter myFilter = MyFilter.create(logicalFilter.getInput(), + logicalFilter.getCondition()); + call.transformTo(myFilter); + } +} + +/** + * Custom implementation of {@link Project} for use + * in test case to verify that {@link ProjectMultiJoinMergeRule} + * can be created with any {@link Project} and not limited to + * {@link org.apache.calcite.rel.logical.LogicalProject} + */ +class MyProject extends Project { + MyProject( + RelOptCluster cluster, + RelTraitSet traitSet, + RelNode input, + List<? extends RexNode> projects, + RelDataType rowType) { + super(cluster, traitSet, input, projects, rowType); + } + + public MyProject copy(RelTraitSet traitSet, RelNode input, + List<RexNode> projects, RelDataType rowType) { + return new MyProject(getCluster(), traitSet, input, projects, rowType); + } + + /** Creates a MyProject. */ + public static MyProject create(final RelNode input, + final List<? extends RexNode> projects, List<String> fieldNames) { + final RelOptCluster cluster = input.getCluster(); + final RelDataType rowType = + RexUtil.createStructType(cluster.getTypeFactory(), projects, + fieldNames, SqlValidatorUtil.F_SUGGESTER); + return create(input, projects, rowType); + } + + /** Creates a MyProject. */ + public static MyProject create(final RelNode input, + final List<? extends RexNode> projects, RelDataType rowType) { + final RelOptCluster cluster = input.getCluster(); + final RelMetadataQuery mq = cluster.getMetadataQuery(); + final RelTraitSet traitSet = + cluster.traitSet().replace(Convention.NONE) + .replaceIfs(RelCollationTraitDef.INSTANCE, + () -> RelMdCollation.project(mq, input, projects)); + return new MyProject(cluster, traitSet, input, projects, rowType); + } +} + +/** + * Rule to transform {@link LogicalProject} into custom + * MyProject + */ +class MyProjectRule extends RelOptRule { + static final MyProjectRule INSTANCE = + new MyProjectRule(LogicalProject.class, RelFactories.LOGICAL_BUILDER); + + private MyProjectRule(Class<? extends Project> clazz, + RelBuilderFactory relBuilderFactory) { + super(RelOptRule.operand(clazz, RelOptRule.any()), relBuilderFactory, null); + } + + @Override public void onMatch(RelOptRuleCall call) { + final LogicalProject logicalProject = call.rel(0); + final MyProject myProject = MyProject.create(logicalProject.getInput(), + logicalProject.getChildExps(), logicalProject.getRowType()); + call.transformTo(myProject); + } } + // End RelOptRulesTest.java 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 21f99d2..97ee0b9 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -10976,4 +10976,25 @@ EnumerableUnion(all=[true]) ]]> </Resource> </TestCase> + <TestCase name="testFilterAndProjectWithMultiJoin"> + <Resource name="sql"> + <![CDATA[select * from (select * from emp e1 left outer join dept d on e1.deptno = d.deptno where d.deptno > 3) where ename LIKE 'bar']]> + </Resource> + <Resource name="planBefore"> + <![CDATA[ +MyProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10]) + MyFilter(condition=[>($9, 3)]) + LogicalJoin(condition=[=($7, $9)], joinType=[left]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) +]]> + </Resource> + <Resource name="planAfter"> + <![CDATA[ +MultiJoin(joinFilter=[true], isFullOuterJoin=[false], joinTypes=[[INNER, LEFT]], outerJoinConditions=[[NULL, =($7, $9)]], projFields=[[{0, 1, 2, 3, 4, 5, 6, 7, 8}, {0, 1}]], postJoinFilter=[>($9, 3)]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + LogicalTableScan(table=[[CATALOG, SALES, DEPT]]) +]]> + </Resource> + </TestCase> </Root>