This is an automated email from the ASF dual-hosted git repository. joemcdonnell pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/impala.git
commit 3cf05fe21ae3a79ec682a41c1cb4341c980a4aa4 Author: Steve Carlin <[email protected]> AuthorDate: Wed Oct 16 10:46:03 2024 -0700 IMPALA-13461: Added rules to make tpcds queries work. Among the rules that were added: The "Minus" RelNode is not handled directly by the physical node translator and is changed into other nodes that are handled. This was added in the ImpalaMinusToDistinctRule The ExtractLiteralAgg rule compensates for the fact that a literal value cannot be used directly in an agg. The CalciteRelNodeConverter handles breaking down a SubQuery RelNode into simpler RelNodes that can be optimized. The pom.xml file was also changed. There is a java bug in java 8 that causes incremental compiles to fail. So we do a full compile for the Calcite planner now. Change-Id: I03a38aaa5c413b9b4d2f4c179de07935b672a031 Reviewed-on: http://gerrit.cloudera.org:8080/21941 Tested-by: Impala Public Jenkins <[email protected]> Reviewed-by: Joe McDonnell <[email protected]> --- java/calcite-planner/pom.xml | 17 ++ .../impala/calcite/rules/ExtractLiteralAgg.java | 86 +++++++++ .../calcite/rules/ImpalaMinusToDistinctRule.java | 214 +++++++++++++++++++++ .../impala/calcite/service/CalciteOptimizer.java | 44 ++++- .../calcite/service/CalciteRelNodeConverter.java | 47 ++++- 5 files changed, 400 insertions(+), 8 deletions(-) diff --git a/java/calcite-planner/pom.xml b/java/calcite-planner/pom.xml index 2ccd519f7..e00925046 100644 --- a/java/calcite-planner/pom.xml +++ b/java/calcite-planner/pom.xml @@ -48,6 +48,11 @@ under the License. <artifactId>avatica-core</artifactId> <version>1.23.0</version> </dependency> + <dependency> + <groupId>org.immutables</groupId> + <artifactId>value</artifactId> + <version>2.10.1</version> + </dependency> </dependencies> <build> @@ -60,6 +65,18 @@ under the License. </plugins> </pluginManagement> <plugins> + <plugin> + <artifactId>maven-clean-plugin</artifactId> + <executions> + <execution> + <id>auto-clean</id> + <phase>initialize</phase> + <goals> + <goal>clean</goal> + </goals> + </execution> + </executions> + </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/rules/ExtractLiteralAgg.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rules/ExtractLiteralAgg.java new file mode 100644 index 000000000..ed1c9cfe9 --- /dev/null +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rules/ExtractLiteralAgg.java @@ -0,0 +1,86 @@ +// 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.impala.calcite.rules; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import org.apache.calcite.plan.RelOptRule; +import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.AggregateCall; +import org.apache.calcite.rel.logical.LogicalAggregate; +import org.apache.calcite.rel.logical.LogicalProject; +import org.apache.calcite.rex.RexBuilder; +import org.apache.calcite.rex.RexLiteral; +import org.apache.calcite.rex.RexNode; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +/** + * ExtractLiteralAgg rule gets rid of the LITERAL_AGG into + * something Impala can handle. + * + * Calcite can create an Aggregate RelNode with a single LITERAL_AGG AggregateCall + * to check for existence in a subquery. In this case, the Aggregate will do a group + * by, and just spit out a literal value for each row. + * The LITERAL_AGG function does not exist in Impala. In order to create a RelNode + * structure that Impala supports, this Aggregate is turned into an Aggregate that + * still does the "group by" on the relevant groups, but places a Project RelNode + * on top that returns the literal value. + */ +public class ExtractLiteralAgg extends RelOptRule { + + public ExtractLiteralAgg() { + super(operand(LogicalAggregate.class, none())); + } + + @Override + public void onMatch(RelOptRuleCall call) { + final LogicalAggregate aggregate = call.rel(0); + + if (hasSingleLiteralAgg(aggregate.getAggCallList())) { + LogicalAggregate newAggregate = LogicalAggregate.create(aggregate.getInput(), + aggregate.getHints(), aggregate.getGroupSet(), aggregate.getGroupSets(), + new ArrayList<>()); + RexBuilder rexBuilder = aggregate.getCluster().getRexBuilder(); + List<RexNode> projects = new ArrayList<>(); + // Because we know there is only one agg call and its a literal agg, the + // first n - 1 columns are not changed in the newly created project. + for (int i = 0; i < aggregate.getRowType().getFieldCount() - 1; ++i) { + projects.add(rexBuilder.makeInputRef(aggregate, i)); + } + AggregateCall aggCall = aggregate.getAggCallList().get(0); + Preconditions.checkState(aggCall.rexList.get(0) instanceof RexLiteral); + // Add the nth column as the LITERAL_AGG value + projects.add(aggCall.rexList.get(0)); + RelNode projectRelNode = LogicalProject.create(newAggregate, new ArrayList<>(), + projects, aggregate.getRowType().getFieldNames(), new HashSet<>()); + call.transformTo(projectRelNode); + } + } + + private boolean hasSingleLiteralAgg(List<AggregateCall> aggCallList) { + if (aggCallList.size() != 1) { + return false; + } + AggregateCall aggCall = aggCallList.get(0); + return (aggCall.getAggregation().getName().equals("LITERAL_AGG")); + } +} diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/rules/ImpalaMinusToDistinctRule.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rules/ImpalaMinusToDistinctRule.java new file mode 100644 index 000000000..71702b73f --- /dev/null +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/rules/ImpalaMinusToDistinctRule.java @@ -0,0 +1,214 @@ +// 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.impala.calcite.rules; + +import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelOptRule; +import org.apache.calcite.plan.RelOptRuleCall; +import org.apache.calcite.plan.RelRule; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Minus; +import org.apache.calcite.rel.rules.TransformationRule; +import org.apache.calcite.rel.logical.LogicalMinus; +import org.apache.calcite.rex.RexBuilder; +import org.apache.calcite.rex.RexNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.tools.RelBuilder; +import org.apache.calcite.tools.RelBuilderFactory; +import org.apache.calcite.util.ImmutableBitSet; +import org.apache.calcite.util.Util; +import org.apache.impala.calcite.operators.ImpalaOperator; + +import com.google.common.collect.ImmutableList; + +import org.immutables.value.Value; + +import java.math.BigDecimal; + +/** + * ImpalaMinusToDistinctRule is the same as Calcite's MinustToDistinctRule with some + * changes. The Calcite code was grabbed from here: + * https://github.com/apache/calcite/blob/calcite-1.36.0/core/src/main/java/org/apache/... + * /calcite/rel/rules/MinusToDistinctRule.java + * + * The Calcite code generates a "count() filter" operation which is not supported by + * Impala. A couple of small changes were made in the creation of the RelNodes which + * can be found in this file by searching for 'IMPALA CHANGE' in multiple places. + */ + +/** + * Planner rule that translates a distinct + * {@link org.apache.calcite.rel.core.Minus} + * (<code>all</code> = <code>false</code>) + * into a group of operators composed of + * {@link org.apache.calcite.rel.core.Union}, + * {@link org.apache.calcite.rel.core.Aggregate}, + * {@link org.apache.calcite.rel.core.Filter},etc. + * + * <p>For example, the query plan + + * <blockquote><pre>{@code + * LogicalMinus(all=[false]) + * LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2]) + * LogicalFilter(condition=[=($7, 10)]) + * LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + * LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2]) + * LogicalFilter(condition=[=($7, 20)]) + * LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + * }</pre></blockquote> + * + * <p> will convert to + * + * <blockquote><pre>{@code + * LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2]) + * LogicalFilter(condition=[AND(>($3, 0), =($4, 0))]) + * LogicalAggregate(group=[{0, 1, 2}], agg#0=[COUNT() FILTER $3], + * agg#1=[COUNT() FILTER $4]) + * LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], $f3=[=($3, 0)], $f4=[=($3, 1)]) + * LogicalUnion(all=[true]) + * LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], $f3=[0]) + * LogicalFilter(condition=[=($7, 10)]) + * LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + * LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], $f3=[1]) + * LogicalFilter(condition=[=($7, 20)]) + * LogicalTableScan(table=[[CATALOG, SALES, EMP]]) + * }</pre></blockquote> + * + * @see CoreRules#MINUS_TO_DISTINCT + */ [email protected] +public class ImpalaMinusToDistinctRule + extends RelRule<ImpalaMinusToDistinctRule.Config> + implements TransformationRule { + + protected ImpalaMinusToDistinctRule(Config config) { + super(config); + } + + @Override public void onMatch(RelOptRuleCall call) { + final Minus minus = call.rel(0); + + if (minus.all) { + // Nothing we can do + return; + } + + final RelOptCluster cluster = minus.getCluster(); + final RelBuilder relBuilder = call.builder(); + final RexBuilder rexBuilder = cluster.getRexBuilder(); + final int branchCount = minus.getInputs().size(); + + // For each child branch in minus, add a column which indicates the branch index + // + // e.g., select EMPNO from emp -> select EMPNO, 0 from emp + // 0 indicates that it comes from the first child branch (operand) of the minus + // operator + for (int i = 0; i < branchCount; i++) { + relBuilder.push(minus.getInput(i)); + relBuilder.projectPlus(relBuilder.literal(new BigDecimal(i))); + } + + // create a union above all the branches + relBuilder.union(true, branchCount); + + final RelNode union = relBuilder.peek(); + final int originalFieldCnt = union.getRowType().getFieldCount() - 1; + + ImmutableList.Builder<RexNode> projects = ImmutableList.builder(); + // skip the branch index column + projects.addAll(Util.first(relBuilder.fields(), originalFieldCnt)); + + // IMPALA CHANGE 1, commented out below code because Impala does not support + // COUNT FILTER operation + // On top of the Union, add a Project and add one boolean column per branch counter, + // where the i-th boolean column is true iff the tuple comes from the i-th branch + // + // e.g., LogicalProject(EMPNO=[$0], $f1=[=($1, 0)], $f2=[=($1, 1)], $f3=[=($1, 2)]) + // $f1,$f2,$f3 are the boolean indicate whether it comes from the corresponding branch + //for (int i = 0; i < branchCount; i++) { + // projects.add( + // relBuilder.equals(relBuilder.field(originalFieldCnt), + // relBuilder.literal(new BigDecimal(i)))); + //} + // IMPALA CHANGE 2, instead, added this code: + // The count filter operation is not supported. Using the above example, the code + // we create for Impala is: + // e.g., LogicalProject(EMPNO=[$0], $f1=[if(=($1, 0),1,null)], + // $f2=[if(=($1, 1),1,null)], + // $f3=[if(=($1, 2),1,null)]) + // A '1' is created for counting purposes only if it comes from the branch, and a null + // otherwise. Since the count() operation will not count null values for the field, + // we get the count for each branch for each new column + for (int i = 0; i < branchCount; i++) { + RexNode equalsNode = relBuilder.equals( + relBuilder.field(originalFieldCnt), relBuilder.literal(new BigDecimal(i))); + RexNode one = relBuilder.literal(new BigDecimal(1)); + RexNode ifOp = rexBuilder.makeCall(new ImpalaOperator("if"), + ImmutableList.of(equalsNode, one, rexBuilder.makeNullLiteral(one.getType()))); + projects.add(ifOp); + } + + relBuilder.project(projects.build()); + + // IMPALA CHANGE 3: commented out below code because Impala does not support + // COUNT FILTER operation + // Add the count(*) filter $f1(..) for each branch + //ImmutableList.Builder<RelBuilder.AggCall> aggCalls = ImmutableList.builder(); + //for (int i = 0; i < branchCount; i++) { + // aggCalls.add(relBuilder.countStar(null).filter( + // relBuilder.field(originalFieldCnt + i))); + //} + // IMPALA CHANGE 4: Instead, added just a plain count. This combined with the above + // logic should provide the same count as the count filter operation. + ImmutableList.Builder<RelBuilder.AggCall> aggCalls = ImmutableList.builder(); + for (int i = 0; i < branchCount; i++) { + aggCalls.add(relBuilder.count(relBuilder.field(originalFieldCnt + i))); + } + + final ImmutableBitSet groupSet = ImmutableBitSet.range(originalFieldCnt); + relBuilder.aggregate(relBuilder.groupKey(groupSet), aggCalls.build()); + + ImmutableList.Builder<RexNode> filters = ImmutableList.builder(); + for (int i = 0; i < branchCount; i++) { + SqlOperator operator = + i == 0 ? SqlStdOperatorTable.GREATER_THAN + : SqlStdOperatorTable.EQUALS; + filters.add( + rexBuilder.makeCall(operator, relBuilder.field(originalFieldCnt + i), + relBuilder.literal(new BigDecimal(0)))); + } + + relBuilder.filter(filters.build()); + relBuilder.project(Util.first(relBuilder.fields(), originalFieldCnt)); + call.transformTo(relBuilder.build()); + } + + /** + * Rule configuration. + */ + @Value.Immutable + public interface Config extends RelRule.Config { + Config DEFAULT = ImmutableImpalaMinusToDistinctRule.Config.builder() + .operandSupplier(b -> b.operand(LogicalMinus.class).anyInputs()).build(); + + @Override default ImpalaMinusToDistinctRule toRule() { + return new ImpalaMinusToDistinctRule(this); + } + } +} diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteOptimizer.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteOptimizer.java index fe0f74407..0b3aaef6c 100644 --- a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteOptimizer.java +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteOptimizer.java @@ -34,6 +34,8 @@ import org.apache.impala.calcite.coercenodes.CoerceNodes; import org.apache.impala.calcite.rel.node.ConvertToImpalaRelRules; import org.apache.impala.calcite.rel.node.ImpalaPlanRel; import org.apache.impala.calcite.rules.ConvertToCNFRules; +import org.apache.impala.calcite.rules.ExtractLiteralAgg; +import org.apache.impala.calcite.rules.ImpalaMinusToDistinctRule; import org.apache.impala.common.ImpalaException; import org.slf4j.Logger; @@ -65,6 +67,14 @@ public class CalciteOptimizer implements CompilerStep { // Run essential Join Node rules RelNode optimizedPlan = runJoinProgram(relBuilder, expandedNodesPlan); + logDebug(optimizedPlan); + + // Run some essential rules needed to create working RelNodes after + // optimization + optimizedPlan = runPreImpalaConvertProgram(relBuilder, optimizedPlan); + + logDebug(optimizedPlan); + ImpalaPlanRel finalOptimizedPlan = runImpalaConvertProgram(relBuilder, optimizedPlan); @@ -78,9 +88,12 @@ public class CalciteOptimizer implements CompilerStep { builder.addMatchOrder(HepMatchOrder.BOTTOM_UP); builder.addRuleCollection(ImmutableList.of( + CoreRules.INTERSECT_TO_DISTINCT, + CoreRules.UNION_TO_DISTINCT, new ConvertToCNFRules.FilterConvertToCNFRule(), new ConvertToCNFRules.JoinConvertToCNFRule(), - new ConvertToCNFRules.ProjectConvertToCNFRule() + new ConvertToCNFRules.ProjectConvertToCNFRule(), + ImpalaMinusToDistinctRule.Config.DEFAULT.toRule() )); builder.addMatchOrder(HepMatchOrder.BOTTOM_UP); @@ -116,21 +129,40 @@ public class CalciteOptimizer implements CompilerStep { // MULTI_JOIN_OPTIMIZE rule is used to determine the join // ordering. builder.addMatchOrder(HepMatchOrder.BOTTOM_UP); + builder.addRuleInstance(CoreRules.JOIN_CONDITION_PUSH); builder.addRuleInstance(CoreRules.JOIN_TO_MULTI_JOIN); builder.addRuleInstance(CoreRules.MULTI_JOIN_OPTIMIZE); + builder.addRuleInstance(CoreRules.FILTER_PROJECT_TRANSPOSE); return runProgram(plan, builder.build()); } - public ImpalaPlanRel runImpalaConvertProgram(RelBuilder relBuilder, + public RelNode runPreImpalaConvertProgram(RelBuilder relBuilder, RelNode plan) throws ImpalaException { - RelNode preConversionNode = - CoerceNodes.coerceNodes(plan, plan.getCluster().getRexBuilder()); - HepProgramBuilder builder = new HepProgramBuilder(); + // Impala cannot handle the LITERAL_AGG method so we need to create + // an equivalent plan + RelNode retRelNode = plan; builder.addMatchOrder(HepMatchOrder.BOTTOM_UP); + builder.addRuleCollection(ImmutableList.of( + new ExtractLiteralAgg() + )); + + + retRelNode = runProgram(retRelNode, builder.build()); + + // Fix up the operands for the nodes which may also change some return types that + // propagate upwards in the plan. + return CoerceNodes.coerceNodes(retRelNode, plan.getCluster().getRexBuilder()); + + } + + public ImpalaPlanRel runImpalaConvertProgram(RelBuilder relBuilder, + RelNode plan) throws ImpalaException { + HepProgramBuilder builder = new HepProgramBuilder(); + builder.addRuleCollection(ImmutableList.of( new ConvertToImpalaRelRules.ImpalaScanRule(), new ConvertToImpalaRelRules.ImpalaSortRule(), @@ -142,7 +174,7 @@ public class CalciteOptimizer implements CompilerStep { new ConvertToImpalaRelRules.ImpalaValuesRule() )); - return (ImpalaPlanRel) runProgram(preConversionNode, builder.build()); + return (ImpalaPlanRel) runProgram(plan, builder.build()); } private RelNode runProgram(RelNode currentNode, HepProgram program) { diff --git a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteRelNodeConverter.java b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteRelNodeConverter.java index 30ce7e444..a230205d2 100644 --- a/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteRelNodeConverter.java +++ b/java/calcite-planner/src/main/java/org/apache/impala/calcite/service/CalciteRelNodeConverter.java @@ -17,20 +17,33 @@ package org.apache.impala.calcite.service; +import com.google.common.collect.ImmutableList; import org.apache.calcite.plan.ConventionTraitDef; import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelOptCostImpl; import org.apache.calcite.plan.RelOptPlanner; +import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; +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.plan.volcano.VolcanoPlanner; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.RelRoot; +import org.apache.calcite.rel.core.RelFactories; +import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.sql.SqlExplainFormat; import org.apache.calcite.sql.SqlExplainLevel; import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql2rel.RelDecorrelator; import org.apache.calcite.sql2rel.SqlToRelConverter; import org.apache.impala.calcite.operators.ImpalaConvertletTable; +import org.apache.calcite.sql2rel.StandardConvertletTable; +import org.apache.calcite.tools.RelBuilder; + +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,13 +80,30 @@ public class CalciteRelNodeConverter implements CompilerStep { validator_.getCatalogReader(), cluster_, ImpalaConvertletTable.INSTANCE, - SqlToRelConverter.config()); + SqlToRelConverter.config().withCreateValuesRel(false)); // Convert the valid AST into a logical plan RelRoot root = relConverter.convertQuery(validatedNode, false, true); RelNode relNode = root.project(); logDebug(relNode); - return relNode; + + RelNode subQueryRemovedPlan = + runProgram( + ImmutableList.of( + CoreRules.JOIN_SUB_QUERY_TO_CORRELATE, + CoreRules.PROJECT_SUB_QUERY_TO_CORRELATE, + CoreRules.FILTER_SUB_QUERY_TO_CORRELATE + ), + relNode); + logDebug(subQueryRemovedPlan); + + RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(cluster_, + validator_.getCatalogReader()); + RelNode decorrelatedPlan = + RelDecorrelator.decorrelateQuery(subQueryRemovedPlan, relBuilder); + + logDebug(decorrelatedPlan); + return decorrelatedPlan; } public RelOptCluster getCluster() { @@ -93,4 +123,17 @@ public class CalciteRelNodeConverter implements CompilerStep { LOG.info(RelOptUtil.dumpPlan("[Logical plan]", (RelNode) resultObject, SqlExplainFormat.TEXT, SqlExplainLevel.NON_COST_ATTRIBUTES)); } + + private RelNode runProgram(List<RelOptRule> rules, RelNode currentNode) { + HepProgramBuilder builder = new HepProgramBuilder(); + // rules to convert Calcite nodes into ImpalaPlanRel nodes + builder.addRuleCollection(rules); + builder.addMatchOrder(HepMatchOrder.BOTTOM_UP); + + HepPlanner planner = new HepPlanner(builder.build(), + currentNode.getCluster().getPlanner().getContext(), + false, null, RelOptCostImpl.FACTORY); + planner.setRoot(currentNode); + return planner.findBestExp(); + } }
