This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit 555da953fe758a7d310aeb3aed463f3f2f3cdc3b Author: Julian Hyde <[email protected]> AuthorDate: Wed Feb 5 17:02:22 2020 -0800 [CALCITE-3774] In RelBuilder and ProjectMergeRule, prevent merges when it would increase expression complexity Add an option RelBuilder.Config.bloat(), default 100. Set it, using RelBuilder.Config.withBloat(int), to -1 to never merge, 0 to merge only if complexity does not increase, b to merge if complexity increases by no more than b. Deprecate RelBuilder.shouldMergeProject(). Cache the nodeCount value in RexCall and RexWindow. Compute nodeCount each time for RexOver (a sub-class of RexCall with an extra window), because caching it would increase the complexity of RexCall's constructor. --- .../java/org/apache/calcite/plan/RelOptUtil.java | 23 +++++++ .../rel/rules/AbstractMaterializedViewRule.java | 4 +- .../apache/calcite/rel/rules/ProjectMergeRule.java | 24 ++++++- .../main/java/org/apache/calcite/rex/RexCall.java | 6 ++ .../main/java/org/apache/calcite/rex/RexNode.java | 12 ++++ .../main/java/org/apache/calcite/rex/RexOver.java | 4 ++ .../main/java/org/apache/calcite/rex/RexUtil.java | 15 ++++ .../java/org/apache/calcite/rex/RexWindow.java | 18 +++-- .../org/apache/calcite/rex/RexWindowBound.java | 13 ++++ .../java/org/apache/calcite/tools/RelBuilder.java | 56 ++++++++++++++- .../org/apache/calcite/rex/RexProgramTest.java | 2 +- .../org/apache/calcite/rex/RexProgramTestBase.java | 11 --- .../org/apache/calcite/test/RelBuilderTest.java | 80 ++++++++++++++++++++++ .../java/org/apache/calcite/tools/PlannerTest.java | 2 +- .../org/apache/calcite/piglet/PigRelBuilder.java | 17 +++-- 15 files changed, 259 insertions(+), 28 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 99469d6..9b73fd1 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java @@ -135,6 +135,7 @@ import java.util.TreeSet; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * <code>RelOptUtil</code> defines static utility methods for use in optimizing @@ -2967,6 +2968,28 @@ public abstract class RelOptUtil { return list; } + /** As {@link #pushPastProject}, but returns null if the resulting expressions + * are significantly more complex. + * + * @param bloat Maximum allowable increase in complexity */ + public static @Nullable List<RexNode> pushPastProjectUnlessBloat( + List<? extends RexNode> nodes, Project project, int bloat) { + if (bloat < 0) { + // If bloat is negative never merge. + return null; + } + final List<RexNode> list = pushPastProject(nodes, project); + final int bottomCount = RexUtil.nodeCount(project.getProjects()); + final int topCount = RexUtil.nodeCount(nodes); + final int mergedCount = RexUtil.nodeCount(list); + if (mergedCount > bottomCount + topCount + bloat) { + // The merged expression is more complex than the input expressions. + // Do not merge. + return null; + } + return list; + } + private static RexShuttle pushShuttle(final Project project) { return new RexShuttle() { @Override public RexNode visitInputRef(RexInputRef ref) { diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java index 80eb1ff..d1338a9 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java @@ -960,7 +960,9 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule { Filter.class, relBuilderFactory, Aggregate.class); this.aggregateProjectPullUpConstantsRule = new AggregateProjectPullUpConstantsRule( Aggregate.class, Filter.class, relBuilderFactory, "AggFilterPullUpConstants"); - this.projectMergeRule = new ProjectMergeRule(true, relBuilderFactory); + this.projectMergeRule = + new ProjectMergeRule(true, ProjectMergeRule.DEFAULT_BLOAT, + relBuilderFactory); } @Override protected boolean isValidPlan(Project topProject, RelNode node, diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java index 2d550f9..818f7b5 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java @@ -37,14 +37,20 @@ import java.util.List; * provided the projects aren't projecting identical sets of input references. */ public class ProjectMergeRule extends RelOptRule { + /** Default amount by which complexity is allowed to increase. */ + public static final int DEFAULT_BLOAT = 100; + public static final ProjectMergeRule INSTANCE = - new ProjectMergeRule(true, RelFactories.LOGICAL_BUILDER); + new ProjectMergeRule(true, DEFAULT_BLOAT, RelFactories.LOGICAL_BUILDER); //~ Instance fields -------------------------------------------------------- /** Whether to always merge projects. */ private final boolean force; + /** Limit how much complexity can increase during merging. */ + private final int bloat; + //~ Constructors ----------------------------------------------------------- /** @@ -52,13 +58,20 @@ public class ProjectMergeRule extends RelOptRule { * * @param force Whether to always merge projects */ - public ProjectMergeRule(boolean force, RelBuilderFactory relBuilderFactory) { + public ProjectMergeRule(boolean force, int bloat, + RelBuilderFactory relBuilderFactory) { super( operand(Project.class, operand(Project.class, any())), relBuilderFactory, "ProjectMergeRule" + (force ? ":force_mode" : "")); this.force = force; + this.bloat = bloat; + } + + @Deprecated // to be removed before 2.0 + public ProjectMergeRule(boolean force, RelBuilderFactory relBuilderFactory) { + this(force, DEFAULT_BLOAT, relBuilderFactory); } @Deprecated // to be removed before 2.0 @@ -106,7 +119,12 @@ public class ProjectMergeRule extends RelOptRule { } final List<RexNode> newProjects = - RelOptUtil.pushPastProject(topProject.getProjects(), bottomProject); + RelOptUtil.pushPastProjectUnlessBloat(topProject.getProjects(), + bottomProject, bloat); + if (newProjects == null) { + // Merged projects are significantly more complex. Do not merge. + return; + } final RelNode input = bottomProject.getInput(); if (RexUtil.isIdentity(newProjects, input.getRowType())) { if (force diff --git a/core/src/main/java/org/apache/calcite/rex/RexCall.java b/core/src/main/java/org/apache/calcite/rex/RexCall.java index 8c38a7f..d5c2cea 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexCall.java +++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java @@ -65,6 +65,7 @@ public class RexCall extends RexNode { public final SqlOperator op; public final ImmutableList<RexNode> operands; public final RelDataType type; + public final int nodeCount; /** * Simple binary operators are those operators which expects operands from the same Domain. @@ -91,6 +92,7 @@ public class RexCall extends RexNode { this.type = Objects.requireNonNull(type, "type"); this.op = Objects.requireNonNull(op, "operator"); this.operands = ImmutableList.copyOf(operands); + this.nodeCount = RexUtil.nodeCount(1, this.operands); assert op.getKind() != null : op; assert op.validRexOperands(operands.size(), Litmus.THROW) : this; } @@ -342,6 +344,10 @@ public class RexCall extends RexNode { return op; } + @Override public int nodeCount() { + return nodeCount; + } + /** * Creates a new call to the same operator with different operands. * diff --git a/core/src/main/java/org/apache/calcite/rex/RexNode.java b/core/src/main/java/org/apache/calcite/rex/RexNode.java index d9f51c3..7a8f058 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexNode.java +++ b/core/src/main/java/org/apache/calcite/rex/RexNode.java @@ -158,6 +158,18 @@ public abstract class RexNode { } } + /** Returns the number of nodes in this expression. + * + * <p>Leaf nodes, such as {@link RexInputRef} or {@link RexLiteral}, have + * a count of 1. Calls have a count of 1 plus the sum of their operands. + * + * <p>Node count is a measure of expression complexity that is used by some + * planner rules to prevent deeply nested expressions. + */ + public int nodeCount() { + return 1; + } + /** * Accepts a visitor, dispatching to the right overloaded * {@link RexVisitor#visitInputRef visitXxx} method. diff --git a/core/src/main/java/org/apache/calcite/rex/RexOver.java b/core/src/main/java/org/apache/calcite/rex/RexOver.java index 9e6e931..d197cc2 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexOver.java +++ b/core/src/main/java/org/apache/calcite/rex/RexOver.java @@ -125,6 +125,10 @@ public class RexOver extends RexCall { return visitor.visitOver(this, arg); } + @Override public int nodeCount() { + return super.nodeCount() + window.nodeCount; + } + /** * Returns whether an expression contains an OVER clause. */ diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java index 27b2b22..7dd6a9d 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java +++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java @@ -469,6 +469,21 @@ public class RexUtil { return false; } + /** Returns the number of nodes (including leaves) in a list of + * expressions. + * + * @see RexNode#nodeCount() */ + public static int nodeCount(List<? extends RexNode> nodes) { + return nodeCount(0, nodes); + } + + static int nodeCount(int n, List<? extends RexNode> nodes) { + for (RexNode operand : nodes) { + n += operand.nodeCount(); + } + return n; + } + /** * Walks over an expression and determines whether it is constant. */ diff --git a/core/src/main/java/org/apache/calcite/rex/RexWindow.java b/core/src/main/java/org/apache/calcite/rex/RexWindow.java index da9a33f..4e47f68 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexWindow.java +++ b/core/src/main/java/org/apache/calcite/rex/RexWindow.java @@ -16,11 +16,14 @@ */ package org.apache.calcite.rex; +import org.apache.calcite.util.Pair; + import com.google.common.collect.ImmutableList; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; +import javax.annotation.Nullable; /** * Specification of the window of rows over which a {@link RexOver} windowed @@ -37,6 +40,7 @@ public class RexWindow { private final RexWindowBound upperBound; private final boolean isRows; private final String digest; + public final int nodeCount; //~ Constructors ----------------------------------------------------------- @@ -49,16 +53,15 @@ public class RexWindow { RexWindow( List<RexNode> partitionKeys, List<RexFieldCollation> orderKeys, - RexWindowBound lowerBound, - RexWindowBound upperBound, + @Nullable RexWindowBound lowerBound, + @Nullable RexWindowBound upperBound, boolean isRows) { - assert partitionKeys != null; - assert orderKeys != null; this.partitionKeys = ImmutableList.copyOf(partitionKeys); this.orderKeys = ImmutableList.copyOf(orderKeys); this.lowerBound = lowerBound; this.upperBound = upperBound; this.isRows = isRows; + this.nodeCount = computeCodeCount(); this.digest = computeDigest(); } @@ -149,4 +152,11 @@ public class RexWindow { public boolean isRows() { return isRows; } + + private int computeCodeCount() { + return RexUtil.nodeCount(partitionKeys) + + RexUtil.nodeCount(Pair.left(orderKeys)) + + (lowerBound == null ? 0 : lowerBound.nodeCount()) + + (upperBound == null ? 0 : upperBound.nodeCount()); + } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java b/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java index bfcf92d..547bec6 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java +++ b/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java @@ -106,6 +106,15 @@ public abstract class RexWindowBound { } /** + * Returns the number of nodes in this bound. + * + * @see RexNode#nodeCount() + */ + public int nodeCount() { + return 1; + } + + /** * Implements UNBOUNDED PRECEDING/FOLLOWING bound. */ private static class RexWindowBoundUnbounded extends RexWindowBound { @@ -217,6 +226,10 @@ public abstract class RexWindowBound { return offset; } + @Override public int nodeCount() { + return super.nodeCount() + offset.nodeCount(); + } + @Override public <R> RexWindowBound accept(RexVisitor<R> visitor) { R r = offset.accept(visitor); if (r instanceof RexNode && r != offset) { diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java index 800f6d9..eec1af1 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -1324,8 +1324,9 @@ public class RelBuilder { fieldNameList.add(null); } + bloat: if (frame.rel instanceof Project - && shouldMergeProject()) { + && config.bloat() >= 0) { final Project project = (Project) frame.rel; // Populate field names. If the upper expression is an input ref and does // not have a recommended name, use the name of the underlying field. @@ -1340,7 +1341,13 @@ public class RelBuilder { } } final List<RexNode> newNodes = - RelOptUtil.pushPastProject(nodeList, project); + RelOptUtil.pushPastProjectUnlessBloat(nodeList, project, + config.bloat()); + if (newNodes == null) { + // The merged expression is more complex than the input expressions. + // Do not merge. + break bloat; + } // Carefully build a list of fields, so that table aliases from the input // can be seen for fields that are based on a RexInputRef. @@ -1445,6 +1452,7 @@ public class RelBuilder { * <p>The default implementation returns {@code true}; * sub-classes may disable merge by overriding to return {@code false}. */ @Experimental + @Deprecated protected boolean shouldMergeProject() { return true; } @@ -3053,6 +3061,50 @@ public class RelBuilder { return new ConfigBuilder(this); } + /** Controls whether to merge two {@link Project} operators when inlining + * expressions causes complexity to increase. + * + * <p>Usually merging projects is beneficial, but occasionally the + * result is more complex than the original projects. Consider: + * + * <pre> + * P: Project(a+b+c AS x, d+e+f AS y, g+h+i AS z) # complexity 15 + * Q: Project(x*y*z AS p, x-y-z AS q) # complexity 10 + * R: Project((a+b+c)*(d+e+f)*(g+h+i) AS s, + * (a+b+c)-(d+e+f)-(g+h+i) AS t) # complexity 34 + * </pre> + * + * The complexity of an expression is the number of nodes (leaves and + * operators). For example, {@code a+b+c} has complexity 5 (3 field + * references and 2 calls): + * + * <pre> + * + + * / \ + * + c + * / \ + * a b + * </pre> + * + * <p>A negative value never allows merges. + * + * <p>A zero or positive value, {@code bloat}, allows a merge if complexity + * of the result is less than or equal to the sum of the complexity of the + * originals plus {@code bloat}. + * + * <p>The default value, 100, allows a moderate increase in complexity but + * prevents cases where complexity would run away into the millions and run + * out of memory. Moderate complexity is OK; the implementation, say via + * {@link org.apache.calcite.adapter.enumerable.EnumerableCalc}, will often + * gather common sub-expressions and compute them only once. + */ + @ImmutableBeans.Property + @ImmutableBeans.IntDefault(100) + int bloat(); + + /** Sets {@link #bloat}. */ + Config withBloat(int bloat); + /** Whether {@link RelBuilder#aggregate} should eliminate duplicate * aggregate calls; default true. */ @ImmutableBeans.Property diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java index 6f4f77e..09989c1 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java +++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java @@ -738,7 +738,7 @@ public class RexProgramTest extends RexProgramTestBase { rexBuilder.makeFieldAccess(range3, i * 2 + 1))); } final RexNode cnf = RexUtil.toCnf(rexBuilder, or(list)); - final int nodeCount = nodeCount(cnf); + final int nodeCount = cnf.nodeCount(); assertThat((n + 1) * (int) Math.pow(2, n) + 1, equalTo(nodeCount)); if (n == 3) { assertThat(cnf.toStringRaw(), diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramTestBase.java b/core/src/test/java/org/apache/calcite/rex/RexProgramTestBase.java index dc4ae80..9a542ed 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexProgramTestBase.java +++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTestBase.java @@ -171,17 +171,6 @@ public class RexProgramTestBase extends RexProgramBuilderBase { is(expected ? "true" : "false")); } - /** Returns the number of nodes (including leaves) in a Rex tree. */ - protected static int nodeCount(RexNode node) { - int n = 1; - if (node instanceof RexCall) { - for (RexNode operand : ((RexCall) node).getOperands()) { - n += nodeCount(operand); - } - } - return n; - } - protected Comparable eval(RexNode e) { return RexInterpreter.evaluate(e, ImmutableMap.of()); } diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java index 75cde59..aa9e09d 100644 --- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java @@ -75,6 +75,7 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -757,6 +758,85 @@ public class RelBuilderTest { + " LogicalValues(tuples=[[]])\n"); } + @Test public void testProjectBloat() { + final Function<RelBuilder, RelNode> f = b -> + b.scan("EMP") + .project( + b.alias( + caseCall(b, b.field("DEPTNO"), + b.literal(0), b.literal("zero"), + b.literal(1), b.literal("one"), + b.literal(2), b.literal("two"), + b.literal("other")), + "v")) + .project( + b.call(SqlStdOperatorTable.PLUS, b.field("v"), b.field("v"))) + .build(); + // Complexity of bottom is 14; top is 3; merged is 29; difference is -12. + // So, we merge if bloat is 20 or 100 (the default), + // but not if it is -1, 0 or 10. + final String expected = "LogicalProject($f0=[+" + + "(CASE(=($7, 0), 'zero', =($7, 1), 'one', =($7, 2), 'two', 'other')," + + " CASE(=($7, 0), 'zero', =($7, 1), 'one', =($7, 2), 'two', 'other'))])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n"; + final String expectedNeg = "LogicalProject($f0=[+($0, $0)])\n" + + " LogicalProject(v=[CASE(=($7, 0), 'zero', =($7, 1), " + + "'one', =($7, 2), 'two', 'other')])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n"; + assertThat(f.apply(createBuilder(c -> c)), hasTree(expected)); + assertThat(f.apply(createBuilder(c -> c.withBloat(0))), + hasTree(expectedNeg)); + assertThat(f.apply(createBuilder(c -> c.withBloat(-1))), + hasTree(expectedNeg)); + assertThat(f.apply(createBuilder(c -> c.withBloat(10))), + hasTree(expectedNeg)); + assertThat(f.apply(createBuilder(c -> c.withBloat(20))), + hasTree(expected)); + } + + @Test public void testProjectBloat2() { + final Function<RelBuilder, RelNode> f = b -> + b.scan("EMP") + .project( + b.field("DEPTNO"), + b.field("SAL"), + b.alias( + b.call(SqlStdOperatorTable.PLUS, b.field("DEPTNO"), + b.field("EMPNO")), "PLUS")) + .project( + b.call(SqlStdOperatorTable.MULTIPLY, b.field("SAL"), + b.field("PLUS")), + b.field("SAL")) + .build(); + // Complexity of bottom is 5; top is 4; merged is 6; difference is 3. + // So, we merge except when bloat is -1. + final String expected = "LogicalProject($f0=[*($5, +($7, $0))], SAL=[$5])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n"; + final String expectedNeg = "LogicalProject($f0=[*($1, $2)], SAL=[$1])\n" + + " LogicalProject(DEPTNO=[$7], SAL=[$5], PLUS=[+($7, $0)])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n"; + assertThat(f.apply(createBuilder(c -> c)), hasTree(expected)); + assertThat(f.apply(createBuilder(c -> c.withBloat(0))), + hasTree(expected)); + assertThat(f.apply(createBuilder(c -> c.withBloat(-1))), + hasTree(expectedNeg)); + assertThat(f.apply(createBuilder(c -> c.withBloat(10))), + hasTree(expected)); + assertThat(f.apply(createBuilder(c -> c.withBloat(20))), + hasTree(expected)); + } + + private RexNode caseCall(RelBuilder b, RexNode ref, RexNode... nodes) { + final List<RexNode> list = new ArrayList<>(); + for (int i = 0; i + 1 < nodes.length; i += 2) { + list.add(b.equals(ref, nodes[i])); + list.add(nodes[i + 1]); + } + list.add(nodes.length % 2 == 1 ? nodes[nodes.length - 1] + : b.literal(null)); + return b.call(SqlStdOperatorTable.CASE, list); + } + @Test public void testRename() { final RelBuilder builder = RelBuilder.create(config().build()); diff --git a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java index c310594..f78d61e 100644 --- a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java +++ b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java @@ -1377,7 +1377,7 @@ public class PlannerTest { @Test public void testMergeProjectForceMode() throws Exception { RuleSet ruleSet = RuleSets.ofList( - new ProjectMergeRule(true, + new ProjectMergeRule(true, ProjectMergeRule.DEFAULT_BLOAT, RelBuilder.proto(RelFactories.DEFAULT_PROJECT_FACTORY))); Planner planner = getPlanner(null, Programs.of(ruleSet)); planner.close(); diff --git a/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java b/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java index 96150af..b8337eb 100644 --- a/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java +++ b/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java @@ -17,6 +17,7 @@ package org.apache.calcite.piglet; import org.apache.calcite.plan.Context; +import org.apache.calcite.plan.Contexts; import org.apache.calcite.plan.Convention; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptSchema; @@ -56,6 +57,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.UnaryOperator; /** * Extension to {@link RelBuilder} for Pig logical operators. @@ -79,10 +81,19 @@ public class PigRelBuilder extends RelBuilder { public static PigRelBuilder create(FrameworkConfig config) { final RelBuilder relBuilder = RelBuilder.create(config); Hook.REL_BUILDER_SIMPLIFY.addThread(Hook.propertyJ(false)); - return new PigRelBuilder(config.getContext(), relBuilder.getCluster(), + return new PigRelBuilder( + transform(config.getContext(), c -> c.withBloat(-1)), + relBuilder.getCluster(), relBuilder.getRelOptSchema()); } + private static Context transform(Context context, + UnaryOperator<RelBuilder.Config> transform) { + final Config config = + Util.first(context.unwrap(Config.class), Config.DEFAULT); + return Contexts.of(transform.apply(config), context); + } + public RelNode getRel(String alias) { return aliasMap.get(alias); } @@ -108,10 +119,6 @@ public class PigRelBuilder extends RelBuilder { return new CorrelationId(nextCorrelId++); } - @Override protected boolean shouldMergeProject() { - return false; - } - public String getAlias() { final RelNode input = peek(); if (reverseAliasMap.containsKey(input)) {
