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 <[email protected]>
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>