You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by vl...@apache.org on 2018/08/16 07:59:22 UTC

calcite git commit: [CALCITE-2462] RexProgramTest: move "rex building" methods to base class

Repository: calcite
Updated Branches:
  refs/heads/master 62a0de58d -> 45782ed3e


[CALCITE-2462] RexProgramTest: move "rex building" methods to base class

This simplifies creating Rex-based input data for tests

fixes #788


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

Branch: refs/heads/master
Commit: 45782ed3e071695a6f8dd6947b323906cdd5c300
Parents: 62a0de5
Author: Vladimir Sitnikov <si...@gmail.com>
Authored: Thu Aug 16 10:58:47 2018 +0300
Committer: Vladimir Sitnikov <si...@gmail.com>
Committed: Thu Aug 16 10:59:12 2018 +0300

----------------------------------------------------------------------
 .../calcite/test/RexProgramBuilderBase.java     | 555 +++++++++++++++++++
 .../org/apache/calcite/test/RexProgramTest.java | 228 +++-----
 2 files changed, 626 insertions(+), 157 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/45782ed3/core/src/test/java/org/apache/calcite/test/RexProgramBuilderBase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramBuilderBase.java b/core/src/test/java/org/apache/calcite/test/RexProgramBuilderBase.java
new file mode 100644
index 0000000..e1f84fb
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramBuilderBase.java
@@ -0,0 +1,555 @@
+/*
+ * 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.calcite.test;
+
+import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.plan.RelOptPredicateList;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeSystem;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexDynamicParam;
+import org.apache.calcite.rex.RexExecutor;
+import org.apache.calcite.rex.RexExecutorImpl;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexSimplify;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * This class provides helper methods to build rex expressions.
+ */
+public abstract class RexProgramBuilderBase {
+  /**
+   * Input variables for tests should come from a struct type, so
+   * a struct is created where the first {@code MAX_FIELDS} are nullable,
+   * and the next {@code MAX_FIELDS} are not nullable.
+   */
+  protected static final int MAX_FIELDS = 10;
+
+  protected JavaTypeFactory typeFactory;
+  protected RexBuilder rexBuilder;
+  protected RexExecutor executor;
+  protected RexSimplify simplify;
+
+  protected RexLiteral trueLiteral;
+  protected RexLiteral falseLiteral;
+  /**
+   * Nullable int literal.
+   * @deprecated since its name does not give a clue on type. Prefer {@code null_(tInt())}
+   */
+  @Deprecated
+  protected RexNode nullLiteral;
+  /**
+   * Nullable boolean literal.
+   * @deprecated prefer {@code null_(tBoolean())} or {@code nullBool}
+   */
+  @Deprecated
+  protected RexNode unknownLiteral;
+  protected RexLiteral nullBool;
+  protected RexLiteral nullInt;
+  protected RexLiteral nullVarchar;
+
+  private RelDataType nullableBool;
+  private RelDataType nonNullableBool;
+
+  private RelDataType nullableInt;
+  private RelDataType nonNullableInt;
+
+  private RelDataType nullableVarchar;
+  private RelDataType nonNullableVarchar;
+
+  // Note: JUnit 4 creates new instance for each test method,
+  // so we initialize these structures on demand
+  // It maps non-nullable type to struct of (10 nullable, 10 non-nullable) fields
+  private Map<RelDataType, RexDynamicParam> dynamicParams;
+
+  /**
+   * Dummy data context for test.
+   */
+  private static class DummyTestDataContext implements DataContext {
+    private final ImmutableMap<String, Object> map;
+
+    DummyTestDataContext() {
+      this.map =
+          ImmutableMap.of(
+              Variable.TIME_ZONE.camelName, TimeZone.getTimeZone("America/Los_Angeles"),
+              Variable.CURRENT_TIMESTAMP.camelName, 1311120000000L);
+    }
+
+    public SchemaPlus getRootSchema() {
+      return null;
+    }
+
+    public JavaTypeFactory getTypeFactory() {
+      return null;
+    }
+
+    public QueryProvider getQueryProvider() {
+      return null;
+    }
+
+    public Object get(String name) {
+      return map.get(name);
+    }
+  }
+
+  public void setUp() {
+    typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+    rexBuilder = new RexBuilder(typeFactory);
+    executor =
+        new RexExecutorImpl(new DummyTestDataContext());
+    simplify =
+        new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, executor)
+            .withParanoid(true);
+    trueLiteral = rexBuilder.makeLiteral(true);
+    falseLiteral = rexBuilder.makeLiteral(false);
+
+    nonNullableInt = typeFactory.createSqlType(SqlTypeName.INTEGER);
+    nullableInt = typeFactory.createTypeWithNullability(nonNullableInt, true);
+    nullInt = rexBuilder.makeNullLiteral(nullableInt);
+
+    nullLiteral = rexBuilder.makeNullLiteral(nonNullableInt);
+    unknownLiteral = rexBuilder.makeNullLiteral(trueLiteral.getType());
+
+    nonNullableBool = typeFactory.createSqlType(SqlTypeName.BOOLEAN);
+    nullableBool = typeFactory.createTypeWithNullability(nonNullableBool, true);
+    nullBool = rexBuilder.makeNullLiteral(nullableBool);
+
+    nonNullableVarchar = typeFactory.createSqlType(SqlTypeName.VARCHAR);
+    nullableVarchar = typeFactory.createTypeWithNullability(nonNullableVarchar, true);
+    nullVarchar = rexBuilder.makeNullLiteral(nullableVarchar);
+  }
+
+  private RexDynamicParam getDynamicParam(RelDataType type, String fieldNamePrefix) {
+    if (dynamicParams == null) {
+      dynamicParams = new HashMap<>();
+    }
+    return dynamicParams.computeIfAbsent(type, k -> {
+      RelDataType nullableType = typeFactory.createTypeWithNullability(k, true);
+      RelDataTypeFactory.Builder builder = typeFactory.builder();
+      for (int i = 0; i < MAX_FIELDS; i++) {
+        builder.add(fieldNamePrefix + i, nullableType);
+      }
+      String notNullPrefix = "notNull"
+          + Character.toUpperCase(fieldNamePrefix.charAt(0))
+          + fieldNamePrefix.substring(1);
+
+      for (int i = 0; i < MAX_FIELDS; i++) {
+        builder.add(notNullPrefix + i, k);
+      }
+      return rexBuilder.makeDynamicParam(builder.build(), 0);
+    });
+  }
+
+  protected RexNode isNull(RexNode node) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, node);
+  }
+
+  protected RexNode isNotNull(RexNode node) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, node);
+  }
+
+  protected RexNode isFalse(RexNode node) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, node);
+  }
+
+  protected RexNode isNotFalse(RexNode node) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_FALSE, node);
+  }
+
+  protected RexNode isTrue(RexNode node) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.IS_TRUE, node);
+  }
+
+  protected RexNode isNotTrue(RexNode node) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE, node);
+  }
+
+  protected RexNode nullIf(RexNode node1, RexNode node2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.NULLIF, node1, node2);
+  }
+
+  protected RexNode not(RexNode node) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.NOT, node);
+  }
+
+  protected RexNode and(RexNode... nodes) {
+    return and(ImmutableList.copyOf(nodes));
+  }
+
+  protected RexNode and(Iterable<? extends RexNode> nodes) {
+    // Does not flatten nested ANDs. We want test input to contain nested ANDs.
+    return rexBuilder.makeCall(SqlStdOperatorTable.AND,
+        ImmutableList.copyOf(nodes));
+  }
+
+  protected RexNode or(RexNode... nodes) {
+    return or(ImmutableList.copyOf(nodes));
+  }
+
+  protected RexNode or(Iterable<? extends RexNode> nodes) {
+    // Does not flatten nested ORs. We want test input to contain nested ORs.
+    return rexBuilder.makeCall(SqlStdOperatorTable.OR,
+        ImmutableList.copyOf(nodes));
+  }
+
+  protected RexNode case_(RexNode... nodes) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.CASE, nodes);
+  }
+
+  /**
+   * Creates a call to the CAST operator.
+   *
+   * <p>This method enables to create {@code CAST(42 nullable int)} expressions.</p>
+   *
+   * @param e input node
+   * @param type type to cast to
+   * @return call to CAST operator
+   */
+  protected RexNode abstractCast(RexNode e, RelDataType type) {
+    return rexBuilder.makeAbstractCast(type, e);
+  }
+
+  /**
+   * Creates a call to the CAST operator, expanding if possible, and not
+   * preserving nullability.
+   *
+   * <p>Tries to expand the cast, and therefore the result may be something
+   * other than a {@link RexCall} to the CAST operator, such as a
+   * {@link RexLiteral}.</p>
+
+   * @param e input node
+   * @param type type to cast to
+   * @return input node converted to given type
+   */
+  protected RexNode cast(RexNode e, RelDataType type) {
+    return rexBuilder.makeCast(type, e);
+  }
+
+  protected RexNode eq(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, n1, n2);
+  }
+
+  protected RexNode ne(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS, n1, n2);
+  }
+
+  protected RexNode le(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, n1, n2);
+  }
+
+  protected RexNode lt(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, n1, n2);
+  }
+
+  protected RexNode ge(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, n1, n2);
+  }
+
+  protected RexNode gt(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN, n1, n2);
+  }
+
+  protected RexNode plus(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.PLUS, n1, n2);
+  }
+
+  protected RexNode coalesce(RexNode... nodes) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.COALESCE, nodes);
+  }
+
+  protected RexNode divInt(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE_INTEGER, n1, n2);
+  }
+
+  protected RexNode sub(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.MINUS, n1, n2);
+  }
+
+  protected RexNode add(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.PLUS, n1, n2);
+  }
+
+  /**
+   * Generates {@code x IN (y, z)} expression when called as {@code in(x, y, z)}.
+   * @param node left side of the IN expression
+   * @param nodes nodes in the right side of IN expression
+   * @return IN expression
+   */
+  protected RexNode in(RexNode node, RexNode... nodes) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.IN,
+        ImmutableList.<RexNode>builder().add(node).add(nodes).build());
+  }
+
+  // Types
+  protected RelDataType nullable(RelDataType type) {
+    if (type.isNullable()) {
+      return type;
+    }
+    return typeFactory.createTypeWithNullability(type, true);
+  }
+
+  protected RelDataType tVarchar() {
+    return nonNullableVarchar;
+  }
+
+  protected RelDataType tVarchar(boolean nullable) {
+    return nullable ? nullableVarchar : nonNullableVarchar;
+  }
+
+  protected RelDataType tBoolean() {
+    return nonNullableBool;
+  }
+
+  protected RelDataType tBoolean(boolean nullable) {
+    return nullable ? nullableBool : nonNullableBool;
+  }
+
+  protected RelDataType tInt() {
+    return nonNullableInt;
+  }
+
+  protected RelDataType tInt(boolean nullable) {
+    return nullable ? nullableInt : nonNullableInt;
+  }
+
+  // Literals
+
+  /**
+   * Creates null literal with given type.
+   * For instance: {@code null_(tInt())}
+   *
+   * @param type type of required null
+   * @return null literal of a given type
+   */
+  protected RexLiteral null_(RelDataType type) {
+    return rexBuilder.makeNullLiteral(nullable(type));
+  }
+
+  protected RexNode literal(boolean value) {
+    return rexBuilder.makeLiteral(value, nonNullableBool, false);
+  }
+
+  protected RexNode literal(Boolean value) {
+    if (value == null) {
+      return rexBuilder.makeNullLiteral(nullableBool);
+    }
+    return literal(value.booleanValue());
+  }
+
+  protected RexNode literal(int value) {
+    return rexBuilder.makeLiteral(value, nonNullableInt, false);
+  }
+
+  protected RexNode literal(BigDecimal value) {
+    return rexBuilder.makeExactLiteral(value);
+  }
+
+  protected RexNode literal(BigDecimal value, RelDataType type) {
+    return rexBuilder.makeExactLiteral(value, type);
+  }
+
+  protected RexNode literal(Integer value) {
+    if (value == null) {
+      return rexBuilder.makeNullLiteral(nullableInt);
+    }
+    return literal(value.intValue());
+  }
+
+  protected RexNode literal(String value) {
+    if (value == null) {
+      return rexBuilder.makeNullLiteral(nullableVarchar);
+    }
+    return rexBuilder.makeLiteral(value, nonNullableVarchar, false);
+  }
+
+  // Variables
+
+  /**
+   * Generates input ref with given type and index.
+   *
+   * Prefer {@link #vBool()}, {@link #vInt()} and so on.
+   *
+   * The problem with "input refs" is {@code input(tInt(), 0).toString()} yields {@code $0},
+   * so the type of the expression is not printed, and it makes it hard to analyze the expressions.
+   * @param type desired type of the node
+   * @param arg argument index (0-based)
+   * @return input ref with given type and index
+   */
+  protected RexNode input(RelDataType type, int arg) {
+    return rexBuilder.makeInputRef(type, arg);
+  }
+
+  private void assertArgValue(int arg) {
+    assert arg >= 0 && arg < MAX_FIELDS
+        : "arg should be in 0.." + (MAX_FIELDS - 1) + " range. Actual value was " + arg;
+  }
+
+  /**
+   * Creates {@code nullable boolean variable} with index of 0.
+   * If you need several distinct variables, use {@link #vBool(int)}
+   * @return nullable boolean variable with index of 0
+   */
+  protected RexNode vBool() {
+    return vBool(0);
+  }
+
+  /**
+   * Creates {@code nullable boolean variable} with index of {@code arg} (0-based).
+   * The resulting node would look like {@code ?0.bool3} if {@code arg} is {@code 3}.
+   *
+   * @return nullable boolean variable with given index (0-based)
+   */
+  protected RexNode vBool(int arg) {
+    assertArgValue(arg);
+    return rexBuilder.makeFieldAccess(getDynamicParam(nonNullableBool, "bool"), arg);
+  }
+
+  /**
+   * Creates {@code non-nullable boolean variable} with index of 0.
+   * If you need several distinct variables, use {@link #vBoolNotNull(int)}.
+   * The resulting node would look like {@code ?0.notNullBool0}
+   *
+   * @return non-nullable boolean variable with index of 0
+   */
+  protected RexNode vBoolNotNull() {
+    return vBoolNotNull(0);
+  }
+
+  /**
+   * Creates {@code non-nullable boolean variable} with index of {@code arg} (0-based).
+   * The resulting node would look like {@code ?0.notNullBool3} if {@code arg} is {@code 3}.
+   *
+   * @return non-nullable boolean variable with given index (0-based)
+   */
+  protected RexNode vBoolNotNull(int arg) {
+    assertArgValue(arg);
+    return rexBuilder.makeFieldAccess(
+        getDynamicParam(nonNullableBool, "bool"),
+        arg + MAX_FIELDS);
+  }
+
+  /**
+   * Creates {@code nullable int variable} with index of 0.
+   * If you need several distinct variables, use {@link #vInt(int)}.
+   * The resulting node would look like {@code ?0.notNullInt0}
+   *
+   * @return nullable int variable with index of 0
+   */
+  protected RexNode vInt() {
+    return vInt(0);
+  }
+
+  /**
+   * Creates {@code nullable int variable} with index of {@code arg} (0-based).
+   * The resulting node would look like {@code ?0.int3} if {@code arg} is {@code 3}.
+   *
+   * @return nullable int variable with given index (0-based)
+   */
+  protected RexNode vInt(int arg) {
+    assertArgValue(arg);
+    return rexBuilder.makeFieldAccess(getDynamicParam(nonNullableInt, "int"), arg);
+  }
+
+  /**
+   * Creates {@code non-nullable int variable} with index of 0.
+   * If you need several distinct variables, use {@link #vIntNotNull(int)}.
+   * The resulting node would look like {@code ?0.notNullInt0}
+   *
+   * @return non-nullable int variable with index of 0
+   */
+  protected RexNode vIntNotNull() {
+    return vIntNotNull(0);
+  }
+
+  /**
+   * Creates {@code non-nullable int variable} with index of {@code arg} (0-based).
+   * The resulting node would look like {@code ?0.notNullInt3} if {@code arg} is {@code 3}.
+   *
+   * @return non-nullable int variable with given index (0-based)
+   */
+  protected RexNode vIntNotNull(int arg) {
+    assertArgValue(arg);
+    return rexBuilder.makeFieldAccess(
+        getDynamicParam(nonNullableInt, "int"),
+        arg + MAX_FIELDS);
+  }
+
+  /**
+   * Creates {@code nullable varchar variable} with index of 0.
+   * If you need several distinct variables, use {@link #vVarchar(int)}.
+   * The resulting node would look like {@code ?0.notNullVarchar0}
+   *
+   * @return nullable varchar variable with index of 0
+   */
+  protected RexNode vVarchar() {
+    return vVarchar(0);
+  }
+
+  /**
+   * Creates {@code nullable varchar variable} with index of {@code arg} (0-based).
+   * The resulting node would look like {@code ?0.varchar3} if {@code arg} is {@code 3}.
+   *
+   * @return nullable varchar variable with given index (0-based)
+   */
+  protected RexNode vVarchar(int arg) {
+    assertArgValue(arg);
+    return rexBuilder.makeFieldAccess(
+        getDynamicParam(nonNullableVarchar, "varchar"), arg);
+  }
+
+  /**
+   * Creates {@code non-nullable varchar variable} with index of 0.
+   * If you need several distinct variables, use {@link #vVarcharNotNull(int)}.
+   * The resulting node would look like {@code ?0.notNullVarchar0}
+   *
+   * @return non-nullable varchar variable with index of 0
+   */
+  protected RexNode vVarcharNotNull() {
+    return vVarcharNotNull(0);
+  }
+
+  /**
+   * Creates {@code non-nullable varchar variable} with index of {@code arg} (0-based).
+   * The resulting node would look like {@code ?0.notNullVarchar3} if {@code arg} is {@code 3}.
+   *
+   * @return non-nullable varchar variable with given index (0-based)
+   */
+  protected RexNode vVarcharNotNull(int arg) {
+    assertArgValue(arg);
+    return rexBuilder.makeFieldAccess(
+        getDynamicParam(nonNullableVarchar, "varchar"),
+        arg + MAX_FIELDS);
+  }
+}
+
+// End RexProgramBuilderBase.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/45782ed3/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
index 733579d..eb7988f 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -16,23 +16,15 @@
  */
 package org.apache.calcite.test;
 
-import org.apache.calcite.DataContext;
-import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.avatica.util.ByteString;
-import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
-import org.apache.calcite.linq4j.QueryProvider;
 import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.Strong;
 import org.apache.calcite.rel.metadata.NullSentinel;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelDataTypeSystem;
-import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexDynamicParam;
-import org.apache.calcite.rex.RexExecutor;
-import org.apache.calcite.rex.RexExecutorImpl;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexInterpreter;
 import org.apache.calcite.rex.RexLiteral;
@@ -42,7 +34,6 @@ import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.rex.RexProgramBuilder;
 import org.apache.calcite.rex.RexSimplify;
 import org.apache.calcite.rex.RexUtil;
-import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlSpecialOperator;
@@ -73,7 +64,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.util.TimeZone;
 import java.util.TreeMap;
 
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -87,18 +77,7 @@ import static org.junit.Assert.assertThat;
  * Unit tests for {@link RexProgram} and
  * {@link org.apache.calcite.rex.RexProgramBuilder}.
  */
-public class RexProgramTest {
-  //~ Instance fields --------------------------------------------------------
-  private JavaTypeFactory typeFactory;
-  private RexBuilder rexBuilder;
-  private RexLiteral trueLiteral;
-  private RexLiteral falseLiteral;
-  private RexNode nullLiteral;
-  private RexNode unknownLiteral;
-  private RexSimplify simplify;
-
-  //~ Methods ----------------------------------------------------------------
-
+public class RexProgramTest extends RexProgramBuilderBase {
   /**
    * Creates a RexProgramTest.
    */
@@ -106,48 +85,8 @@ public class RexProgramTest {
     super();
   }
 
-  @Before
-  public void setUp() {
-    typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
-    rexBuilder = new RexBuilder(typeFactory);
-    RexExecutor executor =
-        new RexExecutorImpl(new DummyTestDataContext());
-    simplify =
-        new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, executor)
-            .withParanoid(true);
-    trueLiteral = rexBuilder.makeLiteral(true);
-    falseLiteral = rexBuilder.makeLiteral(false);
-    final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
-    nullLiteral = rexBuilder.makeNullLiteral(intType);
-    unknownLiteral = rexBuilder.makeNullLiteral(trueLiteral.getType());
-  }
-
-  /** Dummy data context for test. */
-  private static class DummyTestDataContext implements DataContext {
-    private final ImmutableMap<String, Object> map;
-
-    DummyTestDataContext() {
-      this.map =
-          ImmutableMap.of(
-              Variable.TIME_ZONE.camelName, TimeZone.getTimeZone("America/Los_Angeles"),
-              Variable.CURRENT_TIMESTAMP.camelName, 1311120000000L);
-    }
-
-    public SchemaPlus getRootSchema() {
-      return null;
-    }
-
-    public JavaTypeFactory getTypeFactory() {
-      return null;
-    }
-
-    public QueryProvider getQueryProvider() {
-      return null;
-    }
-
-    public Object get(String name) {
-      return map.get(name);
-    }
+  @Before public void setUp() {
+    super.setUp();
   }
 
   private void checkCnf(RexNode node, String expected) {
@@ -168,6 +107,23 @@ public class RexProgramTest {
         equalTo(expected));
   }
 
+  /**
+   * Asserts that given node has expected string representation with account of node type
+   * @param message extra message that clarifies where the node came from
+   * @param expected expected string representation of the node
+   * @param node node to check
+   */
+  private void assertNode(String message, String expected, RexNode node) {
+    String actual;
+    if (node.isA(SqlKind.CAST) || node.isA(SqlKind.NEW_SPECIFICATION)) {
+      // toString contains type (see RexCall.toString)
+      actual = node.toString();
+    } else {
+      actual = node + ":" + node.getType() + (node.getType().isNullable() ? "" : " NOT NULL");
+    }
+    assertEquals(message, expected, actual);
+  }
+
   /** Simplifies an expression and checks that the result is as expected. */
   private void checkSimplify(RexNode node, String expected) {
     checkSimplify2(node, expected, expected);
@@ -220,82 +176,6 @@ public class RexProgramTest {
     return n;
   }
 
-  private RexNode isNull(RexNode node) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, node);
-  }
-
-  private RexNode isNotNull(RexNode node) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, node);
-  }
-
-  private RexNode nullIf(RexNode node1, RexNode node2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.NULLIF, node1, node2);
-  }
-
-  private RexNode not(RexNode node) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.NOT, node);
-  }
-
-  private RexNode and(RexNode... nodes) {
-    return and(ImmutableList.copyOf(nodes));
-  }
-
-  private RexNode and(Iterable<? extends RexNode> nodes) {
-    // Does not flatten nested ANDs. We want test input to contain nested ANDs.
-    return rexBuilder.makeCall(SqlStdOperatorTable.AND,
-        ImmutableList.copyOf(nodes));
-  }
-
-  private RexNode or(RexNode... nodes) {
-    return or(ImmutableList.copyOf(nodes));
-  }
-
-  private RexNode or(Iterable<? extends RexNode> nodes) {
-    // Does not flatten nested ORs. We want test input to contain nested ORs.
-    return rexBuilder.makeCall(SqlStdOperatorTable.OR,
-        ImmutableList.copyOf(nodes));
-  }
-
-  private RexNode case_(RexNode... nodes) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.CASE, nodes);
-  }
-
-  private RexNode cast(RexNode e, RelDataType type) {
-    return rexBuilder.makeCast(type, e);
-  }
-
-  private RexNode eq(RexNode n1, RexNode n2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, n1, n2);
-  }
-
-  private RexNode ne(RexNode n1, RexNode n2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS, n1, n2);
-  }
-
-  private RexNode le(RexNode n1, RexNode n2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, n1, n2);
-  }
-
-  private RexNode lt(RexNode n1, RexNode n2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, n1, n2);
-  }
-
-  private RexNode ge(RexNode n1, RexNode n2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, n1, n2);
-  }
-
-  private RexNode gt(RexNode n1, RexNode n2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN, n1, n2);
-  }
-
-  private RexNode plus(RexNode n1, RexNode n2) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.PLUS, n1, n2);
-  }
-
-  private RexNode coalesce(RexNode... nodes) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.COALESCE, nodes);
-  }
-
   /**
    * Tests construction of a RexProgram.
    */
@@ -1704,13 +1584,8 @@ public class RexProgramTest {
   }
 
   @Test public void testSimplifyCaseNullableBoolean() {
-    RexNode condition = eq(
-        rexBuilder.makeInputRef(
-            typeFactory.createTypeWithNullability(
-                typeFactory.createSqlType(SqlTypeName.VARCHAR), false),
-            0),
-        rexBuilder.makeLiteral("S"));
-    RexCall caseNode = (RexCall) case_(condition, trueLiteral, falseLiteral);
+    RexNode condition = eq(input(tVarchar(), 0), literal("S"));
+    RexNode caseNode = case_(condition, trueLiteral, falseLiteral);
 
     RexCall result = (RexCall) simplify.simplify(caseNode);
     assertThat(result.getType().isNullable(), is(false));
@@ -1719,16 +1594,8 @@ public class RexProgramTest {
   }
 
   @Test public void testSimplifyCaseNullableVarChar() {
-    RexNode condition = eq(
-        rexBuilder.makeInputRef(
-            typeFactory.createTypeWithNullability(
-                typeFactory.createSqlType(SqlTypeName.VARCHAR), false),
-            0),
-        rexBuilder.makeLiteral("S"));
-    RexLiteral aLiteral = rexBuilder.makeLiteral("A");
-    RexLiteral bLiteral = rexBuilder.makeLiteral("B");
-    RexCall caseNode = (RexCall) case_(condition, aLiteral, bLiteral);
-
+    RexNode condition = eq(input(tVarchar(), 0), literal("S"));
+    RexNode caseNode = case_(condition, literal("A"), literal("B"));
 
     RexCall result = (RexCall) simplify.simplify(caseNode);
     assertThat(result.getType().isNullable(), is(false));
@@ -1773,6 +1640,17 @@ public class RexProgramTest {
     checkSimplify(isNotNull(lt(i0, null_)), "false");
   }
 
+  @Test public void checkSimplifyDynamicParam() {
+    checkSimplify(isNotNull(lt(vInt(0), vInt(1))),
+        "AND(IS NOT NULL(?0.int0), IS NOT NULL(?0.int1))");
+    checkSimplify(isNotNull(lt(vInt(0), vIntNotNull(2))),
+        "IS NOT NULL(?0.int0)");
+    checkSimplify(isNotNull(lt(vIntNotNull(2), vIntNotNull(3))), "true");
+    checkSimplify(isNotNull(lt(vInt(0), literal(BigDecimal.ONE))),
+        "IS NOT NULL(?0.int0)");
+    checkSimplify(isNotNull(lt(vInt(0), null_(tInt()))), "false");
+  }
+
   @Test public void testSimplifyCastLiteral() {
     final List<RexLiteral> literals = new ArrayList<>();
     literals.add(
@@ -1870,6 +1748,18 @@ public class RexProgramTest {
     }
   }
 
+  @Test public void testCastLiteral() {
+    assertNode("cast(literal int not null)",
+        "42:INTEGER NOT NULL", cast(literal(42), tInt()));
+    assertNode("cast(literal int)",
+        "42:INTEGER NOT NULL", cast(literal(42), nullable(tInt())));
+
+    assertNode("abstractCast(literal int not null)",
+        "CAST(42):INTEGER NOT NULL", abstractCast(literal(42), tInt()));
+    assertNode("abstractCast(literal int)",
+        "CAST(42):INTEGER", abstractCast(literal(42), nullable(tInt())));
+  }
+
   @Test public void testSimplifyCastLiteral2() {
     final RexLiteral literalAbc = rexBuilder.makeLiteral("abc");
     final RexLiteral literalOne = rexBuilder.makeExactLiteral(BigDecimal.ONE);
@@ -2050,6 +1940,30 @@ public class RexProgramTest {
     checkSimplifyUnchanged(le(literalAbc, literalZero));
   }
 
+  @Test public void testSimpleDynamicVars() {
+    assertTypeAndToString(
+        vBool(2), "?0.bool2", "BOOLEAN");
+    assertTypeAndToString(
+        vBoolNotNull(0), "?0.notNullBool0", "BOOLEAN NOT NULL");
+
+    assertTypeAndToString(
+        vInt(2), "?0.int2", "INTEGER");
+    assertTypeAndToString(
+        vIntNotNull(0), "?0.notNullInt0", "INTEGER NOT NULL");
+
+    assertTypeAndToString(
+        vVarchar(), "?0.varchar0", "VARCHAR");
+    assertTypeAndToString(
+        vVarcharNotNull(9), "?0.notNullVarchar9", "VARCHAR NOT NULL");
+  }
+
+  private void assertTypeAndToString(
+      RexNode rexNode, String representation, String type) {
+    assertEquals(representation, rexNode.toString());
+    assertEquals("type of " + rexNode, type, rexNode.getType().toString()
+        + (rexNode.getType().isNullable() ? "" : " NOT NULL"));
+  }
+
   @Test public void testIsDeterministic() {
     SqlOperator ndc = new SqlSpecialOperator(
             "NDC",