You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by ja...@apache.org on 2018/12/16 02:10:25 UTC
calcite git commit: [CALCITE-2736] Update the ReduceExpressionsRule
to better expose options
Repository: calcite
Updated Branches:
refs/heads/master f362785e1 -> 0d0fd9e9c
[CALCITE-2736] Update the ReduceExpressionsRule to better expose options
* Update the reduction rule to use an options object to control behavior.
* Update existing methods so they use options object
* Deprecate methods that accept booleans instead of options object
* Expose ability to avoid special handling for dynamic calls
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/0d0fd9e9
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/0d0fd9e9
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/0d0fd9e9
Branch: refs/heads/master
Commit: 0d0fd9e9cbf9ac0adfee6b9c7306c9d49d055bcd
Parents: f362785
Author: Jacques Nadeau <ja...@apache.org>
Authored: Wed Dec 12 10:25:58 2018 +0530
Committer: Jacques Nadeau <ja...@apache.org>
Committed: Sat Dec 15 17:37:47 2018 -0800
----------------------------------------------------------------------
.../rel/rules/ReduceExpressionsRule.java | 293 +++++++++++++++----
.../apache/calcite/test/RelOptRulesTest.java | 34 +++
.../org/apache/calcite/test/RelOptRulesTest.xml | 22 ++
3 files changed, 293 insertions(+), 56 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/0d0fd9e9/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index 48ed3cd..5f3425c 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -92,6 +92,30 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
//~ Static fields/initializers ---------------------------------------------
/**
+ * Default reduction options used for project and calc.
+ *
+ * <ul>
+ * <li>Match nullability of expressions: true
+ * <li>Treat unknown values as false: false
+ * <li>Treat dynamic calls as non constant: true
+ * </ul>
+ */
+ public static final ReductionOptions DEFAULT_OPTIONS =
+ new ReductionOptions(true, false, true);
+
+ /**
+ * Default reduction options used for filter and join.
+ *
+ * <ul>
+ * <li>Match nullability of expressions: true
+ * <li>Treat unknown values as false: true
+ * <li>Treat dynamic calls as non constant: true
+ * </ul>
+ */
+ public static final ReductionOptions DEFAULT_FILTER_OPTIONS =
+ new ReductionOptions(true, true, true);
+
+ /**
* Regular expression that matches the description of all instances of this
* rule and {@link ValuesReduceRule} also. Use
* it to prevent the planner from invoking these rules.
@@ -104,7 +128,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
* {@link org.apache.calcite.rel.logical.LogicalFilter}.
*/
public static final ReduceExpressionsRule FILTER_INSTANCE =
- new FilterReduceExpressionsRule(LogicalFilter.class, true,
+ new FilterReduceExpressionsRule(LogicalFilter.class,
+ DEFAULT_FILTER_OPTIONS,
RelFactories.LOGICAL_BUILDER);
/**
@@ -112,7 +137,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
* {@link org.apache.calcite.rel.logical.LogicalProject}.
*/
public static final ReduceExpressionsRule PROJECT_INSTANCE =
- new ProjectReduceExpressionsRule(LogicalProject.class, true,
+ new ProjectReduceExpressionsRule(LogicalProject.class,
+ DEFAULT_OPTIONS,
RelFactories.LOGICAL_BUILDER);
/**
@@ -120,7 +146,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
* {@link org.apache.calcite.rel.core.Join}.
*/
public static final ReduceExpressionsRule JOIN_INSTANCE =
- new JoinReduceExpressionsRule(Join.class, true,
+ new JoinReduceExpressionsRule(Join.class,
+ DEFAULT_FILTER_OPTIONS,
RelFactories.LOGICAL_BUILDER);
/**
@@ -131,7 +158,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
new CalcReduceExpressionsRule(LogicalCalc.class, true,
RelFactories.LOGICAL_BUILDER);
- protected final boolean matchNullability;
+ protected final ReductionOptions options;
/**
* Rule that reduces constants inside a {@link org.apache.calcite.rel.core.Filter}.
@@ -145,9 +172,16 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
this(filterClass, true, relBuilderFactory);
}
+ @Deprecated // to be removed before 2.0
public FilterReduceExpressionsRule(Class<? extends Filter> filterClass,
boolean matchNullability, RelBuilderFactory relBuilderFactory) {
- super(filterClass, matchNullability, relBuilderFactory,
+ this(filterClass, DEFAULT_FILTER_OPTIONS.matchNullability(matchNullability),
+ relBuilderFactory);
+ }
+
+ public FilterReduceExpressionsRule(Class<? extends Filter> filterClass,
+ ReductionOptions options, RelBuilderFactory relBuilderFactory) {
+ super(filterClass, options, relBuilderFactory,
"ReduceExpressionsRule(Filter)");
}
@@ -160,8 +194,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
final RelMetadataQuery mq = call.getMetadataQuery();
final RelOptPredicateList predicates =
mq.getPulledUpPredicates(filter.getInput());
- if (reduceExpressions(filter, expList, predicates, true,
- matchNullability)) {
+ if (reduceExpressions(filter, expList, predicates, options)) {
assert expList.size() == 1;
newConditionExp = expList.get(0);
reduced = true;
@@ -272,9 +305,19 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
this(projectClass, true, relBuilderFactory);
}
+ @Deprecated // to be removed before 2.0
public ProjectReduceExpressionsRule(Class<? extends Project> projectClass,
boolean matchNullability, RelBuilderFactory relBuilderFactory) {
- super(projectClass, matchNullability, relBuilderFactory,
+ this(projectClass,
+ DEFAULT_OPTIONS.matchNullability(matchNullability).unknownAsFalse(false),
+ relBuilderFactory);
+ }
+
+ public ProjectReduceExpressionsRule(
+ Class<? extends Project> projectClass,
+ ReductionOptions options,
+ RelBuilderFactory relBuilderFactory) {
+ super(projectClass, options, relBuilderFactory,
"ReduceExpressionsRule(Project)");
}
@@ -285,8 +328,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
mq.getPulledUpPredicates(project.getInput());
final List<RexNode> expList =
Lists.newArrayList(project.getProjects());
- if (reduceExpressions(project, expList, predicates, false,
- matchNullability)) {
+ if (reduceExpressions(project, expList, predicates, options)) {
call.transformTo(
call.builder()
.push(project.getInput())
@@ -306,12 +348,20 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
@Deprecated // to be removed before 2.0
public JoinReduceExpressionsRule(Class<? extends Join> joinClass,
RelBuilderFactory relBuilderFactory) {
- this(joinClass, true, relBuilderFactory);
+ this(joinClass, DEFAULT_FILTER_OPTIONS, relBuilderFactory);
}
+ @Deprecated // to be removed before 2.0
public JoinReduceExpressionsRule(Class<? extends Join> joinClass,
boolean matchNullability, RelBuilderFactory relBuilderFactory) {
- super(joinClass, matchNullability, relBuilderFactory,
+ this(joinClass,
+ DEFAULT_FILTER_OPTIONS.matchNullability(matchNullability),
+ relBuilderFactory);
+ }
+
+ public JoinReduceExpressionsRule(Class<? extends Join> joinClass,
+ ReductionOptions options, RelBuilderFactory relBuilderFactory) {
+ super(joinClass, options, relBuilderFactory,
"ReduceExpressionsRule(Join)");
}
@@ -328,8 +378,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
final RelOptPredicateList predicates =
leftPredicates.union(rexBuilder,
rightPredicates.shift(rexBuilder, fieldCount));
- if (!reduceExpressions(join, expList, predicates, true,
- matchNullability)) {
+ if (!reduceExpressions(join, expList, predicates, options)) {
return;
}
if (join instanceof EquiJoin) {
@@ -362,12 +411,22 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
@Deprecated // to be removed before 2.0
public CalcReduceExpressionsRule(Class<? extends Calc> calcClass,
RelBuilderFactory relBuilderFactory) {
- this(calcClass, true, relBuilderFactory);
+ this(calcClass, DEFAULT_OPTIONS, relBuilderFactory);
}
+ @Deprecated // to be removed before 2.0
public CalcReduceExpressionsRule(Class<? extends Calc> calcClass,
boolean matchNullability, RelBuilderFactory relBuilderFactory) {
- super(calcClass, matchNullability, relBuilderFactory,
+ this(calcClass,
+ DEFAULT_OPTIONS.matchNullability(matchNullability),
+ relBuilderFactory);
+ }
+
+ public CalcReduceExpressionsRule(Class<? extends Calc> calcClass,
+ ReductionOptions options, RelBuilderFactory relBuilderFactory) {
+ super(calcClass,
+ options,
+ relBuilderFactory,
"ReduceExpressionsRule(Calc)");
}
@@ -388,8 +447,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
expandedExprList.add(expr.accept(shuttle));
}
final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
- if (reduceExpressions(calc, expandedExprList, predicates, false,
- matchNullability)) {
+ if (reduceExpressions(calc, expandedExprList, predicates, options)) {
final RexProgramBuilder builder =
new RexProgramBuilder(
calc.getInput().getRowType(),
@@ -462,11 +520,25 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
* @param matchNullability Whether to add a CAST when a nullable expression
* reduces to a NOT NULL literal
*/
+ @Deprecated // to be removed before 2.0
protected ReduceExpressionsRule(Class<? extends RelNode> clazz,
boolean matchNullability, RelBuilderFactory relBuilderFactory,
String description) {
+ this(clazz,
+ DEFAULT_OPTIONS.matchNullability(matchNullability)
+ .treatDynamicCallsAsNonConstant(true)
+ .unknownAsFalse(false),
+ relBuilderFactory,
+ description);
+ }
+
+ protected ReduceExpressionsRule(
+ Class<? extends RelNode> clazz,
+ ReductionOptions options,
+ RelBuilderFactory relBuilderFactory,
+ String description) {
super(operand(clazz, any()), relBuilderFactory, description);
- this.matchNullability = matchNullability;
+ this.options = options;
}
@Deprecated // to be removed before 2.0
@@ -485,50 +557,43 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
* @param predicates Constraints known to hold on input expressions
* @return whether reduction found something to change, and succeeded
*/
+ @Deprecated // to be removed before 2.0
protected static boolean reduceExpressions(RelNode rel, List<RexNode> expList,
RelOptPredicateList predicates) {
- return reduceExpressions(rel, expList, predicates, false, true);
+ return reduceExpressions(rel, expList, predicates,
+ DEFAULT_OPTIONS.matchNullability(true).unknownAsFalse(false));
}
@Deprecated // to be removed before 2.0
protected static boolean reduceExpressions(RelNode rel, List<RexNode> expList,
RelOptPredicateList predicates, boolean unknownAsFalse) {
- return reduceExpressions(rel, expList, predicates, unknownAsFalse, true);
+ return reduceExpressions(rel, expList, predicates,
+ DEFAULT_OPTIONS.matchNullability(true).unknownAsFalse(unknownAsFalse));
+ }
+
+ @Deprecated
+ protected static boolean reduceExpressions(RelNode rel, List<RexNode> expList,
+ RelOptPredicateList predicates, boolean unknownAsFalse,
+ boolean matchNullability) {
+ return reduceExpressions(rel, expList, predicates,
+ DEFAULT_OPTIONS.matchNullability(matchNullability)
+ .treatDynamicCallsAsNonConstant(true)
+ .matchNullability(matchNullability)
+ );
}
/**
* Reduces a list of expressions.
*
- * <p>The {@code matchNullability} flag comes into play when reducing a
- * expression whose type is nullable. Suppose we are reducing an expression
- * {@code CASE WHEN 'a' = 'a' THEN 1 ELSE NULL END}. Before reduction the
- * type is {@code INTEGER} (nullable), but after reduction the literal 1 has
- * type {@code INTEGER NOT NULL}.
- *
- * <p>In some situations it is more important to preserve types; in this
- * case you should use {@code matchNullability = true} (which used to be
- * the default behavior of this method), and it will cast the literal to
- * {@code INTEGER} (nullable).
- *
- * <p>In other situations, you would rather propagate the new stronger type,
- * because it may allow further optimizations later; pass
- * {@code matchNullability = false} and no cast will be added, but you may
- * need to adjust types elsewhere in the expression tree.
- *
* @param rel Relational expression
* @param expList List of expressions, modified in place
* @param predicates Constraints known to hold on input expressions
- * @param unknownAsFalse Whether UNKNOWN will be treated as FALSE
- * @param matchNullability Whether Calcite should add a CAST to a literal
- * resulting from simplification and expression if the
- * expression had nullable type and the literal is
- * NOT NULL
+ * @param options ReductionOptions to use
*
* @return whether reduction found something to change, and succeeded
*/
protected static boolean reduceExpressions(RelNode rel, List<RexNode> expList,
- RelOptPredicateList predicates, boolean unknownAsFalse,
- boolean matchNullability) {
+ RelOptPredicateList predicates, ReductionOptions options) {
final RelOptCluster cluster = rel.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RexExecutor executor =
@@ -537,15 +602,15 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
new RexSimplify(rexBuilder, predicates, executor);
// Simplify predicates in place
- final RexUnknownAs unknownAs = RexUnknownAs.falseIf(unknownAsFalse);
+ final RexUnknownAs unknownAs = RexUnknownAs.falseIf(options.unknownAsFalse);
final boolean reduced = reduceExpressionsInternal(rel, simplify, unknownAs,
- expList, predicates);
+ expList, predicates, options.treatDynamicCallsAsNonConstant);
boolean simplified = false;
for (int i = 0; i < expList.size(); i++) {
final RexNode expr2 =
simplify.simplifyPreservingType(expList.get(i), unknownAs,
- matchNullability);
+ options.matchNullability);
if (!expr2.equals(expList.get(i))) {
expList.set(i, expr2);
simplified = true;
@@ -557,7 +622,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
protected static boolean reduceExpressionsInternal(RelNode rel,
RexSimplify simplify, RexUnknownAs unknownAs, List<RexNode> expList,
- RelOptPredicateList predicates) {
+ RelOptPredicateList predicates, boolean treatDynamicCallsAsNonConstant) {
boolean changed = false;
// Replace predicates on CASE to CASE on predicates.
changed |= new CaseShuttle().mutate(expList);
@@ -567,7 +632,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
List<Boolean> addCasts = new ArrayList<>();
final List<RexNode> removableCasts = new ArrayList<>();
findReducibleExps(rel.getCluster().getTypeFactory(), expList,
- predicates.constantMap, constExps, addCasts, removableCasts);
+ predicates.constantMap, constExps, addCasts, removableCasts,
+ treatDynamicCallsAsNonConstant);
if (constExps.isEmpty() && removableCasts.isEmpty()) {
return changed;
}
@@ -659,10 +725,10 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
protected static void findReducibleExps(RelDataTypeFactory typeFactory,
List<RexNode> exps, ImmutableMap<RexNode, RexNode> constants,
List<RexNode> constExps, List<Boolean> addCasts,
- List<RexNode> removableCasts) {
+ List<RexNode> removableCasts, boolean treatDynamicCallsAsNonConstant) {
ReducibleExprLocator gardener =
new ReducibleExprLocator(typeFactory, constants, constExps,
- addCasts, removableCasts);
+ addCasts, removableCasts, treatDynamicCallsAsNonConstant);
for (RexNode exp : exps) {
gardener.analyze(exp);
}
@@ -828,6 +894,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
private final RelDataTypeFactory typeFactory;
+ private final boolean treatDynamicCallsAsNonConstant;
+
private final List<Constancy> stack = new ArrayList<>();
private final ImmutableMap<RexNode, RexNode> constants;
@@ -842,7 +910,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
ReducibleExprLocator(RelDataTypeFactory typeFactory,
ImmutableMap<RexNode, RexNode> constants, List<RexNode> constExprs,
- List<Boolean> addCasts, List<RexNode> removableCasts) {
+ List<Boolean> addCasts, List<RexNode> removableCasts,
+ boolean treatDynamicCallsAsNonConstant) {
// go deep
super(true);
this.typeFactory = typeFactory;
@@ -850,6 +919,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
this.constExprs = constExprs;
this.addCasts = addCasts;
this.removableCasts = removableCasts;
+ this.treatDynamicCallsAsNonConstant = treatDynamicCallsAsNonConstant;
}
public void analyze(RexNode exp) {
@@ -962,10 +1032,10 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
// be non-deterministic.
if (!call.getOperator().isDeterministic()) {
callConstancy = Constancy.NON_CONSTANT;
- } else if (call.getOperator().isDynamicFunction()) {
- // We can reduce the call to a constant, but we can't
- // cache the plan if the function is dynamic.
- // For now, treat it same as non-deterministic.
+ } else if (treatDynamicCallsAsNonConstant
+ && call.getOperator().isDynamicFunction()) {
+ // In some circumstances,, we should avoid caching the plan if we have dynamic functions.
+ // If desired, treat this situation the same as a non-deterministic function.
callConstancy = Constancy.NON_CONSTANT;
}
@@ -1069,6 +1139,117 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
}
}
}
+
+ /**
+ * An immutable object capturing the set of options to be used for reducing
+ * expressions.
+ */
+ public static final class ReductionOptions {
+ private final boolean matchNullability;
+ private final boolean unknownAsFalse;
+ private final boolean treatDynamicCallsAsNonConstant;
+
+ private ReductionOptions(
+ boolean matchNullability,
+ boolean unknownAsFalse,
+ boolean treatDynamicCallsAsNonConstant) {
+ this.matchNullability = matchNullability;
+ this.unknownAsFalse = unknownAsFalse;
+ this.treatDynamicCallsAsNonConstant = treatDynamicCallsAsNonConstant;
+ }
+
+ /**
+ * Update the options to control dynamic treatment.
+ *
+ * @param treatDynamicCallsAsNonConstant Whether to treat dynamic functions as
+ * non-constants (defaults to true)
+ * @return A new set of ReductionOptions
+ */
+ public ReductionOptions treatDynamicCallsAsNonConstant(
+ boolean treatDynamicCallsAsNonConstant) {
+ return new ReductionOptions(
+ this.matchNullability,
+ this.unknownAsFalse,
+ treatDynamicCallsAsNonConstant);
+ }
+
+ /**
+ * <p> Update the options to control nullability matching.
+ *
+ * <p>The {@code matchNullability} flag comes into play when reducing a
+ * expression whose type is nullable. Suppose we are reducing an expression
+ * {@code CASE WHEN 'a' = 'a' THEN 1 ELSE NULL END}. Before reduction the
+ * type is {@code INTEGER} (nullable), but after reduction the literal 1 has
+ * type {@code INTEGER NOT NULL}.
+ *
+ * <p>In some situations it is more important to preserve types; in this
+ * case you should use {@code matchNullability = true} (which used to be
+ * the default behavior of this method), and it will cast the literal to
+ * {@code INTEGER} (nullable).
+ *
+ * <p>In other situations, you would rather propagate the new stronger type,
+ * because it may allow further optimizations later; pass
+ * {@code matchNullability = false} and no cast will be added, but you may
+ * need to adjust types elsewhere in the expression tree.
+ *
+ * @param matchNullability Whether Calcite should add a CAST to a literal
+ * resulting from simplification and expression if the
+ * expression had nullable type and the literal is
+ * NOT NULL
+ * @return A new set of ReductionOptions
+ */
+ public ReductionOptions matchNullability(
+ boolean matchNullability) {
+ return new ReductionOptions(
+ matchNullability,
+ this.unknownAsFalse,
+ this.treatDynamicCallsAsNonConstant);
+ }
+
+ /**
+ * Update the options to control behavior around UNKNOWN handling.
+ *
+ * @param unknownAsFalse Whether UNKNOWN will be treated as FALSE
+ * @return A new set of ReductionOptions
+ */
+ public ReductionOptions unknownAsFalse(boolean unknownAsFalse) {
+ return new ReductionOptions(
+ this.matchNullability,
+ unknownAsFalse,
+ this.treatDynamicCallsAsNonConstant
+ );
+ }
+
+ /**
+ * Whether Calcite should add a CAST to a literal resulting from simplification and
+ * expression if the expression had nullable type and the literal is NOT NULL
+ *
+ * @return True if reduction should cast for nullabilities.
+ */
+ public boolean isMatchNullability() {
+ return matchNullability;
+ }
+
+ /**
+ * Whether UNKNOWN will be treated as false. Typically used in the context of a filter
+ * reduction
+ *
+ * @return If Unknown (null) values are treated as false.
+ */
+ public boolean isUnknownAsFalse() {
+ return unknownAsFalse;
+ }
+
+ /**
+ * Whether to treat dynamic calls as non-constant for purposes of reduction.
+ *
+ * @return True if dynamic calls should be treated as non-constant.
+ */
+ public boolean isTreatDynamicCallsAsNonConstant() {
+ return treatDynamicCallsAsNonConstant;
+ }
+
+ }
}
// End ReduceExpressionsRule.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/0d0fd9e9/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
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 ae82438..6f9ea77 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -90,6 +90,7 @@ import org.apache.calcite.rel.rules.ProjectWindowTransposeRule;
import org.apache.calcite.rel.rules.PruneEmptyRules;
import org.apache.calcite.rel.rules.PushProjector;
import org.apache.calcite.rel.rules.ReduceExpressionsRule;
+import org.apache.calcite.rel.rules.ReduceExpressionsRule.ProjectReduceExpressionsRule;
import org.apache.calcite.rel.rules.SemiJoinFilterTransposeRule;
import org.apache.calcite.rel.rules.SemiJoinJoinTransposeRule;
import org.apache.calcite.rel.rules.SemiJoinProjectTransposeRule;
@@ -134,6 +135,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import static org.apache.calcite.plan.RelOptRule.none;
import static org.apache.calcite.plan.RelOptRule.operand;
@@ -286,6 +288,38 @@ public class RelOptRulesTest extends RelOptTestBase {
sql(sql).with(hepPlanner).checkUnchanged();
}
+ @Test public void testReduceDynamic() {
+ testDynamic(true).get();
+ }
+
+ @Test public void testNoReduceDynamic() {
+ testDynamic(false).get();
+ }
+
+ /**
+ * Test reduction or not of a dynamic function.
+ *
+ * @param allowReduce Whether to allow dynamic functions to be reduced.
+ * @return The supplier to be executed in the context of the original test to ensure correct
+ * test name mapping.
+ */
+ private Supplier<Void> testDynamic(boolean allowReduce) {
+ HepProgramBuilder builder = new HepProgramBuilder();
+ RelOptRule rule = new ProjectReduceExpressionsRule(
+ LogicalProject.class,
+ ReduceExpressionsRule.DEFAULT_OPTIONS.treatDynamicCallsAsNonConstant(!allowReduce),
+ RelFactories.LOGICAL_BUILDER);
+ builder.addRuleInstance(rule);
+ HepPlanner hepPlanner = new HepPlanner(builder.build());
+ final String sql = "select USER from emp";
+
+ // return a supplier to be executed in the context of the original test method.
+ return () -> {
+ checkPlanning(tester, null, hepPlanner, sql, !allowReduce);
+ return null;
+ };
+ }
+
@Test public void testProjectToWindowRuleForMultipleWindows() {
HepProgram preProgram = new HepProgramBuilder()
.build();
http://git-wip-us.apache.org/repos/asf/calcite/blob/0d0fd9e9/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
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 23bf02a..1c17663 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -6842,6 +6842,28 @@ LogicalAggregate(group=[{0, 1, 2}])
]]>
</Resource>
</TestCase>
+ <TestCase name="testReduceDynamic">
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(USER=['sa'])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(USER=[USER])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testNoReduceDynamic">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(USER=[USER])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
<TestCase name="testReduceNullableToNotNull">
<Resource name="sql">
<![CDATA[select