Repository: calcite
Updated Branches:
  refs/heads/master 0d10336a3 -> 0ea976eed


[CALCITE-2041] When simplifying a nullable expression, allow the result to 
change type to NOT NULL

Enable tests for [CALCITE-1439].

Close apache/calcite#570
Close apache/calcite#563


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/0ea976ee
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/0ea976ee
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/0ea976ee

Branch: refs/heads/master
Commit: 0ea976eed7ec6a4368c6002025a1eba0377df509
Parents: 0d10336
Author: Slim <[email protected]>
Authored: Tue Nov 7 16:13:30 2017 -0800
Committer: Julian Hyde <[email protected]>
Committed: Fri Nov 24 20:43:42 2017 -0800

----------------------------------------------------------------------
 .../rel/rules/ReduceExpressionsRule.java        | 114 +++++++++++++++----
 .../calcite/rel/rules/ValuesReduceRule.java     |   3 +-
 .../java/org/apache/calcite/rex/RexUtil.java    |   9 +-
 .../calcite/sql/test/SqlOperatorBaseTest.java   |  10 +-
 .../apache/calcite/test/RelOptRulesTest.java    |  34 +++++-
 .../calcite/test/RexImplicationCheckerTest.java |  42 ++++++-
 .../org/apache/calcite/test/RelOptRulesTest.xml |  46 +++++++-
 7 files changed, 224 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/0ea976ee/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 b5fd694..47a859c 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
@@ -102,28 +102,34 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
    * {@link org.apache.calcite.rel.logical.LogicalFilter}.
    */
   public static final ReduceExpressionsRule FILTER_INSTANCE =
-      new FilterReduceExpressionsRule(LogicalFilter.class, 
RelFactories.LOGICAL_BUILDER);
+      new FilterReduceExpressionsRule(LogicalFilter.class, true,
+          RelFactories.LOGICAL_BUILDER);
 
   /**
    * Singleton rule that reduces constants inside a
    * {@link org.apache.calcite.rel.logical.LogicalProject}.
    */
   public static final ReduceExpressionsRule PROJECT_INSTANCE =
-      new ProjectReduceExpressionsRule(LogicalProject.class, 
RelFactories.LOGICAL_BUILDER);
+      new ProjectReduceExpressionsRule(LogicalProject.class, true,
+          RelFactories.LOGICAL_BUILDER);
 
   /**
    * Singleton rule that reduces constants inside a
    * {@link org.apache.calcite.rel.core.Join}.
    */
   public static final ReduceExpressionsRule JOIN_INSTANCE =
-      new JoinReduceExpressionsRule(Join.class, RelFactories.LOGICAL_BUILDER);
+      new JoinReduceExpressionsRule(Join.class, true,
+          RelFactories.LOGICAL_BUILDER);
 
   /**
    * Singleton rule that reduces constants inside a
    * {@link org.apache.calcite.rel.logical.LogicalCalc}.
    */
   public static final ReduceExpressionsRule CALC_INSTANCE =
-      new CalcReduceExpressionsRule(LogicalCalc.class, 
RelFactories.LOGICAL_BUILDER);
+      new CalcReduceExpressionsRule(LogicalCalc.class, true,
+          RelFactories.LOGICAL_BUILDER);
+
+  protected final boolean matchNullability;
 
   /**
    * Rule that reduces constants inside a {@link 
org.apache.calcite.rel.core.Filter}.
@@ -131,10 +137,16 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
    * an empty {@link org.apache.calcite.rel.core.Values} (if FALSE or NULL).
    */
   public static class FilterReduceExpressionsRule extends 
ReduceExpressionsRule {
-
+    @Deprecated // to be removed before 2.0
     public FilterReduceExpressionsRule(Class<? extends Filter> filterClass,
         RelBuilderFactory relBuilderFactory) {
-      super(filterClass, relBuilderFactory, "ReduceExpressionsRule(Filter)");
+      this(filterClass, true, relBuilderFactory);
+    }
+
+    public FilterReduceExpressionsRule(Class<? extends Filter> filterClass,
+        boolean matchNullability, RelBuilderFactory relBuilderFactory) {
+      super(filterClass, matchNullability, relBuilderFactory,
+          "ReduceExpressionsRule(Filter)");
     }
 
     @Override public void onMatch(RelOptRuleCall call) {
@@ -146,7 +158,8 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
       final RelMetadataQuery mq = call.getMetadataQuery();
       final RelOptPredicateList predicates =
           mq.getPulledUpPredicates(filter.getInput());
-      if (reduceExpressions(filter, expList, predicates, true)) {
+      if (reduceExpressions(filter, expList, predicates, true,
+          matchNullability)) {
         assert expList.size() == 1;
         newConditionExp = expList.get(0);
         reduced = true;
@@ -251,10 +264,16 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
    * Rule that reduces constants inside a {@link 
org.apache.calcite.rel.core.Project}.
    */
   public static class ProjectReduceExpressionsRule extends 
ReduceExpressionsRule {
-
+    @Deprecated // to be removed before 2.0
     public ProjectReduceExpressionsRule(Class<? extends Project> projectClass,
         RelBuilderFactory relBuilderFactory) {
-      super(projectClass, relBuilderFactory, "ReduceExpressionsRule(Project)");
+      this(projectClass, true, relBuilderFactory);
+    }
+
+    public ProjectReduceExpressionsRule(Class<? extends Project> projectClass,
+        boolean matchNullability, RelBuilderFactory relBuilderFactory) {
+      super(projectClass, matchNullability, relBuilderFactory,
+          "ReduceExpressionsRule(Project)");
     }
 
     @Override public void onMatch(RelOptRuleCall call) {
@@ -264,7 +283,8 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
           mq.getPulledUpPredicates(project.getInput());
       final List<RexNode> expList =
           Lists.newArrayList(project.getProjects());
-      if (reduceExpressions(project, expList, predicates)) {
+      if (reduceExpressions(project, expList, predicates, false,
+          matchNullability)) {
         call.transformTo(
             call.builder()
                 .push(project.getInput())
@@ -281,10 +301,16 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
    * Rule that reduces constants inside a {@link 
org.apache.calcite.rel.core.Join}.
    */
   public static class JoinReduceExpressionsRule extends ReduceExpressionsRule {
-
+    @Deprecated // to be removed before 2.0
     public JoinReduceExpressionsRule(Class<? extends Join> joinClass,
         RelBuilderFactory relBuilderFactory) {
-      super(joinClass, relBuilderFactory, "ReduceExpressionsRule(Join)");
+      this(joinClass, true, relBuilderFactory);
+    }
+
+    public JoinReduceExpressionsRule(Class<? extends Join> joinClass,
+        boolean matchNullability, RelBuilderFactory relBuilderFactory) {
+      super(joinClass, matchNullability, relBuilderFactory,
+          "ReduceExpressionsRule(Join)");
     }
 
     @Override public void onMatch(RelOptRuleCall call) {
@@ -300,7 +326,8 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
       final RelOptPredicateList predicates =
           leftPredicates.union(rexBuilder,
               rightPredicates.shift(rexBuilder, fieldCount));
-      if (!reduceExpressions(join, expList, predicates, true)) {
+      if (!reduceExpressions(join, expList, predicates, true,
+          matchNullability)) {
         return;
       }
       if (join instanceof EquiJoin) {
@@ -330,10 +357,16 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
    * Rule that reduces constants inside a {@link 
org.apache.calcite.rel.core.Calc}.
    */
   public static class CalcReduceExpressionsRule extends ReduceExpressionsRule {
-
+    @Deprecated // to be removed before 2.0
     public CalcReduceExpressionsRule(Class<? extends Calc> calcClass,
         RelBuilderFactory relBuilderFactory) {
-      super(calcClass, relBuilderFactory, "ReduceExpressionsRule(Calc)");
+      this(calcClass, true, relBuilderFactory);
+    }
+
+    public CalcReduceExpressionsRule(Class<? extends Calc> calcClass,
+        boolean matchNullability, RelBuilderFactory relBuilderFactory) {
+      super(calcClass, matchNullability, relBuilderFactory,
+          "ReduceExpressionsRule(Calc)");
     }
 
     @Override public void onMatch(RelOptRuleCall call) {
@@ -353,7 +386,8 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
         expandedExprList.add(expr.accept(shuttle));
       }
       final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
-      if (reduceExpressions(calc, expandedExprList, predicates)) {
+      if (reduceExpressions(calc, expandedExprList, predicates, false,
+          matchNullability)) {
         final RexProgramBuilder builder =
             new RexProgramBuilder(
                 calc.getInput().getRowType(),
@@ -423,10 +457,20 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
    * Creates a ReduceExpressionsRule.
    *
    * @param clazz class of rels to which this rule should apply
+   * @param matchNullability Whether to add a CAST when a nullable expression
+   *                         reduces to a NOT NULL literal
    */
   protected ReduceExpressionsRule(Class<? extends RelNode> clazz,
-      RelBuilderFactory relBuilderFactory, String desc) {
-    super(operand(clazz, any()), relBuilderFactory, desc);
+      boolean matchNullability, RelBuilderFactory relBuilderFactory,
+      String description) {
+    super(operand(clazz, any()), relBuilderFactory, description);
+    this.matchNullability = matchNullability;
+  }
+
+  @Deprecated // to be removed before 2.0
+  protected ReduceExpressionsRule(Class<? extends RelNode> clazz,
+      RelBuilderFactory relBuilderFactory, String description) {
+    this(clazz, true, relBuilderFactory, description);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -441,21 +485,48 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
    */
   protected static boolean reduceExpressions(RelNode rel, List<RexNode> 
expList,
       RelOptPredicateList predicates) {
-    return reduceExpressions(rel, expList, predicates, false);
+    return reduceExpressions(rel, expList, predicates, false, true);
+  }
+
+  @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);
   }
 
   /**
    * 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
    *
    * @return whether reduction found something to change, and succeeded
    */
   protected static boolean reduceExpressions(RelNode rel, List<RexNode> 
expList,
-      RelOptPredicateList predicates, boolean unknownAsFalse) {
+      RelOptPredicateList predicates, boolean unknownAsFalse,
+      boolean matchNullability) {
     final RelOptCluster cluster = rel.getCluster();
     final RexBuilder rexBuilder = cluster.getRexBuilder();
     final RexExecutor executor =
@@ -467,7 +538,8 @@ public abstract class ReduceExpressionsRule extends 
RelOptRule {
     boolean reduced = reduceExpressionsInternal(rel, simplify, expList,
         predicates);
 
-    final ExprSimplifier simplifier = new ExprSimplifier(simplify);
+    final ExprSimplifier simplifier =
+        new ExprSimplifier(simplify, matchNullability);
     boolean simplified = false;
     for (int i = 0; i < expList.size(); i++) {
       RexNode expr2 = simplifier.apply(expList.get(i));

http://git-wip-us.apache.org/repos/asf/calcite/blob/0ea976ee/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
index f7d5b15..b8b02c6 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java
@@ -188,7 +188,8 @@ public abstract class ValuesReduceRule extends RelOptRule {
 
     // Compute the values they reduce to.
     final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
-    ReduceExpressionsRule.reduceExpressions(values, reducibleExps, predicates);
+    ReduceExpressionsRule.reduceExpressions(values, reducibleExps, predicates,
+        false, true);
 
     int changeCount = 0;
     final ImmutableList.Builder<ImmutableList<RexLiteral>> tuplesBuilder =

http://git-wip-us.apache.org/repos/asf/calcite/blob/0ea976ee/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
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 216b27c..1dd0396 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -2574,10 +2574,17 @@ public class RexUtil {
   public static class ExprSimplifier extends RexShuttle {
     private final RexSimplify simplify;
     private final Map<RexNode, Boolean> unknownAsFalseMap;
+    private final boolean matchNullability;
 
+    @Deprecated // to be removed before 2.0
     public ExprSimplifier(RexSimplify simplify) {
+      this(simplify, true);
+    }
+
+    public ExprSimplifier(RexSimplify simplify, boolean matchNullability) {
       this.simplify = simplify;
       this.unknownAsFalseMap = new HashMap<>();
+      this.matchNullability = matchNullability;
     }
 
     @Override public RexNode visitCall(RexCall call) {
@@ -2611,7 +2618,7 @@ public class RexUtil {
       if (simplifiedNode.getType().equals(call.getType())) {
         return simplifiedNode;
       }
-      return simplify.rexBuilder.makeCast(call.getType(), simplifiedNode, 
true);
+      return simplify.rexBuilder.makeCast(call.getType(), simplifiedNode, 
matchNullability);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/0ea976ee/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java 
b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 8bd922a..e1c9e9c 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -1139,11 +1139,13 @@ public abstract class SqlOperatorBaseTest {
     tester.checkNull("cast(null as boolean)");
   }
 
-  @Ignore("[CALCITE-1439] Handling errors during constant reduction")
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-1439";>[CALCITE-1439]
+   * Handling errors during constant reduction</a>. */
   @Test public void testCastInvalid() {
-    // Constant reduction kicks in and generates Java constants that throw
-    // when the class is loaded, thus ExceptionInInitializerError. We don't 
have
-    // a fix yet.
+    // Before CALCITE-1439 was fixed, constant reduction would kick in and
+    // generate Java constants that throw when the class is loaded, thus
+    // ExceptionInInitializerError.
     tester.checkScalarExact("cast('15' as integer)", "INTEGER NOT NULL", "15");
     tester.checkFails("cast('15.4' as integer)", "xxx", true);
     tester.checkFails("cast('15.6' as integer)", "xxx", true);

http://git-wip-us.apache.org/repos/asf/calcite/blob/0ea976ee/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 53c2c6a..67da90b 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -35,6 +35,7 @@ import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.core.Minus;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.metadata.CachingRelMetadataProvider;
 import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
@@ -1876,7 +1877,10 @@ public class RelOptRulesTest extends RelOptTestBase {
             + " where a - b < 21");
   }
 
-  @Ignore @Test public void testReduceCase() throws Exception {
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-1439";>[CALCITE-1439]
+   * Handling errors during constant reduction</a>. */
+  @Test public void testReduceCase() throws Exception {
     HepProgram program = new HepProgramBuilder()
         .addRuleInstance(ReduceExpressionsRule.PROJECT_INSTANCE)
         .build();
@@ -1890,6 +1894,34 @@ public class RelOptRulesTest extends RelOptTestBase {
         .check();
   }
 
+  private void checkReduceNullableToNotNull(ReduceExpressionsRule rule) {
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(rule)
+        .build();
+
+    final String sql = "select\n"
+        + "  empno + case when 'a' = 'a' then 1 else null end as newcol\n"
+        + "from emp";
+    sql(sql).with(program)
+        .withProperty(Hook.REL_BUILDER_SIMPLIFY, false)
+        .check();
+  }
+
+  /** Test case that reduces a nullable expression to a NOT NULL literal that
+   *  is cast to nullable. */
+  @Test public void testReduceNullableToNotNull() throws Exception {
+    checkReduceNullableToNotNull(ReduceExpressionsRule.PROJECT_INSTANCE);
+  }
+
+  /** Test case that reduces a nullable expression to a NOT NULL literal. */
+  @Test public void testReduceNullableToNotNull2() throws Exception {
+    final ReduceExpressionsRule.ProjectReduceExpressionsRule rule =
+        new ReduceExpressionsRule.ProjectReduceExpressionsRule(
+            LogicalProject.class, false,
+            RelFactories.LOGICAL_BUILDER);
+    checkReduceNullableToNotNull(rule);
+  }
+
   @Test public void testReduceConstantsIsNull() throws Exception {
     HepProgram program = new HepProgramBuilder()
         .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)

http://git-wip-us.apache.org/repos/asf/calcite/blob/0ea976ee/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java 
b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
index 074a0f8..b42241c 100644
--- a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
@@ -31,6 +31,7 @@ import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexSimplify;
+import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.schema.Schemas;
 import org.apache.calcite.server.CalciteServerStatement;
@@ -51,7 +52,9 @@ import java.sql.Date;
 import java.sql.Time;
 import java.sql.Timestamp;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -187,9 +190,9 @@ public class RexImplicationCheckerTest {
     f.checkNotImplies(node1, node2);
 
     final DateString dBeforeEpoch1 = DateString.fromDaysSinceEpoch(-12345);
-    final DateString dBeforeEpcoh2 = DateString.fromDaysSinceEpoch(-123);
+    final DateString dBeforeEpoch2 = DateString.fromDaysSinceEpoch(-123);
     final RexNode nodeBe1 = f.lt(f.dt, 
f.rexBuilder.makeDateLiteral(dBeforeEpoch1));
-    final RexNode nodeBe2 = f.lt(f.dt, 
f.rexBuilder.makeDateLiteral(dBeforeEpcoh2));
+    final RexNode nodeBe2 = f.lt(f.dt, 
f.rexBuilder.makeDateLiteral(dBeforeEpoch2));
     f.checkImplies(nodeBe1, nodeBe2);
     f.checkNotImplies(nodeBe2, nodeBe1);
   }
@@ -333,6 +336,41 @@ public class RexImplicationCheckerTest {
     f.checkNotImplies(f.gt(f.i, f.literal(10)), iIsNull);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-2041";>[CALCITE-2041]
+   * When simplifying a nullable expression, allow the result to change type to
+   * NOT NULL</a> and
+   * {@link org.apache.calcite.rex.RexUtil.ExprSimplifier#matchNullability}. */
+  @Test public void testSimplifyCastMatchNullability() {
+    final Fixture f = new Fixture();
+    final RexUtil.ExprSimplifier defaultSimplifier =
+        new RexUtil.ExprSimplifier(f.simplify, true);
+    final RexUtil.ExprSimplifier nonMatchingNullabilitySimplifier =
+        new RexUtil.ExprSimplifier(f.simplify, false);
+
+    // The cast is nullable, while the literal is not nullable. When we 
simplify
+    // it, we end up with the literal. If defaultSimplifier is used, a CAST is
+    // introduced on top of the expression, as nullability of the new 
expression
+    // does not match the nullability of the original one. If
+    // nonMatchingNullabilitySimplifier is used, the CAST is not added and the
+    // simplified expression only consists of the literal.
+    final RexNode e = f.cast(f.intRelDataType, f.literal(2014));
+    assertThat(defaultSimplifier.apply(e).toString(),
+        is("CAST(2014):JavaType(class java.lang.Integer)"));
+    assertThat(nonMatchingNullabilitySimplifier.apply(e).toString(),
+        is("2014"));
+
+    // In this case, the cast is not nullable. Thus, in both cases, the
+    // simplified expression only consists of the literal.
+    RelDataType notNullIntRelDataType = 
f.typeFactory.createJavaType(int.class);
+    final RexNode e2 = f.cast(notNullIntRelDataType,
+        f.cast(notNullIntRelDataType, f.literal(2014)));
+    assertThat(defaultSimplifier.apply(e2).toString(),
+        is("2014"));
+    assertThat(nonMatchingNullabilitySimplifier.apply(e2).toString(),
+        is("2014"));
+  }
+
   /** Contains all the nourishment a test case could possibly need.
    *
    * <p>We put the data in here, rather than as fields in the test case, so 
that

http://git-wip-us.apache.org/repos/asf/calcite/blob/0ea976ee/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 b8e1131..b1c4852 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -489,11 +489,11 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$
     </TestCase>
     <TestCase name="testDecorrelateUncorrelatedInAndCorrelatedExists">
         <Resource name="sql">
-            <![CDATA["select * from sales.emp
+            <![CDATA[select * from sales.emp
 WHERE job in (
-  select job from emp ee where ee.sal=34)
-AND EXISTS (
-  select * from emp e where emp.deptno = e.deptno)]]>
+  select job from emp ee where ee.sal=34)AND EXISTS (
+  select * from emp e where emp.deptno = e.deptno)
+]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -6229,6 +6229,44 @@ LogicalAggregate(group=[{0, 1, 2}])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testReduceNullableToNotNull">
+        <Resource name="sql">
+            <![CDATA[select
+  empno + case when 'a' = 'a' then 1 else null end as newcol
+from emp]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(NEWCOL=[+($0, CASE(=('a', 'a'), 1, null))])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(NEWCOL=[+($0, CAST(1):INTEGER)])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testReduceNullableToNotNull2">
+        <Resource name="sql">
+            <![CDATA[select
+  empno + case when 'a' = 'a' then 1 else null end as newcol
+from emp]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(NEWCOL=[+($0, CASE(=('a', 'a'), 1, null))])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(NEWCOL=[+($0, 1)])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testSemiJoinRule">
         <Resource name="sql">
             <![CDATA[select dept.* from dept join (

Reply via email to