You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by jc...@apache.org on 2015/10/09 10:02:35 UTC
hive git commit: HIVE-11976: Extend CBO rules to being able to apply
rules only once on a given operator (Jesus Camacho Rodriguez,
reviewed by Laljo John Pullokkaran)
Repository: hive
Updated Branches:
refs/heads/master aded0d32d -> 5201f188b
HIVE-11976: Extend CBO rules to being able to apply rules only once on a given operator (Jesus Camacho Rodriguez, reviewed by Laljo John Pullokkaran)
Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/5201f188
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/5201f188
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/5201f188
Branch: refs/heads/master
Commit: 5201f188bc6c808c2a9f3d100118340af3ebd7c4
Parents: aded0d3
Author: Jesus Camacho Rodriguez <jc...@apache.org>
Authored: Mon Oct 5 12:12:39 2015 +0100
Committer: Jesus Camacho Rodriguez <jc...@apache.org>
Committed: Fri Oct 9 09:01:48 2015 +0100
----------------------------------------------------------------------
.../ql/optimizer/calcite/HiveConfigContext.java | 37 ----
.../calcite/HiveHepPlannerContext.java | 37 ++++
.../calcite/HiveVolcanoPlannerContext.java | 37 ++++
.../calcite/cost/HiveVolcanoPlanner.java | 6 +-
.../calcite/rules/HivePreFilteringRule.java | 42 ++++-
.../calcite/rules/HiveRulesRegistry.java | 44 +++++
.../hadoop/hive/ql/parse/CalcitePlanner.java | 11 +-
.../calcite/TestCBORuleFiredOnlyOnce.java | 168 +++++++++++++++++++
8 files changed, 332 insertions(+), 50 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveConfigContext.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveConfigContext.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveConfigContext.java
deleted file mode 100644
index 0e559e0..0000000
--- a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveConfigContext.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * 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.hadoop.hive.ql.optimizer.calcite;
-
-import org.apache.calcite.plan.Context;
-import org.apache.hadoop.hive.ql.optimizer.calcite.cost.HiveAlgorithmsConf;
-
-
-public class HiveConfigContext implements Context {
- private HiveAlgorithmsConf config;
-
- public HiveConfigContext(HiveAlgorithmsConf config) {
- this.config = config;
- }
-
- public <T> T unwrap(Class<T> clazz) {
- if (clazz.isInstance(config)) {
- return clazz.cast(config);
- }
- return null;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveHepPlannerContext.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveHepPlannerContext.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveHepPlannerContext.java
new file mode 100644
index 0000000..ad79aee
--- /dev/null
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveHepPlannerContext.java
@@ -0,0 +1,37 @@
+/**
+ * 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.hadoop.hive.ql.optimizer.calcite;
+
+import org.apache.calcite.plan.Context;
+import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HiveRulesRegistry;
+
+
+public class HiveHepPlannerContext implements Context {
+ private HiveRulesRegistry registry;
+
+ public HiveHepPlannerContext(HiveRulesRegistry registry) {
+ this.registry = registry;
+ }
+
+ public <T> T unwrap(Class<T> clazz) {
+ if (clazz.isInstance(registry)) {
+ return clazz.cast(registry);
+ }
+ return null;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveVolcanoPlannerContext.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveVolcanoPlannerContext.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveVolcanoPlannerContext.java
new file mode 100644
index 0000000..8859fc2
--- /dev/null
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/HiveVolcanoPlannerContext.java
@@ -0,0 +1,37 @@
+/**
+ * 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.hadoop.hive.ql.optimizer.calcite;
+
+import org.apache.calcite.plan.Context;
+import org.apache.hadoop.hive.ql.optimizer.calcite.cost.HiveAlgorithmsConf;
+
+
+public class HiveVolcanoPlannerContext implements Context {
+ private HiveAlgorithmsConf config;
+
+ public HiveVolcanoPlannerContext(HiveAlgorithmsConf config) {
+ this.config = config;
+ }
+
+ public <T> T unwrap(Class<T> clazz) {
+ if (clazz.isInstance(config)) {
+ return clazz.cast(config);
+ }
+ return null;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/cost/HiveVolcanoPlanner.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/cost/HiveVolcanoPlanner.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/cost/HiveVolcanoPlanner.java
index a39ded2..8610edc 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/cost/HiveVolcanoPlanner.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/cost/HiveVolcanoPlanner.java
@@ -22,7 +22,7 @@ import org.apache.calcite.plan.ConventionTraitDef;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.volcano.VolcanoPlanner;
import org.apache.calcite.rel.RelCollationTraitDef;
-import org.apache.hadoop.hive.ql.optimizer.calcite.HiveConfigContext;
+import org.apache.hadoop.hive.ql.optimizer.calcite.HiveVolcanoPlannerContext;
/**
* Refinement of {@link org.apache.calcite.plan.volcano.VolcanoPlanner} for Hive.
@@ -35,11 +35,11 @@ public class HiveVolcanoPlanner extends VolcanoPlanner {
private static final boolean ENABLE_COLLATION_TRAIT = true;
/** Creates a HiveVolcanoPlanner. */
- public HiveVolcanoPlanner(HiveConfigContext conf) {
+ public HiveVolcanoPlanner(HiveVolcanoPlannerContext conf) {
super(HiveCost.FACTORY, conf);
}
- public static RelOptPlanner createPlanner(HiveConfigContext conf) {
+ public static RelOptPlanner createPlanner(HiveVolcanoPlannerContext conf) {
final VolcanoPlanner planner = new HiveVolcanoPlanner(conf);
planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
if (ENABLE_COLLATION_TRAIT) {
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
index 3e2311c..349c7f8 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HivePreFilteringRule.java
@@ -76,14 +76,38 @@ public class HivePreFilteringRule extends RelOptRule {
this.filterFactory = HiveFilter.DEFAULT_FILTER_FACTORY;
}
- public void onMatch(RelOptRuleCall call) {
+ @Override
+ public boolean matches(RelOptRuleCall call) {
final Filter filter = call.rel(0);
final RelNode filterChild = call.rel(1);
- // 0. If the filter is already on top of a TableScan,
- // we can bail out
+ // If the filter is already on top of a TableScan,
+ // we can bail out
if (filterChild instanceof TableScan) {
- return;
+ return false;
+ }
+
+ HiveRulesRegistry registry = call.getPlanner().
+ getContext().unwrap(HiveRulesRegistry.class);
+
+ // If this operator has been visited already by the rule,
+ // we do not need to apply the optimization
+ if (registry != null && registry.getVisited(this).contains(filter)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onMatch(RelOptRuleCall call) {
+ final Filter filter = call.rel(0);
+
+ // 0. Register that we have visited this operator in this rule
+ HiveRulesRegistry registry = call.getPlanner().
+ getContext().unwrap(HiveRulesRegistry.class);
+ if (registry != null) {
+ registry.registerVisited(this, filter);
}
final RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
@@ -114,7 +138,7 @@ public class HivePreFilteringRule extends RelOptRule {
}
// 3. If the new conjuncts are already present in the plan, we bail out
- final RelOptPredicateList predicates = RelMetadataQuery.getPulledUpPredicates(filter);
+ final RelOptPredicateList predicates = RelMetadataQuery.getPulledUpPredicates(filter.getInput());
final List<RexNode> newConjuncts = new ArrayList<>();
for (RexNode commonOperand : commonOperands) {
boolean found = false;
@@ -137,9 +161,15 @@ public class HivePreFilteringRule extends RelOptRule {
RexUtil.composeConjunction(rexBuilder, newConjuncts, false));
// 5. We create the new filter that might be pushed down
- RelNode newFilter = filterFactory.createFilter(filterChild, newCondition);
+ RelNode newFilter = filterFactory.createFilter(filter.getInput(), newCondition);
RelNode newTopFilter = filterFactory.createFilter(newFilter, condition);
+ // 6. We register both so we do not fire the rule on them again
+ if (registry != null) {
+ registry.registerVisited(this, newFilter);
+ registry.registerVisited(this, newTopFilter);
+ }
+
call.transformTo(newTopFilter);
}
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRulesRegistry.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRulesRegistry.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRulesRegistry.java
new file mode 100644
index 0000000..18a065e
--- /dev/null
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveRulesRegistry.java
@@ -0,0 +1,44 @@
+/**
+ * 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.hadoop.hive.ql.optimizer.calcite.rules;
+
+import java.util.Set;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.rel.RelNode;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+
+public class HiveRulesRegistry {
+
+ private SetMultimap<RelOptRule, RelNode> registry;
+
+ public HiveRulesRegistry() {
+ this.registry = HashMultimap.create();
+ }
+
+ public void registerVisited(RelOptRule rule, RelNode operator) {
+ this.registry.put(rule, operator);
+ }
+
+ public Set<RelNode> getVisited(RelOptRule rule) {
+ return this.registry.get(rule);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
index e68b385..61ee2bd 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java
@@ -63,7 +63,6 @@ import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.metadata.CachingRelMetadataProvider;
import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
import org.apache.calcite.rel.metadata.RelMetadataProvider;
-import org.apache.calcite.rel.rules.AggregateJoinTransposeRule;
import org.apache.calcite.rel.rules.FilterAggregateTransposeRule;
import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
import org.apache.calcite.rel.rules.JoinToMultiJoinRule;
@@ -118,9 +117,10 @@ import org.apache.hadoop.hive.ql.metadata.VirtualColumn;
import org.apache.hadoop.hive.ql.optimizer.calcite.CalciteSemanticException;
import org.apache.hadoop.hive.ql.optimizer.calcite.CalciteSemanticException.UnsupportedFeature;
import org.apache.hadoop.hive.ql.optimizer.calcite.HiveCalciteUtil;
-import org.apache.hadoop.hive.ql.optimizer.calcite.HiveConfigContext;
import org.apache.hadoop.hive.ql.optimizer.calcite.HiveDefaultRelMetadataProvider;
+import org.apache.hadoop.hive.ql.optimizer.calcite.HiveHepPlannerContext;
import org.apache.hadoop.hive.ql.optimizer.calcite.HiveTypeSystemImpl;
+import org.apache.hadoop.hive.ql.optimizer.calcite.HiveVolcanoPlannerContext;
import org.apache.hadoop.hive.ql.optimizer.calcite.RelOptHiveTable;
import org.apache.hadoop.hive.ql.optimizer.calcite.TraitsUtil;
import org.apache.hadoop.hive.ql.optimizer.calcite.cost.HiveAlgorithmsConf;
@@ -151,6 +151,7 @@ import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HivePartitionPruneRule;
import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HivePreFilteringRule;
import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HiveProjectMergeRule;
import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HiveRelFieldTrimmer;
+import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HiveRulesRegistry;
import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HiveWindowingFixRule;
import org.apache.hadoop.hive.ql.optimizer.calcite.translator.ASTConverter;
import org.apache.hadoop.hive.ql.optimizer.calcite.translator.HiveOpConverter;
@@ -841,7 +842,7 @@ public class CalcitePlanner extends SemanticAnalyzer {
final Double maxMemory = (double) HiveConf.getLongVar(
conf, HiveConf.ConfVars.HIVECONVERTJOINNOCONDITIONALTASKTHRESHOLD);
HiveAlgorithmsConf algorithmsConf = new HiveAlgorithmsConf(maxSplitSize, maxMemory);
- HiveConfigContext confContext = new HiveConfigContext(algorithmsConf);
+ HiveVolcanoPlannerContext confContext = new HiveVolcanoPlannerContext(algorithmsConf);
RelOptPlanner planner = HiveVolcanoPlanner.createPlanner(confContext);
final RelOptQuery query = new RelOptQuery(planner);
final RexBuilder rexBuilder = cluster.getRexBuilder();
@@ -1061,7 +1062,9 @@ public class CalcitePlanner extends SemanticAnalyzer {
programBuilder.addRuleInstance(r);
}
- HepPlanner planner = new HepPlanner(programBuilder.build());
+ HiveRulesRegistry registry = new HiveRulesRegistry();
+ HiveHepPlannerContext context = new HiveHepPlannerContext(registry);
+ HepPlanner planner = new HepPlanner(programBuilder.build(), context);
List<RelMetadataProvider> list = Lists.newArrayList();
list.add(mdProvider);
planner.registerMetadataProviders(list);
http://git-wip-us.apache.org/repos/asf/hive/blob/5201f188/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBORuleFiredOnlyOnce.java
----------------------------------------------------------------------
diff --git a/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBORuleFiredOnlyOnce.java b/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBORuleFiredOnlyOnce.java
new file mode 100644
index 0000000..f1d8d1d
--- /dev/null
+++ b/ql/src/test/org/apache/hadoop/hive/ql/optimizer/calcite/TestCBORuleFiredOnlyOnce.java
@@ -0,0 +1,168 @@
+/**
+ * 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.hadoop.hive.ql.optimizer.calcite;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.plan.hep.HepMatchOrder;
+import org.apache.calcite.plan.hep.HepPlanner;
+import org.apache.calcite.plan.hep.HepProgramBuilder;
+import org.apache.calcite.rel.AbstractRelNode;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.metadata.CachingRelMetadataProvider;
+import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
+import org.apache.calcite.rel.metadata.RelMetadataProvider;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelRecordType;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.ql.optimizer.calcite.rules.HiveRulesRegistry;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class TestCBORuleFiredOnlyOnce {
+
+
+ @Test
+ public void testRuleFiredOnlyOnce() {
+
+ HiveConf conf = new HiveConf();
+
+ // Create HepPlanner
+ HepProgramBuilder programBuilder = new HepProgramBuilder();
+ programBuilder.addMatchOrder(HepMatchOrder.TOP_DOWN);
+ programBuilder = programBuilder.addRuleCollection(
+ ImmutableList.<RelOptRule>of(DummyRule.INSTANCE));
+
+ // Create rules registry to not trigger a rule more than once
+ HiveRulesRegistry registry = new HiveRulesRegistry();
+ HiveHepPlannerContext context = new HiveHepPlannerContext(registry);
+ HepPlanner planner = new HepPlanner(programBuilder.build(), context);
+
+ // Cluster
+ RexBuilder rexBuilder = new RexBuilder(new JavaTypeFactoryImpl());
+ RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder);
+
+ // Create MD provider
+ HiveDefaultRelMetadataProvider mdProvider = new HiveDefaultRelMetadataProvider(conf);
+ List<RelMetadataProvider> list = Lists.newArrayList();
+ list.add(mdProvider.getMetadataProvider());
+ planner.registerMetadataProviders(list);
+ RelMetadataProvider chainedProvider = ChainedRelMetadataProvider.of(list);
+
+ final RelNode node = new DummyNode(cluster, cluster.traitSet());
+
+ node.getCluster().setMetadataProvider(
+ new CachingRelMetadataProvider(chainedProvider, planner));
+
+ planner.setRoot(node);
+
+ planner.findBestExp();
+
+ // Matches 3 times: 2 times the original node, 1 time the new node created by the rule
+ assertEquals(3, DummyRule.INSTANCE.numberMatches);
+ // It is fired only once: on the original node
+ assertEquals(1, DummyRule.INSTANCE.numberOnMatch);
+ }
+
+ public static class DummyRule extends RelOptRule {
+
+ public static final DummyRule INSTANCE =
+ new DummyRule();
+
+ public int numberMatches;
+ public int numberOnMatch;
+
+ private DummyRule() {
+ super(operand(RelNode.class, any()));
+ numberMatches = 0;
+ numberOnMatch = 0;
+ }
+
+ @Override
+ public boolean matches(RelOptRuleCall call) {
+ final RelNode node = call.rel(0);
+
+ numberMatches++;
+
+ HiveRulesRegistry registry = call.getPlanner().
+ getContext().unwrap(HiveRulesRegistry.class);
+
+ // If this operator has been visited already by the rule,
+ // we do not need to apply the optimization
+ if (registry != null && registry.getVisited(this).contains(node)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onMatch(RelOptRuleCall call) {
+ final RelNode node = call.rel(0);
+
+ numberOnMatch++;
+
+ // If we have fired it already once, we return and the test will fail
+ if (numberOnMatch > 1) {
+ return;
+ }
+
+ // Register that we have visited this operator in this rule
+ HiveRulesRegistry registry = call.getPlanner().
+ getContext().unwrap(HiveRulesRegistry.class);
+ if (registry != null) {
+ registry.registerVisited(this, node);
+ }
+
+ // We create a new op if it is the first time we fire the rule
+ final RelNode newNode = new DummyNode(node.getCluster(), node.getTraitSet());
+ // We register it so we do not fire the rule on it again
+ if (registry != null) {
+ registry.registerVisited(this, newNode);
+ }
+
+ call.transformTo(newNode);
+
+ }
+ }
+
+ public static class DummyNode extends AbstractRelNode {
+
+ protected DummyNode(RelOptCluster cluster, RelTraitSet traits) {
+ super(cluster, cluster.traitSet());
+ }
+
+ @Override
+ protected RelDataType deriveRowType() {
+ return new RelRecordType(Lists.<RelDataTypeField>newArrayList());
+ }
+ }
+
+
+}