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 15e63787a6c2ccaa01582991068d8e74daf93d1f Author: Julian Hyde <[email protected]> AuthorDate: Mon Jul 1 17:14:26 2019 -0700 [CALCITE-3166] Make RelBuilder configurable Add immutable class RelBuilder.Config, and a builder for it. Disable elimination of duplicate aggregate calls (added in [CALCITE-3123]), until [CALCITE-3145] is fixed. --- .../java/org/apache/calcite/tools/RelBuilder.java | 96 ++++++++++++++++++++-- .../org/apache/calcite/test/RelBuilderTest.java | 53 +++++++++++- 2 files changed, 141 insertions(+), 8 deletions(-) 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 983fa35..079c3de 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -154,8 +154,8 @@ public class RelBuilder { private final RelFactories.SpoolFactory spoolFactory; private final RelFactories.RepeatUnionFactory repeatUnionFactory; private final Deque<Frame> stack = new ArrayDeque<>(); - private final boolean simplify; private final RexSimplify simplifier; + private final Config config; protected RelBuilder(Context context, RelOptCluster cluster, RelOptSchema relOptSchema) { @@ -164,7 +164,7 @@ public class RelBuilder { if (context == null) { context = Contexts.EMPTY_CONTEXT; } - this.simplify = Hook.REL_BUILDER_SIMPLIFY.get(true); + this.config = getConfig(context); this.aggregateFactory = Util.first(context.unwrap(RelFactories.AggregateFactory.class), RelFactories.DEFAULT_AGGREGATE_FACTORY); @@ -221,6 +221,21 @@ public class RelBuilder { new RexSimplify(cluster.getRexBuilder(), predicates, executor); } + /** Derives the Config to be used for this RelBuilder. + * + * <p>Overrides {@link RelBuilder.Config#simplify} if + * {@link Hook#REL_BUILDER_SIMPLIFY} is set. + */ + private Config getConfig(Context context) { + final Config config = + Util.first(context.unwrap(Config.class), Config.DEFAULT); + boolean simplify = Hook.REL_BUILDER_SIMPLIFY.get(config.simplify); + if (simplify == config.simplify) { + return config; + } + return config.toBuilder().withSimplify(simplify).build(); + } + /** Creates a RelBuilder. */ public static RelBuilder create(FrameworkConfig config) { return Frameworks.withPrepare(config, @@ -1310,7 +1325,7 @@ public class RelBuilder { } // Simplify expressions. - if (simplify) { + if (config.simplify) { for (int i = 0; i < nodeList.size(); i++) { nodeList.set(i, simplifier.simplifyPreservingType(nodeList.get(i))); } @@ -1616,7 +1631,7 @@ public class RelBuilder { assert groupSet.contains(set); } - if (Util.isDistinct(aggregateCalls)) { + if (!config.dedupAggregateCalls || Util.isDistinct(aggregateCalls)) { return aggregate_(groupSet, groupSets, r, aggregateCalls, registrar.extraNodes, frame.fields); } else { @@ -1887,7 +1902,7 @@ public class RelBuilder { final RelNode join; final boolean correlate = variablesSet.size() == 1; RexNode postCondition = literal(true); - if (simplify) { + if (config.simplify) { // Normalize expanded versions IS NOT DISTINCT FROM so that simplifier does not // transform the expression to something unrecognizable if (condition instanceof RexCall) { @@ -2760,6 +2775,77 @@ public class RelBuilder { } } } + + /** Configuration of RelBuilder. + * + * <p>It is immutable, and all fields are public. + * + * <p>Use the {@link #DEFAULT} instance, + * or call {@link #builder()} to create a builder then + * {@link RelBuilder.ConfigBuilder#build()}. You can also use + * {@link #toBuilder()} to modify a few properties of an existing Config. */ + public static class Config { + /** Default configuration. */ + public static final Config DEFAULT = + new Config(false, true); + + /** Whether {@link RelBuilder#aggregate} should eliminate duplicate + * aggregate calls; default true but currently disabled. */ + public final boolean dedupAggregateCalls; + + /** Whether to simplify expressions; default true. */ + public final boolean simplify; + + // called only from ConfigBuilder and when creating DEFAULT; + // parameters and fields must be in alphabetical order + private Config(boolean dedupAggregateCalls, + boolean simplify) { + this.dedupAggregateCalls = dedupAggregateCalls; + this.simplify = simplify; + } + + /** Creates a ConfigBuilder with all properties set to their default + * values. */ + public static ConfigBuilder builder() { + return DEFAULT.toBuilder(); + } + + /** Creates a ConfigBuilder with properties set to the values in this + * Config. */ + public ConfigBuilder toBuilder() { + return new ConfigBuilder() + .withDedupAggregateCalls(dedupAggregateCalls) + .withSimplify(simplify); + } + } + + /** Creates a {@link RelBuilder.Config}. */ + public static class ConfigBuilder { + private boolean dedupAggregateCalls; + private boolean simplify; + + private ConfigBuilder() { + } + + /** Creates a {@link RelBuilder.Config}. */ + public Config build() { + return new Config(dedupAggregateCalls, simplify); + } + + /** Sets the value that will become + * {@link org.apache.calcite.tools.RelBuilder.Config#dedupAggregateCalls}. */ + public ConfigBuilder withDedupAggregateCalls(boolean dedupAggregateCalls) { + this.dedupAggregateCalls = dedupAggregateCalls; + return this; + } + + /** Sets the value that will become + * {@link org.apache.calcite.tools.RelBuilder.Config#simplify}. */ + public ConfigBuilder withSimplify(boolean simplify) { + this.simplify = simplify; + return this; + } + } } // End RelBuilder.java 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 29e42c2..1178a30 100644 --- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java @@ -79,6 +79,8 @@ import java.util.List; import java.util.Locale; import java.util.NoSuchElementException; import java.util.TreeSet; +import java.util.function.Function; +import javax.annotation.Nonnull; import static org.apache.calcite.test.Matchers.hasTree; @@ -157,6 +159,15 @@ public class RelBuilderTest { return Frameworks.newConfigBuilder().defaultSchema(root); } + @Nonnull static RelBuilder createBuilder( + Function<RelBuilder.ConfigBuilder, RelBuilder.ConfigBuilder> transform) { + final Frameworks.ConfigBuilder configBuilder = config(); + configBuilder.context( + Contexts.of(transform.apply(RelBuilder.Config.builder()) + .build())); + return RelBuilder.create(configBuilder.build()); + } + @Test public void testScan() { // Equivalent SQL: // SELECT * @@ -377,6 +388,31 @@ public class RelBuilderTest { assertThat(root, hasTree(expected)); } + /** Tests that {@link RelBuilder#project} simplifies expressions if and only if + * {@link RelBuilder.Config#simplify}. */ + @Test public void testSimplify() { + checkSimplify(configBuilder -> configBuilder.withSimplify(true), + hasTree("LogicalProject($f0=[true])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n")); + checkSimplify(configBuilder -> configBuilder, + hasTree("LogicalProject($f0=[true])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n")); + checkSimplify(configBuilder -> configBuilder.withSimplify(false), + hasTree("LogicalProject($f0=[IS NOT NULL($0)])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n")); + } + + private void checkSimplify( + Function<RelBuilder.ConfigBuilder, RelBuilder.ConfigBuilder> transform, + Matcher<RelNode> matcher) { + final RelBuilder builder = createBuilder(transform); + final RelNode root = + builder.scan("EMP") + .project(builder.isNotNull(builder.field("EMPNO"))) + .build(); + assertThat(root, matcher); + } + @Test public void testScanFilterOr() { // Equivalent SQL: // SELECT * @@ -931,8 +967,7 @@ public class RelBuilderTest { /** Tests that {@link RelBuilder#aggregate} eliminates duplicate aggregate * calls and creates a {@code Project} to compensate. */ @Test public void testAggregateEliminatesDuplicateCalls() { - final RelBuilder builder = RelBuilder.create(config().build()); - RelNode root = + final Function<RelBuilder, RelNode> fn = builder -> builder.scan("EMP") .aggregate(builder.groupKey(), builder.sum(builder.field(1)).as("S1"), @@ -940,11 +975,23 @@ public class RelBuilderTest { builder.sum(builder.field(2)).as("S2"), builder.sum(builder.field(1)).as("S1b")) .build(); + final RelBuilder builder = + createBuilder(configBuilder -> + configBuilder.withDedupAggregateCalls(true)); final String expected = "" + "LogicalProject(S1=[$0], C=[$1], S2=[$2], S1b=[$0])\n" + " LogicalAggregate(group=[{}], S1=[SUM($1)], C=[COUNT()], S2=[SUM($2)])\n" + " LogicalTableScan(table=[[scott, EMP]])\n"; - assertThat(root, hasTree(expected)); + assertThat(fn.apply(builder), hasTree(expected)); + + // Now, disable the rewrite + final RelBuilder builder2 = + createBuilder(configBuilder -> + configBuilder.withDedupAggregateCalls(false)); + final String expected2 = "" + + "LogicalAggregate(group=[{}], S1=[SUM($1)], C=[COUNT()], S2=[SUM($2)], S1b=[SUM($1)])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n"; + assertThat(fn.apply(builder2), hasTree(expected2)); } @Test public void testAggregateFilter() {
