You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2020/06/26 17:12:19 UTC

[calcite] branch master updated (6f90aca -> 7e55739)

This is an automated email from the ASF dual-hosted git repository.

jhyde pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git.


    from 6f90aca  [CALCITE-4087] Hoist, a utility to replace literals in a SQL string with placeholders
     new dae53ef  [CALCITE-3224] New implementation of RexNode-to-Expression code generation
     new d839a57  [CALCITE-4089] In Babel, allow 'CAST(integer AS DATE)' even though it is illegal in Calcite SQL
     new 7e55739  [CALCITE-4090] When generating SQL for DB2, a complex SELECT above a sub-query generates a bad table alias (Steven Talbot)

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/calcite/test/BabelQuidemTest.java   |   10 +
 babel/src/test/resources/sql/big-query.iq          |  137 ++
 .../calcite/adapter/enumerable/EnumUtils.java      |    8 +-
 .../calcite/adapter/enumerable/NullPolicy.java     |    9 -
 .../calcite/adapter/enumerable/RexImpTable.java    | 1952 +++++++++++---------
 .../adapter/enumerable/RexToLixTranslator.java     |  830 ++++++---
 .../calcite/config/CalciteConnectionProperty.java  |    4 +-
 .../apache/calcite/rel/rel2sql/SqlImplementor.java |    5 +-
 .../main/java/org/apache/calcite/rex/RexCall.java  |    3 +-
 .../org/apache/calcite/runtime/SqlFunctions.java   |   49 +-
 .../calcite/schema/impl/TableFunctionImpl.java     |    2 +-
 .../org/apache/calcite/sql/fun/SqlLibrary.java     |   20 +-
 .../calcite/sql/fun/SqlLibraryOperators.java       |   72 +
 .../org/apache/calcite/sql/type/OperandTypes.java  |    6 +
 .../org/apache/calcite/sql/type/ReturnTypes.java   |   28 +-
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |   51 +
 .../calcite/sql/test/SqlOperatorBaseTest.java      |   42 +-
 .../java/org/apache/calcite/test/JdbcTest.java     |  275 ++-
 .../test/MaterializedViewRelOptRulesTest.java      |    3 +-
 .../apache/calcite/test/ReflectiveSchemaTest.java  |   18 +-
 .../test/enumerable/EnumerableCalcTest.java        |    2 +-
 core/src/test/resources/sql/misc.iq                |   52 +
 site/_docs/reference.md                            |   14 +-
 23 files changed, 2252 insertions(+), 1340 deletions(-)
 create mode 100755 babel/src/test/resources/sql/big-query.iq


[calcite] 01/03: [CALCITE-3224] New implementation of RexNode-to-Expression code generation

Posted by jh...@apache.org.
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 dae53ef7917c46f6d5593477b2ef81b16427c541
Author: wellfengzhu <we...@gmail.com>
AuthorDate: Mon Aug 5 21:28:29 2019 +0800

    [CALCITE-3224] New implementation of RexNode-to-Expression code generation
    
    Fixes various issues where, due to common sub-expression
    elimination, a guarded value would be accessed before the
    condition had been evaluated. For example, division-by-zero
    occurs if the quotient is computed before the zero check.
    
    Issues fixed:
     * [CALCITE-3142] NullPointerException when rounding a
       nullable numeric
     * [CALCITE-3143] Dividing by NULLIF may cause
       "ArithmeticException: Division by zero"
     * [CALCITE-3150] NullPointerException when UPPER is repeated
       and combined with LIKE
     * [CALCITE-3717] Dividing by nullable numeric throws
       "ArithmeticException: Division by zero"
    
    Restore type to RexCall.equals, necessary to distinguish CAST calls (a
    bug introduced by [CALCITE-3786]).
    
    Close apache/calcite#1347
---
 .../calcite/adapter/enumerable/NullPolicy.java     |    9 -
 .../calcite/adapter/enumerable/RexImpTable.java    | 1932 +++++++++++---------
 .../adapter/enumerable/RexToLixTranslator.java     |  830 ++++++---
 .../main/java/org/apache/calcite/rex/RexCall.java  |    3 +-
 .../calcite/schema/impl/TableFunctionImpl.java     |    2 +-
 .../calcite/sql/test/SqlOperatorBaseTest.java      |    6 +
 .../java/org/apache/calcite/test/JdbcTest.java     |  275 ++-
 .../test/MaterializedViewRelOptRulesTest.java      |    3 +-
 .../apache/calcite/test/ReflectiveSchemaTest.java  |   18 +-
 .../test/enumerable/EnumerableCalcTest.java        |    2 +-
 core/src/test/resources/sql/misc.iq                |   52 +
 11 files changed, 1818 insertions(+), 1314 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java
index 1bb2111..2e2e873 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java
@@ -33,14 +33,5 @@ public enum NullPolicy {
   ANY,
   /** If the first argument is null, return null. */
   ARG0,
-  /** If any of the arguments are false, result is false; else if any
-   * arguments are null, result is null; else true. */
-  AND,
-  /** If any of the arguments are true, result is true; else if any
-   * arguments are null, result is null; else false. */
-  OR,
-  /** If any argument is true, result is false; else if any argument is null,
-   * result is null; else true. */
-  NOT,
   NONE
 }
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 839826d..e0457e9 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -16,10 +16,10 @@
  */
 package org.apache.calcite.adapter.enumerable;
 
+import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.avatica.util.TimeUnit;
 import org.apache.calcite.avatica.util.TimeUnitRange;
-import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.tree.BinaryExpression;
 import org.apache.calcite.linq4j.tree.BlockBuilder;
 import org.apache.calcite.linq4j.tree.BlockStatement;
@@ -50,6 +50,7 @@ import org.apache.calcite.sql.SqlBinaryOperator;
 import org.apache.calcite.sql.SqlJsonConstructorNullClause;
 import org.apache.calcite.sql.SqlJsonEmptyOrError;
 import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
+import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlMatchFunction;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlTypeConstructorFunction;
@@ -62,6 +63,8 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.sql.validate.SqlUserDefinedAggFunction;
 import org.apache.calcite.sql.validate.SqlUserDefinedFunction;
+import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction;
+import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.Util;
 
@@ -79,13 +82,14 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 import static org.apache.calcite.adapter.enumerable.EnumUtils.generateCollatorExpression;
 import static org.apache.calcite.linq4j.tree.ExpressionType.Add;
-import static org.apache.calcite.linq4j.tree.ExpressionType.AndAlso;
 import static org.apache.calcite.linq4j.tree.ExpressionType.Divide;
 import static org.apache.calcite.linq4j.tree.ExpressionType.Equal;
 import static org.apache.calcite.linq4j.tree.ExpressionType.GreaterThan;
@@ -94,9 +98,7 @@ import static org.apache.calcite.linq4j.tree.ExpressionType.LessThan;
 import static org.apache.calcite.linq4j.tree.ExpressionType.LessThanOrEqual;
 import static org.apache.calcite.linq4j.tree.ExpressionType.Multiply;
 import static org.apache.calcite.linq4j.tree.ExpressionType.Negate;
-import static org.apache.calcite.linq4j.tree.ExpressionType.Not;
 import static org.apache.calcite.linq4j.tree.ExpressionType.NotEqual;
-import static org.apache.calcite.linq4j.tree.ExpressionType.OrElse;
 import static org.apache.calcite.linq4j.tree.ExpressionType.Subtract;
 import static org.apache.calcite.linq4j.tree.ExpressionType.UnaryPlus;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CHR;
@@ -146,7 +148,6 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BIT_AND;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BIT_OR;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BIT_XOR;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CARDINALITY;
-import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CASE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CAST;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CBRT;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CEIL;
@@ -257,7 +258,6 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.PI;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.PLUS;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.POSITION;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.POWER;
-import static org.apache.calcite.sql.fun.SqlStdOperatorTable.PREV;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.RADIANS;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.RAND;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.RAND_INTEGER;
@@ -295,6 +295,8 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.USER;
  * Contains implementations of Rex operators as Java code.
  */
 public class RexImpTable {
+  public static final RexImpTable INSTANCE = new RexImpTable();
+
   public static final ConstantExpression NULL_EXPR =
       Expressions.constant(null);
   public static final ConstantExpression FALSE_EXPR =
@@ -308,7 +310,7 @@ public class RexImpTable {
   public static final MemberExpression BOXED_TRUE_EXPR =
       Expressions.field(null, Boolean.class, "TRUE");
 
-  private final Map<SqlOperator, CallImplementor> map = new HashMap<>();
+  private final Map<SqlOperator, RexCallImplementor> map = new HashMap<>();
   private final Map<SqlAggFunction, Supplier<? extends AggImplementor>> aggMap =
       new HashMap<>();
   private final Map<SqlAggFunction, Supplier<? extends WinAggImplementor>> winAggMap =
@@ -319,7 +321,7 @@ public class RexImpTable {
       tvfImplementorMap = new HashMap<>();
 
   RexImpTable() {
-    defineMethod(ROW, BuiltInMethod.ARRAY.method, NullPolicy.ANY);
+    defineMethod(ROW, BuiltInMethod.ARRAY.method, NullPolicy.NONE);
     defineMethod(UPPER, BuiltInMethod.UPPER.method, NullPolicy.STRICT);
     defineMethod(LOWER, BuiltInMethod.LOWER.method, NullPolicy.STRICT);
     defineMethod(INITCAP,  BuiltInMethod.INITCAP.method, NullPolicy.STRICT);
@@ -351,13 +353,12 @@ public class RexImpTable {
     defineMethod(DIFFERENCE, BuiltInMethod.DIFFERENCE.method, NullPolicy.STRICT);
     defineMethod(REVERSE, BuiltInMethod.REVERSE.method, NullPolicy.STRICT);
 
-    final TrimImplementor trimImplementor = new TrimImplementor();
-    defineImplementor(TRIM, NullPolicy.STRICT, trimImplementor, false);
+    map.put(TRIM, new TrimImplementor());
 
     // logical
-    defineBinary(AND, AndAlso, NullPolicy.AND, null);
-    defineBinary(OR, OrElse, NullPolicy.OR, null);
-    defineUnary(NOT, Not, NullPolicy.NOT);
+    map.put(AND, new LogicalAndImplementor());
+    map.put(OR, new LogicalOrImplementor());
+    map.put(NOT, new LogicalNotImplementor());
 
     // comparisons
     defineBinary(LESS_THAN, LessThan, NullPolicy.STRICT, "lt");
@@ -384,33 +385,8 @@ public class RexImpTable {
     defineMethod(LOG10, "log10", NullPolicy.STRICT);
     defineMethod(ABS, "abs", NullPolicy.STRICT);
 
-    defineImplementor(RAND, NullPolicy.STRICT,
-        new NotNullImplementor() {
-          final NotNullImplementor[] implementors = {
-              new ReflectiveCallNotNullImplementor(BuiltInMethod.RAND.method),
-              new ReflectiveCallNotNullImplementor(BuiltInMethod.RAND_SEED.method)
-          };
-          public Expression implement(RexToLixTranslator translator,
-              RexCall call, List<Expression> translatedOperands) {
-            return implementors[call.getOperands().size()]
-                .implement(translator, call, translatedOperands);
-          }
-        }, false);
-    defineImplementor(RAND_INTEGER, NullPolicy.STRICT,
-        new NotNullImplementor() {
-          final NotNullImplementor[] implementors = {
-              null,
-              new ReflectiveCallNotNullImplementor(
-                BuiltInMethod.RAND_INTEGER.method),
-              new ReflectiveCallNotNullImplementor(
-                BuiltInMethod.RAND_INTEGER_SEED.method)
-          };
-          public Expression implement(RexToLixTranslator translator,
-              RexCall call, List<Expression> translatedOperands) {
-            return implementors[call.getOperands().size()]
-                .implement(translator, call, translatedOperands);
-          }
-        }, false);
+    map.put(RAND, new RandImplementor());
+    map.put(RAND_INTEGER, new RandIntegerImplementor());
 
     defineMethod(ACOS, "acos", NullPolicy.STRICT);
     defineMethod(ASIN, "asin", NullPolicy.STRICT);
@@ -430,82 +406,63 @@ public class RexImpTable {
     defineMethod(TANH, "tanh", NullPolicy.STRICT);
     defineMethod(TRUNCATE, "struncate", NullPolicy.STRICT);
 
-    map.put(PI, (translator, call, nullAs) -> Expressions.constant(Math.PI));
+    map.put(PI, new PiImplementor());
 
     // datetime
-    defineImplementor(DATETIME_PLUS, NullPolicy.STRICT,
-        new DatetimeArithmeticImplementor(), false);
-    defineImplementor(MINUS_DATE, NullPolicy.STRICT,
-        new DatetimeArithmeticImplementor(), false);
-    defineImplementor(EXTRACT, NullPolicy.STRICT,
-        new ExtractImplementor(), false);
-    defineImplementor(FLOOR, NullPolicy.STRICT,
+    map.put(DATETIME_PLUS, new DatetimeArithmeticImplementor());
+    map.put(MINUS_DATE, new DatetimeArithmeticImplementor());
+    map.put(EXTRACT, new ExtractImplementor());
+    map.put(FLOOR,
         new FloorImplementor(BuiltInMethod.FLOOR.method.getName(),
             BuiltInMethod.UNIX_TIMESTAMP_FLOOR.method,
-            BuiltInMethod.UNIX_DATE_FLOOR.method), false);
-    defineImplementor(CEIL, NullPolicy.STRICT,
+            BuiltInMethod.UNIX_DATE_FLOOR.method));
+    map.put(CEIL,
         new FloorImplementor(BuiltInMethod.CEIL.method.getName(),
             BuiltInMethod.UNIX_TIMESTAMP_CEIL.method,
-            BuiltInMethod.UNIX_DATE_CEIL.method), false);
+            BuiltInMethod.UNIX_DATE_CEIL.method));
 
     defineMethod(LAST_DAY, "lastDay", NullPolicy.STRICT);
-    defineImplementor(DAYNAME, NullPolicy.STRICT,
+    map.put(DAYNAME,
         new PeriodNameImplementor("dayName",
             BuiltInMethod.DAYNAME_WITH_TIMESTAMP,
-            BuiltInMethod.DAYNAME_WITH_DATE), false);
-    defineImplementor(MONTHNAME, NullPolicy.STRICT,
+            BuiltInMethod.DAYNAME_WITH_DATE));
+    map.put(MONTHNAME,
         new PeriodNameImplementor("monthName",
             BuiltInMethod.MONTHNAME_WITH_TIMESTAMP,
-            BuiltInMethod.MONTHNAME_WITH_DATE), false);
+            BuiltInMethod.MONTHNAME_WITH_DATE));
 
-    map.put(IS_NULL, new IsXxxImplementor(null, false));
-    map.put(IS_NOT_NULL, new IsXxxImplementor(null, true));
-    map.put(IS_TRUE, new IsXxxImplementor(true, false));
-    map.put(IS_NOT_TRUE, new IsXxxImplementor(true, true));
-    map.put(IS_FALSE, new IsXxxImplementor(false, false));
-    map.put(IS_NOT_FALSE, new IsXxxImplementor(false, true));
+    map.put(IS_NULL, new IsNullImplementor());
+    map.put(IS_NOT_NULL, new IsNotNullImplementor());
+    map.put(IS_TRUE, new IsTrueImplementor());
+    map.put(IS_NOT_TRUE, new IsNotTrueImplementor());
+    map.put(IS_FALSE, new IsFalseImplementor());
+    map.put(IS_NOT_FALSE, new IsNotFalseImplementor());
 
     // LIKE and SIMILAR
     final MethodImplementor likeImplementor =
-        new MethodImplementor(BuiltInMethod.LIKE.method);
-    defineImplementor(LIKE, NullPolicy.STRICT, likeImplementor, false);
-    defineImplementor(NOT_LIKE, NullPolicy.STRICT,
-        NotImplementor.of(likeImplementor), false);
+        new MethodImplementor(BuiltInMethod.LIKE.method, NullPolicy.STRICT,
+            false);
+    map.put(LIKE, likeImplementor);
+    map.put(NOT_LIKE, likeImplementor);
     final MethodImplementor similarImplementor =
-        new MethodImplementor(BuiltInMethod.SIMILAR.method);
-    defineImplementor(SIMILAR_TO, NullPolicy.STRICT, similarImplementor, false);
-    defineImplementor(NOT_SIMILAR_TO, NullPolicy.STRICT,
-        NotImplementor.of(similarImplementor), false);
+        new MethodImplementor(BuiltInMethod.SIMILAR.method, NullPolicy.STRICT,
+            false);
+    map.put(SIMILAR_TO, similarImplementor);
+    map.put(NOT_SIMILAR_TO, NotImplementor.of(similarImplementor));
 
     // POSIX REGEX
     final MethodImplementor posixRegexImplementor =
-        new MethodImplementor(BuiltInMethod.POSIX_REGEX.method);
-    defineImplementor(SqlStdOperatorTable.POSIX_REGEX_CASE_INSENSITIVE, NullPolicy.STRICT,
-        posixRegexImplementor, false);
-    defineImplementor(SqlStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE, NullPolicy.STRICT,
-        posixRegexImplementor, false);
-    defineImplementor(SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_INSENSITIVE, NullPolicy.STRICT,
-        NotImplementor.of(posixRegexImplementor), false);
-    defineImplementor(SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_SENSITIVE, NullPolicy.STRICT,
-        NotImplementor.of(posixRegexImplementor), false);
-    defineImplementor(REGEXP_REPLACE, NullPolicy.STRICT,
-        new NotNullImplementor() {
-          final NotNullImplementor[] implementors = {
-              new ReflectiveCallNotNullImplementor(
-                  BuiltInMethod.REGEXP_REPLACE3.method),
-              new ReflectiveCallNotNullImplementor(
-                  BuiltInMethod.REGEXP_REPLACE4.method),
-              new ReflectiveCallNotNullImplementor(
-                  BuiltInMethod.REGEXP_REPLACE5.method),
-              new ReflectiveCallNotNullImplementor(
-                  BuiltInMethod.REGEXP_REPLACE6.method)
-          };
-          public Expression implement(RexToLixTranslator translator, RexCall call,
-              List<Expression> translatedOperands) {
-            return implementors[call.getOperands().size() - 3]
-                .implement(translator, call, translatedOperands);
-          }
-        }, false);
+        new MethodImplementor(BuiltInMethod.POSIX_REGEX.method,
+            NullPolicy.STRICT, false);
+    map.put(SqlStdOperatorTable.POSIX_REGEX_CASE_INSENSITIVE,
+        posixRegexImplementor);
+    map.put(SqlStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE,
+        posixRegexImplementor);
+    map.put(SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_INSENSITIVE,
+        NotImplementor.of(posixRegexImplementor));
+    map.put(SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_SENSITIVE,
+        NotImplementor.of(posixRegexImplementor));
+    map.put(REGEXP_REPLACE, new RegexpReplaceImplementor());
 
     // Multisets & arrays
     defineMethod(CARDINALITY, BuiltInMethod.COLLECTION_SIZE.method,
@@ -515,15 +472,15 @@ public class RexImpTable {
     defineMethod(STRUCT_ACCESS, BuiltInMethod.STRUCT_ACCESS.method, NullPolicy.ANY);
     defineMethod(MEMBER_OF, BuiltInMethod.MEMBER_OF.method, NullPolicy.NONE);
     final MethodImplementor isEmptyImplementor =
-        new MethodImplementor(BuiltInMethod.IS_EMPTY.method);
-    defineImplementor(IS_EMPTY, NullPolicy.NONE, isEmptyImplementor, false);
-    defineImplementor(IS_NOT_EMPTY, NullPolicy.NONE,
-        NotImplementor.of(isEmptyImplementor), false);
+        new MethodImplementor(BuiltInMethod.IS_EMPTY.method, NullPolicy.NONE,
+            false);
+    map.put(IS_EMPTY, isEmptyImplementor);
+    map.put(IS_NOT_EMPTY, NotImplementor.of(isEmptyImplementor));
     final MethodImplementor isASetImplementor =
-        new MethodImplementor(BuiltInMethod.IS_A_SET.method);
-    defineImplementor(IS_A_SET, NullPolicy.NONE, isASetImplementor, false);
-    defineImplementor(IS_NOT_A_SET, NullPolicy.NONE,
-        NotImplementor.of(isASetImplementor), false);
+        new MethodImplementor(BuiltInMethod.IS_A_SET.method, NullPolicy.NONE,
+            false);
+    map.put(IS_A_SET, isASetImplementor);
+    map.put(IS_NOT_A_SET, NotImplementor.of(isASetImplementor));
     defineMethod(MULTISET_INTERSECT_DISTINCT,
         BuiltInMethod.MULTISET_INTERSECT_DISTINCT.method, NullPolicy.NONE);
     defineMethod(MULTISET_INTERSECT,
@@ -535,28 +492,27 @@ public class RexImpTable {
         BuiltInMethod.MULTISET_UNION_DISTINCT.method, NullPolicy.NONE);
     defineMethod(MULTISET_UNION, BuiltInMethod.MULTISET_UNION_ALL.method, NullPolicy.NONE);
     final MethodImplementor subMultisetImplementor =
-        new MethodImplementor(BuiltInMethod.SUBMULTISET_OF.method);
-    defineImplementor(SUBMULTISET_OF, NullPolicy.NONE, subMultisetImplementor, false);
-    defineImplementor(NOT_SUBMULTISET_OF,
-        NullPolicy.NONE, NotImplementor.of(subMultisetImplementor), false);
+        new MethodImplementor(BuiltInMethod.SUBMULTISET_OF.method, NullPolicy.NONE, false);
+    map.put(SUBMULTISET_OF, subMultisetImplementor);
+    map.put(NOT_SUBMULTISET_OF, NotImplementor.of(subMultisetImplementor));
 
-    map.put(CASE, new CaseImplementor());
     map.put(COALESCE, new CoalesceImplementor());
-    map.put(CAST, new CastOptimizedImplementor());
+    map.put(CAST, new CastImplementor());
 
-    defineImplementor(REINTERPRET, NullPolicy.STRICT,
-        new ReinterpretImplementor(), false);
+    map.put(REINTERPRET, new ReinterpretImplementor());
 
-    final CallImplementor value = new ValueConstructorImplementor();
+    final RexCallImplementor value = new ValueConstructorImplementor();
     map.put(MAP_VALUE_CONSTRUCTOR, value);
     map.put(ARRAY_VALUE_CONSTRUCTOR, value);
     map.put(ITEM, new ItemImplementor());
 
-    map.put(DEFAULT, (translator, call, nullAs) -> Expressions.constant(null));
+    map.put(DEFAULT, new DefaultImplementor());
 
     // Sequences
-    defineMethod(CURRENT_VALUE, BuiltInMethod.SEQUENCE_CURRENT_VALUE.method, NullPolicy.STRICT);
-    defineMethod(NEXT_VALUE, BuiltInMethod.SEQUENCE_NEXT_VALUE.method, NullPolicy.STRICT);
+    defineMethod(CURRENT_VALUE, BuiltInMethod.SEQUENCE_CURRENT_VALUE.method,
+        NullPolicy.STRICT);
+    defineMethod(NEXT_VALUE, BuiltInMethod.SEQUENCE_NEXT_VALUE.method,
+        NullPolicy.STRICT);
 
     // Compression Operators
     defineMethod(COMPRESS, BuiltInMethod.COMPRESS.method, NullPolicy.ARG0);
@@ -571,8 +527,8 @@ public class RexImpTable {
     defineMethod(JSON_VALUE_EXPRESSION,
         BuiltInMethod.JSON_VALUE_EXPRESSION.method, NullPolicy.STRICT);
     defineMethod(JSON_EXISTS, BuiltInMethod.JSON_EXISTS.method, NullPolicy.ARG0);
-    defineImplementor(JSON_VALUE, NullPolicy.ARG0,
-        new JsonValueImplementor(BuiltInMethod.JSON_VALUE.method), false);
+    map.put(JSON_VALUE,
+        new JsonValueImplementor(BuiltInMethod.JSON_VALUE.method));
     defineMethod(JSON_QUERY, BuiltInMethod.JSON_QUERY.method, NullPolicy.ARG0);
     defineMethod(JSON_TYPE, BuiltInMethod.JSON_TYPE.method, NullPolicy.ARG0);
     defineMethod(JSON_DEPTH, BuiltInMethod.JSON_DEPTH.method, NullPolicy.ARG0);
@@ -595,26 +551,34 @@ public class RexImpTable {
     aggMap.put(JSON_ARRAYAGG.with(SqlJsonConstructorNullClause.NULL_ON_NULL),
         JsonArrayAggImplementor
             .supplierFor(BuiltInMethod.JSON_ARRAYAGG_ADD.method));
-    defineImplementor(IS_JSON_VALUE, NullPolicy.NONE,
-        new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method), false);
-    defineImplementor(IS_JSON_OBJECT, NullPolicy.NONE,
-        new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method), false);
-    defineImplementor(IS_JSON_ARRAY, NullPolicy.NONE,
-        new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method), false);
-    defineImplementor(IS_JSON_SCALAR, NullPolicy.NONE,
-        new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method), false);
-    defineImplementor(IS_NOT_JSON_VALUE, NullPolicy.NONE,
+    map.put(IS_JSON_VALUE,
+        new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method,
+            NullPolicy.NONE, false));
+    map.put(IS_JSON_OBJECT,
+        new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method,
+            NullPolicy.NONE, false));
+    map.put(IS_JSON_ARRAY,
+        new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method,
+            NullPolicy.NONE, false));
+    map.put(IS_JSON_SCALAR,
+        new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method,
+            NullPolicy.NONE, false));
+    map.put(IS_NOT_JSON_VALUE,
         NotImplementor.of(
-            new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method)), false);
-    defineImplementor(IS_NOT_JSON_OBJECT, NullPolicy.NONE,
+            new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method,
+                NullPolicy.NONE, false)));
+    map.put(IS_NOT_JSON_OBJECT,
         NotImplementor.of(
-            new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method)), false);
-    defineImplementor(IS_NOT_JSON_ARRAY, NullPolicy.NONE,
+            new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method,
+                NullPolicy.NONE, false)));
+    map.put(IS_NOT_JSON_ARRAY,
         NotImplementor.of(
-            new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method)), false);
-    defineImplementor(IS_NOT_JSON_SCALAR, NullPolicy.NONE,
+            new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method,
+                NullPolicy.NONE, false)));
+    map.put(IS_NOT_JSON_SCALAR,
         NotImplementor.of(
-            new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method)), false);
+            new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method,
+                NullPolicy.NONE, false)));
 
     // System functions
     final SystemFunctionImplementor systemFunctionImplementor =
@@ -645,7 +609,8 @@ public class RexImpTable {
     aggMap.put(ANY_VALUE, minMax);
     aggMap.put(SOME, minMax);
     aggMap.put(EVERY, minMax);
-    final Supplier<BitOpImplementor> bitop = constructorSupplier(BitOpImplementor.class);
+    final Supplier<BitOpImplementor> bitop =
+        constructorSupplier(BitOpImplementor.class);
     aggMap.put(BIT_AND, bitop);
     aggMap.put(BIT_OR, bitop);
     aggMap.put(BIT_XOR, bitop);
@@ -674,7 +639,7 @@ public class RexImpTable {
     // Functions for MATCH_RECOGNIZE
     matchMap.put(CLASSIFIER, ClassifierImplementor::new);
     matchMap.put(LAST, LastImplementor::new);
-    map.put(PREV, new PrevImplementor());
+
     tvfImplementorMap.put(TUMBLE, TumbleImplementor::new);
     tvfImplementorMap.put(HOP, HopImplementor::new);
     tvfImplementorMap.put(SESSION, SessionImplementor::new);
@@ -699,228 +664,76 @@ public class RexImpTable {
     };
   }
 
-  private void defineImplementor(
-      SqlOperator operator,
-      NullPolicy nullPolicy,
-      NotNullImplementor implementor,
-      boolean harmonize) {
-    CallImplementor callImplementor =
-        createImplementor(implementor, nullPolicy, harmonize);
-    map.put(operator, callImplementor);
-  }
-
-  private static RexCall call2(
-      boolean harmonize,
-      RexToLixTranslator translator,
-      RexCall call) {
-    if (!harmonize) {
-      return call;
-    }
-    final List<RexNode> operands2 =
-        harmonize(translator, call.getOperands());
-    if (operands2.equals(call.getOperands())) {
-      return call;
-    }
-    return call.clone(call.getType(), operands2);
-  }
-
   public static CallImplementor createImplementor(
       final NotNullImplementor implementor,
       final NullPolicy nullPolicy,
       final boolean harmonize) {
-    switch (nullPolicy) {
-    case ANY:
-    case STRICT:
-    case SEMI_STRICT:
-    case ARG0:
-      return (translator, call, nullAs) -> implementNullSemantics0(
-          translator, call, nullAs, nullPolicy, harmonize,
-          implementor);
-    case AND:
-/* TODO:
-            if (nullAs == NullAs.FALSE) {
-                nullPolicy2 = NullPolicy.ANY;
-            }
-*/
-      // If any of the arguments are false, result is false;
-      // else if any arguments are null, result is null;
-      // else true.
-      //
-      // b0 == null ? (b1 == null || b1 ? null : Boolean.FALSE)
-      //   : b0 ? b1
-      //   : Boolean.FALSE;
-      return (translator, call, nullAs) -> {
-        assert call.getOperator() == AND
-            : "AND null semantics is supported only for AND operator. Actual operator is "
-            + String.valueOf(call.getOperator());
-        final RexCall call2 = call2(false, translator, call);
-        switch (nullAs) {
-        case NOT_POSSIBLE:
-          // This doesn't mean that none of the arguments might be null, ex: (s and s is not null)
-          nullAs = NullAs.TRUE;
-          // fallthru
-        case TRUE:
-          // AND call should return false iff has FALSEs,
-          // thus if we convert nulls to true then no harm is made
-        case FALSE:
-          // AND call should return false iff has FALSEs or has NULLs,
-          // thus if we convert nulls to false, no harm is made
-          final List<Expression> expressions =
-              translator.translateList(call2.getOperands(), nullAs);
-          return Expressions.foldAnd(expressions);
-        case NULL:
-        case IS_NULL:
-        case IS_NOT_NULL:
-          final List<Expression> nullAsTrue =
-              translator.translateList(call2.getOperands(), NullAs.TRUE);
-          final List<Expression> nullAsIsNull =
-              translator.translateList(call2.getOperands(), NullAs.IS_NULL);
-          Expression hasFalse =
-              Expressions.not(Expressions.foldAnd(nullAsTrue));
-          Expression hasNull = Expressions.foldOr(nullAsIsNull);
-          return nullAs.handle(
-              Expressions.condition(hasFalse, BOXED_FALSE_EXPR,
-                  Expressions.condition(hasNull, NULL_EXPR, BOXED_TRUE_EXPR)));
-        default:
-          throw new IllegalArgumentException(
-              "Unknown nullAs when implementing AND: " + nullAs);
-        }
-      };
-    case OR:
-      // If any of the arguments are true, result is true;
-      // else if any arguments are null, result is null;
-      // else false.
-      //
-      // b0 == null ? (b1 == null || !b1 ? null : Boolean.TRUE)
-      //   : !b0 ? b1
-      //   : Boolean.TRUE;
-      return (translator, call, nullAs) -> {
-        assert call.getOperator() == OR
-            : "OR null semantics is supported only for OR operator. Actual operator is "
-            + String.valueOf(call.getOperator());
-        final RexCall call2 = call2(harmonize, translator, call);
-        switch (nullAs) {
-        case NOT_POSSIBLE:
-          // This doesn't mean that none of the arguments might be null, ex: (s or s is null)
-          nullAs = NullAs.FALSE;
-          // fallthru
-        case TRUE:
-          // This should return false iff all arguments are FALSE,
-          // thus we convert nulls to TRUE and foldOr
-        case FALSE:
-          // This should return true iff has TRUE arguments,
-          // thus we convert nulls to FALSE and foldOr
-          final List<Expression> expressions =
-              translator.translateList(call2.getOperands(), nullAs);
-          return Expressions.foldOr(expressions);
-        case NULL:
-        case IS_NULL:
-        case IS_NOT_NULL:
-          final List<Expression> nullAsFalse =
-              translator.translateList(call2.getOperands(), NullAs.FALSE);
-          final List<Expression> nullAsIsNull =
-              translator.translateList(call2.getOperands(), NullAs.IS_NULL);
-          Expression hasTrue = Expressions.foldOr(nullAsFalse);
-          Expression hasNull = Expressions.foldOr(nullAsIsNull);
-          Expression result = nullAs.handle(
-              Expressions.condition(hasTrue, BOXED_TRUE_EXPR,
-                  Expressions.condition(hasNull, NULL_EXPR, BOXED_FALSE_EXPR)));
-          return result;
-        default:
-          throw new IllegalArgumentException(
-              "Unknown nullAs when implementing OR: " + nullAs);
-        }
-      };
-    case NOT:
-      // If any of the arguments are false, result is true;
-      // else if any arguments are null, result is null;
-      // else false.
-      return new CallImplementor() {
-        public Expression implement(RexToLixTranslator translator, RexCall call,
-            NullAs nullAs) {
-          switch (nullAs) {
-          case NULL:
-            return Expressions.call(BuiltInMethod.NOT.method,
-                translator.translateList(call.getOperands(), nullAs));
-          default:
-            return Expressions.not(
-                translator.translate(call.getOperands().get(0),
-                    negate(nullAs)));
-          }
-        }
-
-        private NullAs negate(NullAs nullAs) {
-          switch (nullAs) {
-          case FALSE:
-            return NullAs.TRUE;
-          case TRUE:
-            return NullAs.FALSE;
-          case IS_NULL:
-            return NullAs.IS_NOT_NULL;
-          case IS_NOT_NULL:
-            return NullAs.IS_NULL;
-          default:
-            return nullAs;
-          }
-        }
-      };
-    case NONE:
-      return (translator, call, nullAs) -> {
-        final RexCall call2 = call2(false, translator, call);
-        return implementCall(
-            translator, call2, implementor, nullAs);
-      };
-    default:
-      throw new AssertionError(nullPolicy);
-    }
+    return (translator, call, nullAs) -> {
+      final RexCallImplementor rexCallImplementor =
+          createRexCallImplementor(implementor, nullPolicy, harmonize);
+      final List<RexToLixTranslator.Result> arguments =
+          translator.getCallOperandResult(call);
+      assert arguments != null;
+      final RexToLixTranslator.Result result =
+          rexCallImplementor.implement(translator, call, arguments);
+      return nullAs.handle(result.valueVariable);
+    };
   }
 
-  private void defineMethod(
-      SqlOperator operator, String functionName, NullPolicy nullPolicy) {
-    defineImplementor(
-        operator,
-        nullPolicy,
-        new MethodNameImplementor(functionName),
-        false);
+  private void defineMethod(SqlOperator operator, String functionName,
+      NullPolicy nullPolicy) {
+    map.put(operator,
+        new MethodNameImplementor(functionName, nullPolicy, false));
   }
 
-  private void defineMethod(
-      SqlOperator operator, Method method, NullPolicy nullPolicy) {
-    defineImplementor(
-        operator, nullPolicy, new MethodImplementor(method), false);
+  private void defineMethod(SqlOperator operator, Method method,
+      NullPolicy nullPolicy) {
+    map.put(operator, new MethodImplementor(method, nullPolicy, false));
   }
 
-  private void defineMethodReflective(
-      SqlOperator operator, Method method, NullPolicy nullPolicy) {
-    defineImplementor(
-        operator, nullPolicy, new ReflectiveCallNotNullImplementor(method),
-        false);
+  private void defineUnary(SqlOperator operator, ExpressionType expressionType,
+      NullPolicy nullPolicy) {
+    map.put(operator, new UnaryImplementor(expressionType, nullPolicy));
   }
 
-  private void defineUnary(
-      SqlOperator operator, ExpressionType expressionType,
-      NullPolicy nullPolicy) {
-    defineImplementor(
-        operator,
-        nullPolicy,
-        new UnaryImplementor(expressionType), false);
+  private void defineBinary(SqlOperator operator, ExpressionType expressionType,
+      NullPolicy nullPolicy, String backupMethodName) {
+    map.put(operator,
+        new BinaryImplementor(nullPolicy, true, expressionType,
+            backupMethodName));
   }
 
-  private void defineBinary(
-      SqlOperator operator,
-      ExpressionType expressionType,
-      NullPolicy nullPolicy,
-      String backupMethodName) {
-    defineImplementor(
-        operator,
-        nullPolicy,
-        new BinaryImplementor(expressionType, backupMethodName),
-        true);
+  private static RexCallImplementor createRexCallImplementor(
+      final NotNullImplementor implementor,
+      final NullPolicy nullPolicy,
+      final boolean harmonize) {
+    return new AbstractRexCallImplementor(nullPolicy, harmonize) {
+      @Override String getVariableName() {
+        return "not_null_udf";
+      }
+
+      @Override Expression implementSafe(RexToLixTranslator translator,
+          RexCall call, List<Expression> argValueList) {
+        return implementor.implement(translator, call, argValueList);
+      }
+    };
   }
 
-  public static final RexImpTable INSTANCE = new RexImpTable();
+  private static RexCallImplementor wrapAsRexCallImplementor(
+      final CallImplementor implementor) {
+    return new AbstractRexCallImplementor(NullPolicy.NONE, false) {
+      @Override String getVariableName() {
+        return "udf";
+      }
 
-  public CallImplementor get(final SqlOperator operator) {
+      @Override Expression implementSafe(RexToLixTranslator translator,
+          RexCall call, List<Expression> argValueList) {
+        return implementor.implement(translator, call, RexImpTable.NullAs.NULL);
+      }
+    };
+  }
+
+  public RexCallImplementor get(final SqlOperator operator) {
     if (operator instanceof SqlUserDefinedFunction) {
       org.apache.calcite.schema.Function udf =
           ((SqlUserDefinedFunction) operator).getFunction();
@@ -928,7 +741,9 @@ public class RexImpTable {
         throw new IllegalStateException("User defined function " + operator
             + " must implement ImplementableFunction");
       }
-      return ((ImplementableFunction) udf).getImplementor();
+      CallImplementor implementor =
+          ((ImplementableFunction) udf).getImplementor();
+      return wrapAsRexCallImplementor(implementor);
     } else if (operator instanceof SqlTypeConstructorFunction) {
       return map.get(SqlStdOperatorTable.ROW);
     }
@@ -984,14 +799,6 @@ public class RexImpTable {
     }
   }
 
-  static Expression maybeNegate(boolean negate, Expression expression) {
-    if (!negate) {
-      return expression;
-    } else {
-      return Expressions.not(expression);
-    }
-  }
-
   static Expression optimize(Expression expression) {
     return expression.accept(new OptimizeShuttle());
   }
@@ -1003,56 +810,12 @@ public class RexImpTable {
     } else {
       return optimize(
           Expressions.condition(
-              Expressions.equal(
-                  operand,
-                  NULL_EXPR),
+              Expressions.equal(operand, NULL_EXPR),
               NULL_EXPR,
               expression));
     }
   }
 
-  private static boolean nullable(RexCall call, int i) {
-    return call.getOperands().get(i).getType().isNullable();
-  }
-
-  /** Ensures that operands have identical type. */
-  private static List<RexNode> harmonize(
-      final RexToLixTranslator translator, final List<RexNode> operands) {
-    int nullCount = 0;
-    final List<RelDataType> types = new ArrayList<>();
-    final RelDataTypeFactory typeFactory =
-        translator.builder.getTypeFactory();
-    for (RexNode operand : operands) {
-      RelDataType type = operand.getType();
-      type = toSql(typeFactory, type);
-      if (translator.isNullable(operand)) {
-        ++nullCount;
-      } else {
-        type = typeFactory.createTypeWithNullability(type, false);
-      }
-      types.add(type);
-    }
-    if (allSame(types)) {
-      // Operands have the same nullability and type. Return them
-      // unchanged.
-      return operands;
-    }
-    final RelDataType type = typeFactory.leastRestrictive(types);
-    if (type == null) {
-      // There is no common type. Presumably this is a binary operator with
-      // asymmetric arguments (e.g. interval / integer) which is not intended
-      // to be harmonized.
-      return operands;
-    }
-    assert (nullCount > 0) == type.isNullable();
-    final List<RexNode> list = new ArrayList<>();
-    for (RexNode operand : operands) {
-      list.add(
-          translator.builder.ensureType(type, operand, false));
-    }
-    return list;
-  }
-
   private static RelDataType toSql(RelDataTypeFactory typeFactory,
       RelDataType type) {
     if (type instanceof RelDataTypeFactoryImpl.JavaType) {
@@ -1077,181 +840,6 @@ public class RexImpTable {
     return true;
   }
 
-  private static Expression implementNullSemantics0(
-      RexToLixTranslator translator,
-      RexCall call,
-      NullAs nullAs,
-      NullPolicy nullPolicy,
-      boolean harmonize,
-      NotNullImplementor implementor) {
-    switch (nullAs) {
-    case IS_NOT_NULL:
-      // If "f" is strict, then "f(a0, a1) IS NOT NULL" is
-      // equivalent to "a0 IS NOT NULL AND a1 IS NOT NULL".
-      switch (nullPolicy) {
-      case STRICT:
-        return Expressions.foldAnd(
-            translator.translateList(
-                call.getOperands(), nullAs));
-      }
-      break;
-    case IS_NULL:
-      // If "f" is strict, then "f(a0, a1) IS NULL" is
-      // equivalent to "a0 IS NULL OR a1 IS NULL".
-      switch (nullPolicy) {
-      case STRICT:
-        return Expressions.foldOr(
-            translator.translateList(
-                call.getOperands(), nullAs));
-      }
-      break;
-    }
-    final RexCall call2 = call2(harmonize, translator, call);
-    try {
-      return implementNullSemantics(
-          translator, call2, nullAs, nullPolicy, implementor);
-    } catch (RexToLixTranslator.AlwaysNull e) {
-      switch (nullAs) {
-      case NOT_POSSIBLE:
-        throw e;
-      case FALSE:
-      case IS_NOT_NULL:
-        return FALSE_EXPR;
-      case TRUE:
-      case IS_NULL:
-        return TRUE_EXPR;
-      default:
-        return NULL_EXPR;
-      }
-    }
-  }
-
-  private static Expression implementNullSemantics(
-      RexToLixTranslator translator,
-      RexCall call,
-      NullAs nullAs,
-      NullPolicy nullPolicy,
-      NotNullImplementor implementor) {
-    final List<Expression> list = new ArrayList<>();
-    final List<RexNode> conditionalOps =
-        nullPolicy == NullPolicy.ARG0
-            ? Collections.singletonList(call.getOperands().get(0))
-            : call.getOperands();
-    switch (nullAs) {
-    case NULL:
-    case IS_NULL:
-    case IS_NOT_NULL:
-      // v0 == null || v1 == null ? null : f(v0, v1)
-      for (Ord<RexNode> operand : Ord.zip(conditionalOps)) {
-        if (translator.isNullable(operand.e)) {
-          list.add(
-              translator.translate(
-                  operand.e, NullAs.IS_NULL));
-          translator = translator.setNullable(operand.e, false);
-        }
-      }
-      final Expression box =
-          Expressions.box(
-              implementCall(translator, call, implementor, nullAs));
-      final Expression ifTrue;
-      switch (nullAs) {
-      case NULL:
-        ifTrue = EnumUtils.convert(NULL_EXPR, box.getType());
-        break;
-      case IS_NULL:
-        ifTrue = TRUE_EXPR;
-        break;
-      case IS_NOT_NULL:
-        ifTrue = FALSE_EXPR;
-        break;
-      default:
-        throw new AssertionError();
-      }
-      return optimize(
-          Expressions.condition(
-              Expressions.foldOr(list),
-              ifTrue,
-              box));
-    case FALSE:
-      // v0 != null && v1 != null && f(v0, v1)
-      for (Ord<RexNode> operand : Ord.zip(conditionalOps)) {
-        if (translator.isNullable(operand.e)) {
-          list.add(
-              translator.translate(
-                  operand.e, NullAs.IS_NOT_NULL));
-          translator = translator.setNullable(operand.e, false);
-        }
-      }
-      list.add(implementCall(translator, call, implementor, nullAs));
-      return Expressions.foldAnd(list);
-    case TRUE:
-      // v0 == null || v1 == null || f(v0, v1)
-      for (Ord<RexNode> operand : Ord.zip(conditionalOps)) {
-        if (translator.isNullable(operand.e)) {
-          list.add(
-              translator.translate(
-                  operand.e, NullAs.IS_NULL));
-          translator = translator.setNullable(operand.e, false);
-        }
-      }
-      list.add(implementCall(translator, call, implementor, nullAs));
-      return Expressions.foldOr(list);
-    case NOT_POSSIBLE:
-      // Need to transmit to the implementor the fact that call cannot
-      // return null. In particular, it should return a primitive (e.g.
-      // int) rather than a box type (Integer).
-      // The cases with setNullable above might not help since the same
-      // RexNode can be referred via multiple ways: RexNode itself, RexLocalRef,
-      // and may be others.
-      final Map<RexNode, Boolean> nullable = new HashMap<>();
-      switch (nullPolicy) {
-      case STRICT:
-        // The arguments should be not nullable if STRICT operator is computed
-        // in nulls NOT_POSSIBLE mode
-        for (RexNode arg : call.getOperands()) {
-          if (translator.isNullable(arg) && !nullable.containsKey(arg)) {
-            nullable.put(arg, false);
-          }
-        }
-      }
-      nullable.put(call, false);
-      translator = translator.setNullable(nullable);
-      // fall through
-    default:
-      return implementCall(translator, call, implementor, nullAs);
-    }
-  }
-
-  private static Expression implementCall(
-      final RexToLixTranslator translator,
-      RexCall call,
-      NotNullImplementor implementor,
-      final NullAs nullAs) {
-    List<Expression> translatedOperands =
-        translator.translateList(call.getOperands());
-    // Make sure the operands marked not null in the translator have all been
-    // handled for nulls before being passed to the NotNullImplementor.
-    if (nullAs == NullAs.NOT_POSSIBLE) {
-      List<Expression> nullHandled = translatedOperands;
-      for (int i = 0; i < translatedOperands.size(); i++) {
-        RexNode arg = call.getOperands().get(i);
-        Expression e = translatedOperands.get(i);
-        if (!translator.isNullable(arg)) {
-          if (nullHandled == translatedOperands) {
-            nullHandled = new ArrayList<>(translatedOperands.subList(0, i));
-          }
-          nullHandled.add(translator.handleNull(e, nullAs));
-        } else if (nullHandled != translatedOperands) {
-          nullHandled.add(e);
-        }
-      }
-      translatedOperands = nullHandled;
-    }
-    Expression result =
-        implementor.implement(translator, call, translatedOperands);
-    return translator.handleNull(result, nullAs);
-  }
-
   /** Strategy what an operator should return if one of its
    * arguments is null. */
   public enum NullAs {
@@ -1306,8 +894,7 @@ public class RexImpTable {
       case BOX:
         switch (this) {
         case NOT_POSSIBLE:
-          return EnumUtils.convert(
-              x,
+          return EnumUtils.convert(x,
               Primitive.ofBox(x.getType()).primitiveClass);
         }
         // fall through
@@ -1317,13 +904,9 @@ public class RexImpTable {
       case NOT_POSSIBLE:
         return x;
       case FALSE:
-        return Expressions.call(
-            BuiltInMethod.IS_TRUE.method,
-            x);
+        return Expressions.call(BuiltInMethod.IS_TRUE.method, x);
       case TRUE:
-        return Expressions.call(
-            BuiltInMethod.IS_NOT_FALSE.method,
-            x);
+        return Expressions.call(BuiltInMethod.IS_NOT_FALSE.method, x);
       case IS_NULL:
         return Expressions.equal(x, NULL_EXPR);
       case IS_NOT_NULL:
@@ -2179,12 +1762,19 @@ public class RexImpTable {
   }
 
   /** Implementor for the {@code TRIM} function. */
-  private static class TrimImplementor implements NotNullImplementor {
-    public Expression implement(RexToLixTranslator translator, RexCall call,
-        List<Expression> translatedOperands) {
+  private static class TrimImplementor extends AbstractRexCallImplementor {
+    TrimImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "trim";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       final boolean strict = !translator.conformance.allowExtendedTrim();
-      final Object value =
-          ((ConstantExpression) translatedOperands.get(0)).value;
+      final Object value = translator.getLiteralValue(argValueList.get(0));
       SqlTrimFunction.Flag flag = (SqlTrimFunction.Flag) value;
       return Expressions.call(
           BuiltInMethod.TRIM.method,
@@ -2194,8 +1784,8 @@ public class RexImpTable {
           Expressions.constant(
               flag == SqlTrimFunction.Flag.BOTH
               || flag == SqlTrimFunction.Flag.TRAILING),
-          translatedOperands.get(1),
-          translatedOperands.get(2),
+          argValueList.get(1),
+          argValueList.get(2),
           Expressions.constant(strict));
     }
   }
@@ -2208,14 +1798,18 @@ public class RexImpTable {
 
     PeriodNameImplementor(String methodName, BuiltInMethod timestampMethod,
         BuiltInMethod dateMethod) {
-      super(methodName);
+      super(methodName, NullPolicy.STRICT, false);
       this.timestampMethod = timestampMethod;
       this.dateMethod = dateMethod;
     }
 
-    @Override public Expression implement(RexToLixTranslator translator,
-        RexCall call, List<Expression> translatedOperands) {
-      Expression operand = translatedOperands.get(0);
+    @Override String getVariableName() {
+      return "periodName";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      Expression operand = argValueList.get(0);
       final RelDataType type = call.operands.get(0).getType();
       switch (type.getSqlTypeName()) {
       case TIMESTAMP:
@@ -2243,13 +1837,17 @@ public class RexImpTable {
 
     FloorImplementor(String methodName, Method timestampMethod,
         Method dateMethod) {
-      super(methodName);
+      super(methodName, NullPolicy.STRICT, false);
       this.timestampMethod = timestampMethod;
       this.dateMethod = dateMethod;
     }
 
-    public Expression implement(RexToLixTranslator translator, RexCall call,
-        List<Expression> translatedOperands) {
+    @Override String getVariableName() {
+      return "floor";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       switch (call.getOperands().size()) {
       case 1:
         switch (call.getType().getSqlTypeName()) {
@@ -2257,16 +1855,16 @@ public class RexImpTable {
         case INTEGER:
         case SMALLINT:
         case TINYINT:
-          return translatedOperands.get(0);
+          return argValueList.get(0);
         default:
-          return super.implement(translator, call, translatedOperands);
+          return super.implementSafe(translator, call, argValueList);
         }
 
       case 2:
         final Type type;
         final Method floorMethod;
         final boolean preFloor;
-        Expression operand = translatedOperands.get(0);
+        Expression operand = argValueList.get(0);
         switch (call.getType().getSqlTypeName()) {
         case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
           operand = Expressions.call(
@@ -2284,9 +1882,8 @@ public class RexImpTable {
           floorMethod = dateMethod;
           preFloor = false;
         }
-        final ConstantExpression tur =
-            (ConstantExpression) translatedOperands.get(1);
-        final TimeUnitRange timeUnitRange = (TimeUnitRange) tur.value;
+        final TimeUnitRange timeUnitRange =
+            (TimeUnitRange) translator.getLiteralValue(argValueList.get(1));
         switch (timeUnitRange) {
         case YEAR:
         case QUARTER:
@@ -2295,7 +1892,8 @@ public class RexImpTable {
         case DAY:
           final Expression operand1 =
               preFloor ? call(operand, type, TimeUnit.DAY) : operand;
-          return Expressions.call(floorMethod, tur, operand1);
+          return Expressions.call(floorMethod,
+              translator.getLiteral(argValueList.get(1)), operand1);
         case NANOSECOND:
         default:
           return call(operand, type, timeUnitRange.startUnit);
@@ -2316,29 +1914,29 @@ public class RexImpTable {
   }
 
   /** Implementor for a function that generates calls to a given method. */
-  private static class MethodImplementor implements NotNullImplementor {
+  private static class MethodImplementor extends AbstractRexCallImplementor {
     protected final Method method;
 
-    MethodImplementor(Method method) {
+    MethodImplementor(Method method, NullPolicy nullPolicy, boolean harmonize) {
+      super(nullPolicy, harmonize);
       this.method = method;
     }
 
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> translatedOperands) {
+    @Override String getVariableName() {
+      return "method_call";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList) {
       final Expression expression;
       Class clazz = method.getDeclaringClass();
       if (Modifier.isStatic(method.getModifiers())) {
-        expression = EnumUtils.call(clazz, method.getName(), translatedOperands);
+        expression = EnumUtils.call(clazz, method.getName(), argValueList);
       } else {
         expression = EnumUtils.call(clazz, method.getName(),
-            Util.skip(translatedOperands, 1), translatedOperands.get(0));
+            Util.skip(argValueList, 1), argValueList.get(0));
       }
-
-      final Type returnType =
-          translator.typeFactory.getJavaClass(call.getType());
-      return EnumUtils.convert(expression, returnType);
+      return expression;
     }
   }
 
@@ -2352,18 +1950,16 @@ public class RexImpTable {
    */
   private static class JsonValueImplementor extends MethodImplementor {
     JsonValueImplementor(Method method) {
-      super(method);
+      super(method, NullPolicy.ARG0, false);
     }
 
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> translatedOperands) {
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList) {
       final Expression expression;
       final List<Expression> newOperands = new ArrayList<>();
-      newOperands.add(translatedOperands.get(0));
-      newOperands.add(translatedOperands.get(1));
-      List<Expression> leftExprs = Util.skip(translatedOperands, 2);
+      newOperands.add(argValueList.get(0));
+      newOperands.add(argValueList.get(1));
+      List<Expression> leftExprs = Util.skip(argValueList, 2);
       // Default value for JSON_VALUE behaviors.
       Expression emptyBehavior = Expressions.constant(SqlJsonValueEmptyOrErrorBehavior.NULL);
       Expression defaultValueOnEmpty = Expressions.constant(null);
@@ -2373,14 +1969,13 @@ public class RexImpTable {
       if (leftExprs.size() > 0) {
         for (int i = 0; i < leftExprs.size(); i++) {
           Expression expr = leftExprs.get(i);
-          if (expr instanceof ConstantExpression) {
-            final ConstantExpression constExpr = (ConstantExpression) expr;
-            final Object exprVal = constExpr.value;
+          final Object exprVal = translator.getLiteralValue(expr);
+          if (exprVal != null) {
             int defaultSymbolIdx = i - 2;
             if (exprVal == SqlJsonEmptyOrError.EMPTY) {
               if (defaultSymbolIdx >= 0
-                  && unwrapConstExprVal(leftExprs.get(defaultSymbolIdx))
-                    == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) {
+                  && translator.getLiteralValue(leftExprs.get(defaultSymbolIdx))
+                      == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) {
                 defaultValueOnEmpty = leftExprs.get(i - 1);
                 emptyBehavior = leftExprs.get(defaultSymbolIdx);
               } else {
@@ -2388,8 +1983,8 @@ public class RexImpTable {
               }
             } else if (exprVal == SqlJsonEmptyOrError.ERROR) {
               if (defaultSymbolIdx >= 0
-                  && unwrapConstExprVal(leftExprs.get(defaultSymbolIdx))
-                    == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) {
+                  && translator.getLiteralValue(leftExprs.get(defaultSymbolIdx))
+                      == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) {
                 defaultValueOnError = leftExprs.get(i - 1);
                 errorBehavior = leftExprs.get(defaultSymbolIdx);
               } else {
@@ -2412,37 +2007,34 @@ public class RexImpTable {
     }
   }
 
-  private static Object unwrapConstExprVal(Expression expression) {
-    if (expression instanceof ConstantExpression) {
-      return ((ConstantExpression) expression).value;
-    }
-    return null;
-  }
-
   /** Implementor for SQL functions that generates calls to a given method name.
    *
    * <p>Use this, as opposed to {@link MethodImplementor}, if the SQL function
    * is overloaded; then you can use one implementor for several overloads. */
-  private static class MethodNameImplementor implements NotNullImplementor {
+  private static class MethodNameImplementor extends AbstractRexCallImplementor {
     protected final String methodName;
 
-    MethodNameImplementor(String methodName) {
+    MethodNameImplementor(String methodName,
+        NullPolicy nullPolicy, boolean harmonize) {
+      super(nullPolicy, harmonize);
       this.methodName = methodName;
     }
 
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> translatedOperands) {
+    @Override String getVariableName() {
+      return "method_name_call";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList) {
       return EnumUtils.call(
           SqlFunctions.class,
           methodName,
-          translatedOperands);
+          argValueList);
     }
   }
 
   /** Implementor for binary operators. */
-  private static class BinaryImplementor implements NotNullImplementor {
+  private static class BinaryImplementor extends AbstractRexCallImplementor {
     /** Types that can be arguments to comparison operators such as
      * {@code <}. */
     private static final List<Primitive> COMP_OP_TYPES =
@@ -2472,17 +2064,21 @@ public class RexImpTable {
     private final ExpressionType expressionType;
     private final String backupMethodName;
 
-    BinaryImplementor(
-        ExpressionType expressionType,
-        String backupMethodName) {
+    BinaryImplementor(NullPolicy nullPolicy, boolean harmonize,
+        ExpressionType expressionType, String backupMethodName) {
+      super(nullPolicy, harmonize);
       this.expressionType = expressionType;
       this.backupMethodName = backupMethodName;
     }
 
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> expressions) {
+    @Override String getVariableName() {
+      return "binary_call";
+    }
+
+    @Override Expression implementSafe(
+        final RexToLixTranslator translator,
+        final RexCall call,
+        final List<Expression> argValueList) {
       // neither nullable:
       //   return x OP y
       // x nullable
@@ -2499,16 +2095,16 @@ public class RexImpTable {
         // If one or both operands have ANY type, use the late-binding backup
         // method.
         if (anyAnyOperands(call)) {
-          return callBackupMethodAnyType(translator, call, expressions);
+          return callBackupMethodAnyType(translator, call, argValueList);
         }
 
-        final Type type0 = expressions.get(0).getType();
-        final Type type1 = expressions.get(1).getType();
+        final Type type0 = argValueList.get(0).getType();
+        final Type type1 = argValueList.get(1).getType();
         final SqlBinaryOperator op = (SqlBinaryOperator) call.getOperator();
         final RelDataType relDataType0 = call.getOperands().get(0).getType();
         final Expression fieldComparator = generateCollatorExpression(relDataType0.getCollation());
         if (fieldComparator != null) {
-          expressions.add(fieldComparator);
+          argValueList.add(fieldComparator);
         }
         final Primitive primitive = Primitive.ofBoxOr(type0);
         if (primitive == null
@@ -2516,7 +2112,7 @@ public class RexImpTable {
             || COMPARISON_OPERATORS.contains(op)
             && !COMP_OP_TYPES.contains(primitive)) {
           return Expressions.call(SqlFunctions.class, backupMethodName,
-              expressions);
+              argValueList);
         }
         // When checking equals or not equals on two primitive boxing classes
         // (i.e. Long x, Long y), we should fall back to call `SqlFunctions.eq(x, y)`
@@ -2526,16 +2122,11 @@ public class RexImpTable {
         if (EQUALS_OPERATORS.contains(op)
             && boxPrimitive0 != null && boxPrimitive1 != null) {
           return Expressions.call(SqlFunctions.class, backupMethodName,
-              expressions);
+              argValueList);
         }
       }
-
-      final Type returnType =
-          translator.typeFactory.getJavaClass(call.getType());
-      return EnumUtils.convert(
-          Expressions.makeBinary(expressionType, expressions.get(0),
-              expressions.get(1)),
-          returnType);
+      return Expressions.makeBinary(expressionType,
+          argValueList.get(0), argValueList.get(1));
     }
 
     /** Returns whether any of a call's operands have ANY type. */
@@ -2570,36 +2161,47 @@ public class RexImpTable {
   }
 
   /** Implementor for unary operators. */
-  private static class UnaryImplementor implements NotNullImplementor {
+  private static class UnaryImplementor extends AbstractRexCallImplementor {
     private final ExpressionType expressionType;
 
-    UnaryImplementor(ExpressionType expressionType) {
+    UnaryImplementor(ExpressionType expressionType, NullPolicy nullPolicy) {
+      super(nullPolicy, false);
       this.expressionType = expressionType;
     }
 
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> translatedOperands) {
-      final Expression operand = translatedOperands.get(0);
-      final UnaryExpression e = Expressions.makeUnary(expressionType, operand);
-      if (e.type.equals(operand.type)) {
+    @Override String getVariableName() {
+      return "unary_call";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList) {
+      final Expression argValue = argValueList.get(0);
+      final UnaryExpression e = Expressions.makeUnary(expressionType, argValue);
+      if (e.type.equals(argValue.type)) {
         return e;
       }
       // Certain unary operators do not preserve type. For example, the "-"
       // operator applied to a "byte" expression returns an "int".
-      return Expressions.convert_(e, operand.type);
+      return Expressions.convert_(e, argValue.type);
     }
   }
 
   /** Implementor for the {@code EXTRACT(unit FROM datetime)} function. */
-  private static class ExtractImplementor implements NotNullImplementor {
-    public Expression implement(RexToLixTranslator translator, RexCall call,
-        List<Expression> translatedOperands) {
+  private static class ExtractImplementor extends AbstractRexCallImplementor {
+    ExtractImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "extract";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       final TimeUnitRange timeUnitRange =
-          (TimeUnitRange) ((ConstantExpression) translatedOperands.get(0)).value;
+          (TimeUnitRange) translator.getLiteralValue(argValueList.get(0));
       final TimeUnit unit = timeUnitRange.startUnit;
-      Expression operand = translatedOperands.get(1);
+      Expression operand = argValueList.get(1);
       final SqlTypeName sqlTypeName =
           call.operands.get(1).getType().getSqlTypeName();
       switch (unit) {
@@ -2642,7 +2244,7 @@ public class RexImpTable {
           // fall through
         case DATE:
           return Expressions.call(BuiltInMethod.UNIX_DATE_EXTRACT.method,
-              translatedOperands.get(0), operand);
+              argValueList.get(0), operand);
         default:
           throw new AssertionError("unexpected " + sqlTypeName);
         }
@@ -2712,7 +2314,6 @@ public class RexImpTable {
       }
       return operand;
     }
-
   }
 
   private static Expression mod(Expression operand, long factor) {
@@ -2750,175 +2351,174 @@ public class RexImpTable {
     }
   }
 
-  /** Implementor for the SQL {@code CASE} operator. */
-  private static class CaseImplementor implements CallImplementor {
-    public Expression implement(RexToLixTranslator translator, RexCall call,
-        NullAs nullAs) {
-      return implementRecurse(translator, call, nullAs, 0);
+  /** Implementor for the SQL {@code COALESCE} operator. */
+  private static class CoalesceImplementor extends AbstractRexCallImplementor {
+    CoalesceImplementor() {
+      super(NullPolicy.NONE, false);
     }
 
-    private Expression implementRecurse(RexToLixTranslator translator,
-        RexCall call, NullAs nullAs, int i) {
-      List<RexNode> operands = call.getOperands();
-      if (i == operands.size() - 1) {
-        // the "else" clause
-        return translator.translate(
-            translator.builder.ensureType(
-                call.getType(), operands.get(i), false), nullAs);
-      } else {
-        Expression ifTrue;
-        try {
-          ifTrue = translator.translate(
-              translator.builder.ensureType(call.getType(),
-                  operands.get(i + 1),
-                  false), nullAs);
-        } catch (RexToLixTranslator.AlwaysNull e) {
-          ifTrue = null;
-        }
-
-        Expression ifFalse;
-        try {
-          ifFalse = implementRecurse(translator, call, nullAs, i + 2);
-        } catch (RexToLixTranslator.AlwaysNull e) {
-          if (ifTrue == null) {
-            throw RexToLixTranslator.AlwaysNull.INSTANCE;
-          }
-          ifFalse = null;
-        }
-
-        Expression test = translator.translate(operands.get(i), NullAs.FALSE);
-
-        return ifTrue == null || ifFalse == null
-            ? Util.first(ifTrue, ifFalse)
-            : Expressions.condition(test, ifTrue, ifFalse);
-      }
+    @Override String getVariableName() {
+      return "coalesce";
     }
-  }
 
-  /** Implementor for the SQL {@code COALESCE} operator. */
-  private static class CoalesceImplementor implements CallImplementor {
-    public Expression implement(RexToLixTranslator translator, RexCall call,
-        NullAs nullAs) {
-      return implementRecurse(translator, call.operands, nullAs);
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return implementRecurse(translator, argValueList);
     }
 
     private Expression implementRecurse(RexToLixTranslator translator,
-        List<RexNode> operands, NullAs nullAs) {
-      if (operands.size() == 1) {
-        return translator.translate(operands.get(0), nullAs);
+        final List<Expression> argValueList) {
+      if (argValueList.size() == 1) {
+        return argValueList.get(0);
       } else {
         return Expressions.condition(
-            translator.translate(operands.get(0), NullAs.IS_NOT_NULL),
-            translator.translate(operands.get(0), nullAs),
-            implementRecurse(translator, Util.skip(operands), nullAs));
+            translator.checkNotNull(argValueList.get(0)),
+            argValueList.get(0),
+            implementRecurse(translator, Util.skip(argValueList)));
       }
     }
   }
 
-  /** Implementor for the SQL {@code CAST} function that optimizes if, say, the
-   * argument is already of the desired type. */
-  private static class CastOptimizedImplementor implements CallImplementor {
-    private final CallImplementor accurate;
+  /** Implementor for the SQL {@code CAST} operator. */
+  private static class CastImplementor extends AbstractRexCallImplementor {
+    CastImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
 
-    private CastOptimizedImplementor() {
-      accurate = createImplementor(new CastImplementor(),
-          NullPolicy.STRICT, false);
+    @Override String getVariableName() {
+      return "cast";
     }
 
-    public Expression implement(RexToLixTranslator translator, RexCall call,
-        NullAs nullAs) {
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      assert call.getOperands().size() == 1;
+      final RelDataType sourceType = call.getOperands().get(0).getType();
+
       // Short-circuit if no cast is required
       RexNode arg = call.getOperands().get(0);
-      if (call.getType().equals(arg.getType())) {
+      if (call.getType().equals(sourceType)) {
         // No cast required, omit cast
-        return translator.translate(arg, nullAs);
+        return argValueList.get(0);
       }
       if (SqlTypeUtil.equalSansNullability(translator.typeFactory,
           call.getType(), arg.getType())
-          && nullAs == NullAs.NULL
           && translator.deref(arg) instanceof RexLiteral) {
         return RexToLixTranslator.translateLiteral(
             (RexLiteral) translator.deref(arg), call.getType(),
-            translator.typeFactory, nullAs);
+            translator.typeFactory, NullAs.NULL);
       }
-      return accurate.implement(translator, call, nullAs);
-    }
-  }
-
-  /** Implementor for the SQL {@code CAST} operator. */
-  private static class CastImplementor implements NotNullImplementor {
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> translatedOperands) {
-      assert call.getOperands().size() == 1;
-      final RelDataType sourceType = call.getOperands().get(0).getType();
-      // It's only possible for the result to be null if both expression
-      // and target type are nullable. We assume that the caller did not
-      // make a mistake. If expression looks nullable, caller WILL have
-      // checked that expression is not null before calling us.
-      final boolean nullable =
-          translator.isNullable(call)
-              && sourceType.isNullable()
-              && !Primitive.is(translatedOperands.get(0).getType());
       final RelDataType targetType =
-          translator.nullifyType(call.getType(), nullable);
+          nullifyType(translator.typeFactory, call.getType(), false);
       return translator.translateCast(sourceType,
-          targetType,
-          translatedOperands.get(0));
+              targetType, argValueList.get(0));
+    }
+
+    private RelDataType nullifyType(JavaTypeFactory typeFactory,
+        final RelDataType type, final boolean nullable) {
+      if (type instanceof RelDataTypeFactoryImpl.JavaType) {
+        final Primitive primitive = Primitive.ofBox(
+            ((RelDataTypeFactoryImpl.JavaType) type).getJavaClass());
+        if (primitive != null) {
+          return typeFactory.createJavaType(primitive.primitiveClass);
+        }
+      }
+      return typeFactory.createTypeWithNullability(type, nullable);
     }
   }
 
   /** Implementor for the {@code REINTERPRET} internal SQL operator. */
-  private static class ReinterpretImplementor implements NotNullImplementor {
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> translatedOperands) {
+  private static class ReinterpretImplementor extends AbstractRexCallImplementor {
+    ReinterpretImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "reInterpret";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       assert call.getOperands().size() == 1;
-      return translatedOperands.get(0);
+      return argValueList.get(0);
     }
   }
 
   /** Implementor for a value-constructor. */
   private static class ValueConstructorImplementor
-      implements CallImplementor {
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        NullAs nullAs) {
-      return translator.translateConstructor(call.getOperands(),
-          call.getOperator().getKind());
+      extends AbstractRexCallImplementor {
+
+    ValueConstructorImplementor() {
+      super(NullPolicy.NONE, false);
+    }
+
+    @Override String getVariableName() {
+      return "value_constructor";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      SqlKind kind = call.getOperator().getKind();
+      final BlockBuilder blockBuilder = translator.getBlockBuilder();
+      switch (kind) {
+      case MAP_VALUE_CONSTRUCTOR:
+        Expression map =
+            blockBuilder.append("map", Expressions.new_(LinkedHashMap.class),
+                false);
+        for (int i = 0; i < argValueList.size(); i++) {
+          Expression key = argValueList.get(i++);
+          Expression value = argValueList.get(i);
+          blockBuilder.add(
+              Expressions.statement(
+                  Expressions.call(map, BuiltInMethod.MAP_PUT.method,
+                      Expressions.box(key), Expressions.box(value))));
+        }
+        return map;
+      case ARRAY_VALUE_CONSTRUCTOR:
+        Expression lyst =
+            blockBuilder.append("list", Expressions.new_(ArrayList.class),
+                false);
+        for (Expression value : argValueList) {
+          blockBuilder.add(
+              Expressions.statement(
+                  Expressions.call(lyst, BuiltInMethod.COLLECTION_ADD.method,
+                      Expressions.box(value))));
+        }
+        return lyst;
+      default:
+        throw new AssertionError("unexpected: " + kind);
+      }
     }
   }
 
   /** Implementor for the {@code ITEM} SQL operator. */
-  private static class ItemImplementor
-      implements CallImplementor {
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        NullAs nullAs) {
+  private static class ItemImplementor extends AbstractRexCallImplementor {
+    ItemImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "item";
+    }
+
+    // Since we follow PostgreSQL's semantics that an out-of-bound reference
+    // returns NULL, x[y] can return null even if x and y are both NOT NULL.
+    // (In SQL standard semantics, an out-of-bound reference to an array
+    // throws an exception.)
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       final MethodImplementor implementor =
-          getImplementor(
-              call.getOperands().get(0).getType().getSqlTypeName());
-      // Since we follow PostgreSQL's semantics that an out-of-bound reference
-      // returns NULL, x[y] can return null even if x and y are both NOT NULL.
-      // (In SQL standard semantics, an out-of-bound reference to an array
-      // throws an exception.)
-      final NullPolicy nullPolicy = NullPolicy.ANY;
-      return implementNullSemantics0(translator, call, nullAs, nullPolicy,
-          false, implementor);
+          getImplementor(call.getOperands().get(0).getType().getSqlTypeName());
+      return implementor.implementSafe(translator, call, argValueList);
     }
 
     private MethodImplementor getImplementor(SqlTypeName sqlTypeName) {
       switch (sqlTypeName) {
       case ARRAY:
-        return new MethodImplementor(BuiltInMethod.ARRAY_ITEM.method);
+        return new MethodImplementor(BuiltInMethod.ARRAY_ITEM.method, nullPolicy, false);
       case MAP:
-        return new MethodImplementor(BuiltInMethod.MAP_ITEM.method);
+        return new MethodImplementor(BuiltInMethod.MAP_ITEM.method, nullPolicy, false);
       default:
-        return new MethodImplementor(BuiltInMethod.ANY_ITEM.method);
+        return new MethodImplementor(BuiltInMethod.ANY_ITEM.method, nullPolicy, false);
       }
     }
   }
@@ -2928,17 +2528,17 @@ public class RexImpTable {
    * <p>Several of these are represented internally as constant values, set
    * per execution. */
   private static class SystemFunctionImplementor
-      implements CallImplementor {
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        NullAs nullAs) {
-      switch (nullAs) {
-      case IS_NULL:
-        return Expressions.constant(false);
-      case IS_NOT_NULL:
-        return Expressions.constant(true);
-      }
+      extends AbstractRexCallImplementor {
+    SystemFunctionImplementor() {
+      super(NullPolicy.NONE, false);
+    }
+
+    @Override String getVariableName() {
+      return "system_func";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       final SqlOperator op = call.getOperator();
       final Expression root = translator.getRoot();
       if (op == CURRENT_USER
@@ -2969,77 +2569,48 @@ public class RexImpTable {
     }
   }
 
-  /** Implements "IS XXX" operations such as "IS NULL"
-   * or "IS NOT TRUE".
-   *
-   * <p>What these operators have in common:</p>
-   * 1. They return TRUE or FALSE, never NULL.
-   * 2. Of the 3 input values (TRUE, FALSE, NULL) they return TRUE for 1 or 2,
-   *    FALSE for the other 2 or 1.
-   */
-  private static class IsXxxImplementor
-      implements CallImplementor {
-    private final Boolean seek;
-    private final boolean negate;
-
-    IsXxxImplementor(Boolean seek, boolean negate) {
-      this.seek = seek;
-      this.negate = negate;
-    }
-
-    public Expression implement(
-        RexToLixTranslator translator, RexCall call, NullAs nullAs) {
-      List<RexNode> operands = call.getOperands();
-      assert operands.size() == 1;
-      switch (nullAs) {
-      case IS_NOT_NULL:
-        return BOXED_TRUE_EXPR;
-      case IS_NULL:
-        return BOXED_FALSE_EXPR;
-      }
-      if (seek == null) {
-        return translator.translate(operands.get(0),
-            negate ? NullAs.IS_NOT_NULL : NullAs.IS_NULL);
-      } else {
-        return maybeNegate(negate == seek,
-            translator.translate(operands.get(0),
-                seek ? NullAs.FALSE : NullAs.TRUE));
-      }
-    }
-  }
-
   /** Implementor for the {@code NOT} operator. */
-  private static class NotImplementor implements NotNullImplementor {
-    private final NotNullImplementor implementor;
+  private static class NotImplementor extends AbstractRexCallImplementor {
+    private AbstractRexCallImplementor implementor;
 
-    NotImplementor(NotNullImplementor implementor) {
+    private NotImplementor(AbstractRexCallImplementor implementor) {
+      super(null, false);
       this.implementor = implementor;
     }
 
-    private static NotNullImplementor of(NotNullImplementor implementor) {
+    static AbstractRexCallImplementor of(AbstractRexCallImplementor implementor) {
       return new NotImplementor(implementor);
     }
 
-    public Expression implement(
-        RexToLixTranslator translator,
-        RexCall call,
-        List<Expression> translatedOperands) {
+    @Override String getVariableName() {
+      return "not";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       final Expression expression =
-          implementor.implement(translator, call, translatedOperands);
+          implementor.implementSafe(translator, call, argValueList);
       return Expressions.not(expression);
     }
   }
 
   /** Implementor for various datetime arithmetic. */
   private static class DatetimeArithmeticImplementor
-      implements NotNullImplementor {
-    public Expression implement(RexToLixTranslator translator, RexCall call,
-        List<Expression> translatedOperands) {
+      extends AbstractRexCallImplementor {
+    DatetimeArithmeticImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+    @Override String getVariableName() {
+      return "dateTime_arithmetic";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
       final RexNode operand0 = call.getOperands().get(0);
-      Expression trop0 = translatedOperands.get(0);
+      Expression trop0 = argValueList.get(0);
       final SqlTypeName typeName1 =
           call.getOperands().get(1).getType().getSqlTypeName();
-      Expression trop1 = translatedOperands.get(1);
+      Expression trop1 = argValueList.get(1);
       final SqlTypeName typeName = call.getType().getSqlTypeName();
       switch (operand0.getType().getSqlTypeName()) {
       case DATE:
@@ -3201,20 +2772,602 @@ public class RexImpTable {
     }
   }
 
-  /** Implements PREV match-recognize function. */
-  private static class PrevImplementor implements CallImplementor {
-    @Override public Expression implement(RexToLixTranslator translator, RexCall call,
-        NullAs nullAs) {
-      final RexNode node = call.getOperands().get(0);
-      final RexNode offset = call.getOperands().get(1);
-      final Expression offs = Expressions.multiply(translator.translate(offset),
-          Expressions.constant(-1));
-      ((EnumerableMatch.PrevInputGetter) translator.inputGetter).setOffset(offs);
-      return translator.translate(node, nullAs);
+  /** Null-safe implementor of {@code RexCall}s. */
+  public interface RexCallImplementor {
+    RexToLixTranslator.Result implement(
+        RexToLixTranslator translator,
+        RexCall call,
+        List<RexToLixTranslator.Result> arguments);
+  }
+
+  /**
+   * Abstract implementation of the {@link RexCallImplementor} interface.
+   *
+   * <p>It is not always safe to execute the {@link RexCall} directly due to
+   * the special null arguments. Therefore, the generated code logic is
+   * conditional correspondingly.
+   *
+   * <p>For example, {@code a + b} will generate two declaration statements:
+   *
+   * <blockquote>
+   * <code>
+   * final Integer xxx_value = (a_isNull || b_isNull) ? null : plus(a, b);<br>
+   * final boolean xxx_isNull = xxx_value == null;
+   * </code>
+   * </blockquote>
+   */
+  private abstract static class AbstractRexCallImplementor
+      implements RexCallImplementor {
+    final NullPolicy nullPolicy;
+    private final boolean harmonize;
+
+    AbstractRexCallImplementor(NullPolicy nullPolicy, boolean harmonize) {
+      this.nullPolicy = nullPolicy;
+      this.harmonize = harmonize;
+    }
+
+    @Override public RexToLixTranslator.Result implement(
+        final RexToLixTranslator translator,
+        final RexCall call,
+        final List<RexToLixTranslator.Result> arguments) {
+      final List<Expression> argIsNullList = new ArrayList<>();
+      final List<Expression> argValueList = new ArrayList<>();
+      for (RexToLixTranslator.Result result: arguments) {
+        argIsNullList.add(result.isNullVariable);
+        argValueList.add(result.valueVariable);
+      }
+      final Expression condition = getCondition(argIsNullList);
+      final ParameterExpression valueVariable =
+          genValueStatement(translator, call, argValueList, condition);
+      final ParameterExpression isNullVariable =
+          genIsNullStatement(translator, valueVariable);
+      return new RexToLixTranslator.Result(isNullVariable, valueVariable);
+    }
+
+    // Variable name facilitates reasoning about issues when necessary
+    abstract String getVariableName();
+
+    /** Figures out conditional expression according to NullPolicy. */
+    Expression getCondition(final List<Expression> argIsNullList) {
+      if (argIsNullList.size() == 0
+          || nullPolicy == null
+          || nullPolicy == NullPolicy.NONE) {
+        return FALSE_EXPR;
+      }
+      if (nullPolicy == NullPolicy.ARG0) {
+        return argIsNullList.get(0);
+      }
+      return Expressions.foldOr(argIsNullList);
+    }
+
+    // E.g., "final Integer xxx_value = (a_isNull || b_isNull) ? null : plus(a, b)"
+    private ParameterExpression genValueStatement(
+        final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList,
+        final Expression condition) {
+      List<Expression> optimizedArgValueList = argValueList;
+      if (harmonize) {
+        optimizedArgValueList =
+            harmonize(optimizedArgValueList, translator, call);
+      }
+      optimizedArgValueList = unboxIfNecessary(optimizedArgValueList);
+
+      final Expression callValue =
+          implementSafe(translator, call, optimizedArgValueList);
+
+      // In general, RexCall's type is correct for code generation
+      // and thus we should ensure the consistency.
+      // However, for some special cases (e.g., TableFunction),
+      // the implementation's type is correct, we can't convert it.
+      final SqlOperator op = call.getOperator();
+      final Type returnType = translator.typeFactory.getJavaClass(call.getType());
+      final boolean noConvert = (returnType == null)
+              || (returnType == callValue.getType())
+              || (op instanceof SqlUserDefinedTableMacro)
+              || (op instanceof SqlUserDefinedTableFunction);
+      final Expression convertedCallValue =
+              noConvert
+              ? callValue
+              : EnumUtils.convert(callValue, returnType);
+
+      final Expression valueExpression =
+          Expressions.condition(condition,
+              getIfTrue(convertedCallValue.getType(), argValueList),
+              convertedCallValue);
+      final ParameterExpression value =
+          Expressions.parameter(convertedCallValue.getType(),
+              translator.getBlockBuilder().newName(getVariableName() + "_value"));
+      translator.getBlockBuilder().add(
+          Expressions.declare(Modifier.FINAL, value, valueExpression));
+      return value;
+    }
+
+    Expression getIfTrue(Type type, final List<Expression> argValueList) {
+      return getDefaultValue(type);
+    }
+
+    // E.g., "final boolean xxx_isNull = xxx_value == null"
+    private ParameterExpression genIsNullStatement(
+        final RexToLixTranslator translator, final ParameterExpression value) {
+      final ParameterExpression isNullVariable =
+          Expressions.parameter(Boolean.TYPE,
+              translator.getBlockBuilder().newName(getVariableName() + "_isNull"));
+      final Expression isNullExpression = translator.checkNull(value);
+      translator.getBlockBuilder().add(
+          Expressions.declare(Modifier.FINAL, isNullVariable, isNullExpression));
+      return isNullVariable;
+    }
+
+    /** Ensures that operands have identical type. */
+    private List<Expression> harmonize(final List<Expression> argValueList,
+        final RexToLixTranslator translator, final RexCall call) {
+      int nullCount = 0;
+      final List<RelDataType> types = new ArrayList<>();
+      final RelDataTypeFactory typeFactory =
+          translator.builder.getTypeFactory();
+      for (RexNode operand : call.getOperands()) {
+        RelDataType type = operand.getType();
+        type = toSql(typeFactory, type);
+        if (translator.isNullable(operand)) {
+          ++nullCount;
+        } else {
+          type = typeFactory.createTypeWithNullability(type, false);
+        }
+        types.add(type);
+      }
+      if (allSame(types)) {
+        // Operands have the same nullability and type. Return them
+        // unchanged.
+        return argValueList;
+      }
+      final RelDataType type = typeFactory.leastRestrictive(types);
+      if (type == null) {
+        // There is no common type. Presumably this is a binary operator with
+        // asymmetric arguments (e.g. interval / integer) which is not intended
+        // to be harmonized.
+        return argValueList;
+      }
+      assert (nullCount > 0) == type.isNullable();
+      final Type javaClass =
+          translator.typeFactory.getJavaClass(type);
+      final List<Expression> harmonizedArgValues = new ArrayList<>();
+      for (Expression argValue : argValueList) {
+        harmonizedArgValues.add(
+            EnumUtils.convert(argValue, javaClass));
+      }
+      return harmonizedArgValues;
+    }
+
+    /** Under null check, it is safe to unbox the operands before entering the
+     * implementor. */
+    private List<Expression> unboxIfNecessary(final List<Expression> argValueList) {
+      List<Expression> unboxValueList = argValueList;
+      if (nullPolicy == NullPolicy.STRICT || nullPolicy == NullPolicy.ANY
+          || nullPolicy == NullPolicy.SEMI_STRICT) {
+        unboxValueList = argValueList.stream()
+            .map(this::unboxExpression)
+            .collect(Collectors.toList());
+      }
+      if (nullPolicy == NullPolicy.ARG0 && argValueList.size() > 0) {
+        final Expression unboxArg0 = unboxExpression(unboxValueList.get(0));
+        unboxValueList.set(0, unboxArg0);
+      }
+      return unboxValueList;
+    }
+
+    private Expression unboxExpression(final Expression argValue) {
+      Primitive fromBox = Primitive.ofBox(argValue.getType());
+      if (fromBox == null || fromBox == Primitive.VOID) {
+        return argValue;
+      }
+      // Optimization: for "long x";
+      // "Long.valueOf(x)" generates "x"
+      if (argValue instanceof MethodCallExpression) {
+        MethodCallExpression mce = (MethodCallExpression) argValue;
+        if (mce.method.getName().equals("valueOf") && mce.expressions.size() == 1) {
+          Expression originArg = mce.expressions.get(0);
+          if (Primitive.of(originArg.type) == fromBox) {
+            return originArg;
+          }
+        }
+      }
+      return NullAs.NOT_POSSIBLE.handle(argValue);
+    }
+
+    abstract Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList);
+  }
+
+  /**
+   * Implementor for the {@code AND} operator.
+   *
+   * <p>If any of the arguments are false, result is false;
+   * else if any arguments are null, result is null;
+   * else true.
+   */
+  private static class LogicalAndImplementor extends AbstractRexCallImplementor {
+    LogicalAndImplementor() {
+      super(NullPolicy.NONE, true);
+    }
+
+    @Override String getVariableName() {
+      return "logical_and";
+    }
+
+    @Override public RexToLixTranslator.Result implement(final RexToLixTranslator translator,
+        final RexCall call, final List<RexToLixTranslator.Result> arguments) {
+      final List<Expression> argIsNullList = new ArrayList<>();
+      for (RexToLixTranslator.Result result: arguments) {
+        argIsNullList.add(result.isNullVariable);
+      }
+      final List<Expression> nullAsTrue =
+          arguments.stream()
+              .map(result ->
+                  Expressions.condition(result.isNullVariable, TRUE_EXPR,
+                      result.valueVariable))
+              .collect(Collectors.toList());
+      final Expression hasFalse =
+          Expressions.not(Expressions.foldAnd(nullAsTrue));
+      final Expression hasNull = Expressions.foldOr(argIsNullList);
+      final Expression callExpression =
+          Expressions.condition(hasFalse, BOXED_FALSE_EXPR,
+              Expressions.condition(hasNull, NULL_EXPR, BOXED_TRUE_EXPR));
+      final RexImpTable.NullAs nullAs = translator.isNullable(call)
+          ? RexImpTable.NullAs.NULL : RexImpTable.NullAs.NOT_POSSIBLE;
+      final Expression valueExpression = nullAs.handle(callExpression);
+      final ParameterExpression valueVariable =
+          Expressions.parameter(valueExpression.getType(),
+              translator.getBlockBuilder().newName(getVariableName() + "_value"));
+      final Expression isNullExpression = translator.checkNull(valueVariable);
+      final ParameterExpression isNullVariable =
+          Expressions.parameter(Boolean.TYPE,
+              translator.getBlockBuilder().newName(getVariableName() + "_isNull"));
+      translator.getBlockBuilder().add(
+          Expressions.declare(Modifier.FINAL, valueVariable, valueExpression));
+      translator.getBlockBuilder().add(
+          Expressions.declare(Modifier.FINAL, isNullVariable, isNullExpression));
+      return new RexToLixTranslator.Result(isNullVariable, valueVariable);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return null;
+    }
+  }
+
+  /**
+   * Implementor for the {@code OR} operator.
+   *
+   * <p>If any of the arguments are true, result is true;
+   * else if any arguments are null, result is null;
+   * else false.
+   */
+  private static class LogicalOrImplementor extends AbstractRexCallImplementor {
+    LogicalOrImplementor() {
+      super(NullPolicy.NONE, true);
+    }
+
+    @Override String getVariableName() {
+      return "logical_or";
+    }
+
+    @Override public RexToLixTranslator.Result implement(final RexToLixTranslator translator,
+        final RexCall call, final List<RexToLixTranslator.Result> arguments) {
+      final List<Expression> argIsNullList = new ArrayList<>();
+      for (RexToLixTranslator.Result result: arguments) {
+        argIsNullList.add(result.isNullVariable);
+      }
+      final List<Expression> nullAsFalse =
+          arguments.stream()
+              .map(result ->
+                  Expressions.condition(result.isNullVariable, FALSE_EXPR,
+                      result.valueVariable))
+              .collect(Collectors.toList());
+      final Expression hasTrue = Expressions.foldOr(nullAsFalse);
+      final Expression hasNull = Expressions.foldOr(argIsNullList);
+      final Expression callExpression =
+          Expressions.condition(hasTrue, BOXED_TRUE_EXPR,
+              Expressions.condition(hasNull, NULL_EXPR, BOXED_FALSE_EXPR));
+      final RexImpTable.NullAs nullAs = translator.isNullable(call)
+          ? RexImpTable.NullAs.NULL : RexImpTable.NullAs.NOT_POSSIBLE;
+      final Expression valueExpression = nullAs.handle(callExpression);
+      final ParameterExpression valueVariable =
+          Expressions.parameter(valueExpression.getType(),
+              translator.getBlockBuilder().newName(getVariableName() + "_value"));
+      final Expression isNullExpression = translator.checkNull(valueExpression);
+      final ParameterExpression isNullVariable =
+          Expressions.parameter(Boolean.TYPE,
+              translator.getBlockBuilder().newName(getVariableName() + "_isNull"));
+      translator.getBlockBuilder().add(
+          Expressions.declare(Modifier.FINAL, valueVariable, valueExpression));
+      translator.getBlockBuilder().add(
+          Expressions.declare(Modifier.FINAL, isNullVariable, isNullExpression));
+      return new RexToLixTranslator.Result(isNullVariable, valueVariable);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return null;
+    }
+  }
+
+  /**
+   * Implementor for the {@code NOT} operator.
+   *
+   * <p>If any of the arguments are false, result is true;
+   * else if any arguments are null, result is null;
+   * else false.
+   */
+  private static class LogicalNotImplementor extends AbstractRexCallImplementor {
+    LogicalNotImplementor() {
+      super(NullPolicy.NONE, true);
+    }
+
+    @Override String getVariableName() {
+      return "logical_not";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.call(BuiltInMethod.NOT.method, argValueList);
+    }
+  }
+
+  /**
+   * Implementation that calls a given {@link java.lang.reflect.Method}.
+   *
+   * <p>When method is not static, a new instance of the required class is
+   * created.
+   */
+  private static class ReflectiveImplementor extends AbstractRexCallImplementor {
+    protected final Method method;
+
+    ReflectiveImplementor(Method method, NullPolicy nullPolicy) {
+      super(nullPolicy, false);
+      this.method = method;
+    }
+
+    @Override String getVariableName() {
+      return "reflective_" + method.getName();
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList) {
+      List<Expression> argValueList0 =
+          EnumUtils.fromInternal(method.getParameterTypes(), argValueList);
+      if ((method.getModifiers() & Modifier.STATIC) != 0) {
+        return Expressions.call(method, argValueList0);
+      } else {
+        // The UDF class must have a public zero-args constructor.
+        // Assume that the validator checked already.
+        final Expression target = Expressions.new_(method.getDeclaringClass());
+        return Expressions.call(target, method, argValueList0);
+      }
+    }
+  }
+
+  /** Implementor for the {@code RAND} function. */
+  private static class RandImplementor extends AbstractRexCallImplementor {
+    private final AbstractRexCallImplementor[] implementors = {
+        new ReflectiveImplementor(BuiltInMethod.RAND.method, nullPolicy),
+        new ReflectiveImplementor(BuiltInMethod.RAND_SEED.method, nullPolicy)
+    };
+
+    RandImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "rand";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return implementors[call.getOperands().size()]
+          .implementSafe(translator, call, argValueList);
+    }
+  }
+
+  /** Implementor for the {@code RAND_INTEGER} function. */
+  private static class RandIntegerImplementor extends AbstractRexCallImplementor {
+    private final AbstractRexCallImplementor[] implementors = {
+        null,
+        new ReflectiveImplementor(BuiltInMethod.RAND_INTEGER.method, nullPolicy),
+        new ReflectiveImplementor(BuiltInMethod.RAND_INTEGER_SEED.method, nullPolicy)
+    };
+
+    RandIntegerImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "rand_integer";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return implementors[call.getOperands().size()]
+          .implementSafe(translator, call, argValueList);
+    }
+  }
+
+  /** Implementor for the {@code PI} operator. */
+  private static class PiImplementor extends AbstractRexCallImplementor {
+    PiImplementor() {
+      super(NullPolicy.NONE, false);
+    }
+
+    @Override String getVariableName() {
+      return "pi";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.constant(Math.PI);
+    }
+  }
+
+  /** Implementor for the {@code IS FALSE} SQL operator. */
+  private static class IsFalseImplementor extends AbstractRexCallImplementor {
+    IsFalseImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "is_false";
+    }
+
+    @Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
+      return Expressions.constant(false, type);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.equal(argValueList.get(0), FALSE_EXPR);
+    }
+  }
+
+  /** Implementor for the {@code IS NOT FALSE} SQL operator. */
+  private static class IsNotFalseImplementor extends AbstractRexCallImplementor {
+    IsNotFalseImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "is_not_false";
+    }
+
+    @Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
+      return Expressions.constant(true, type);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.notEqual(argValueList.get(0), FALSE_EXPR);
+    }
+  }
+
+  /** Implementor for the {@code IS NOT NULL} SQL operator. */
+  private static class IsNotNullImplementor extends AbstractRexCallImplementor {
+    IsNotNullImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "is_not_null";
+    }
+
+    @Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
+      return Expressions.constant(false, type);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.notEqual(argValueList.get(0), NULL_EXPR);
+    }
+  }
+
+  /** Implementor for the {@code IS NOT TRUE} SQL operator. */
+  private static class IsNotTrueImplementor extends AbstractRexCallImplementor {
+    IsNotTrueImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "is_not_true";
+    }
+
+    @Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
+      return Expressions.constant(true, type);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.notEqual(argValueList.get(0), TRUE_EXPR);
+    }
+  }
+
+  /** Implementor for the {@code IS NULL} SQL operator. */
+  private static class IsNullImplementor extends AbstractRexCallImplementor {
+    IsNullImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "is_null";
+    }
+
+    @Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
+      return Expressions.constant(true, type);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.equal(argValueList.get(0), NULL_EXPR);
+    }
+  }
+
+  /** Implementor for the {@code IS TRUE} SQL operator. */
+  private static class IsTrueImplementor extends AbstractRexCallImplementor {
+    IsTrueImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "is_true";
+    }
+
+    @Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
+      return Expressions.constant(false, type);
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.equal(argValueList.get(0), TRUE_EXPR);
+    }
+  }
+
+  /** Implementor for the {@code REGEXP_REPLACE} function. */
+  private static class RegexpReplaceImplementor extends AbstractRexCallImplementor {
+    private final AbstractRexCallImplementor[] implementors = {
+        new ReflectiveImplementor(BuiltInMethod.REGEXP_REPLACE3.method, nullPolicy),
+        new ReflectiveImplementor(BuiltInMethod.REGEXP_REPLACE4.method, nullPolicy),
+        new ReflectiveImplementor(BuiltInMethod.REGEXP_REPLACE5.method, nullPolicy),
+        new ReflectiveImplementor(BuiltInMethod.REGEXP_REPLACE6.method, nullPolicy),
+    };
+
+    RegexpReplaceImplementor() {
+      super(NullPolicy.STRICT, false);
+    }
+
+    @Override String getVariableName() {
+      return "regexp_replace";
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList) {
+      return implementors[call.getOperands().size() - 3]
+          .implementSafe(translator, call, argValueList);
     }
   }
 
-  /** Implements tumbling. */
+  /** Implementor for the {@code DEFAULT} function. */
+  private static class DefaultImplementor extends AbstractRexCallImplementor {
+    DefaultImplementor() {
+      super(NullPolicy.NONE, false);
+    }
+
+    @Override String getVariableName() {
+      return "default";
+    }
+
+    @Override Expression implementSafe(final RexToLixTranslator translator,
+        final RexCall call, final List<Expression> argValueList) {
+      return Expressions.constant(null);
+    }
+  }
+
+  /** Implements the {@code TUMBLE} table function. */
   private static class TumbleImplementor implements TableFunctionCallImplementor {
     @Override public Expression implement(RexToLixTranslator translator,
         Expression inputEnumerable,
@@ -3225,25 +3378,23 @@ public class RexImpTable {
       RexCall descriptor = (RexCall) call.getOperands().get(0);
       List<Expression> translatedOperands = new ArrayList<>();
       final ParameterExpression parameter =
-          Expressions.parameter(Primitive.box(inputPhysType.getJavaRowType()), "_input");
-      Expression wmColExpr = inputPhysType.fieldReference(
-          parameter, ((RexInputRef) descriptor.getOperands().get(0)).getIndex(),
-          outputPhysType.getJavaFieldType(inputPhysType.getRowType().getFieldCount()));
+          Expressions.parameter(Primitive.box(inputPhysType.getJavaRowType()),
+              "_input");
+      Expression wmColExpr =
+          inputPhysType.fieldReference(parameter,
+              ((RexInputRef) descriptor.getOperands().get(0)).getIndex(),
+              outputPhysType.getJavaFieldType(
+                  inputPhysType.getRowType().getFieldCount()));
       translatedOperands.add(wmColExpr);
       translatedOperands.add(intervalExpression);
 
-      return Expressions.call(
-          BuiltInMethod.TUMBLING.method,
-          inputEnumerable,
-          EnumUtils.tumblingWindowSelector(
-              inputPhysType,
-              outputPhysType,
-              translatedOperands.get(0),
-              translatedOperands.get(1)));
+      return Expressions.call(BuiltInMethod.TUMBLING.method, inputEnumerable,
+          EnumUtils.tumblingWindowSelector(inputPhysType, outputPhysType,
+              translatedOperands.get(0), translatedOperands.get(1)));
     }
   }
 
-  /** Implements hopping. */
+  /** Implements the {@code HOP} table function. */
   private static class HopImplementor implements TableFunctionCallImplementor {
     @Override public Expression implement(RexToLixTranslator translator,
         Expression inputEnumerable, RexCall call, PhysType inputPhysType, PhysType outputPhysType) {
@@ -3257,17 +3408,17 @@ public class RexImpTable {
       translatedOperands.add(slidingInterval);
       translatedOperands.add(windowSize);
 
-      return Expressions.call(
-          BuiltInMethod.HOPPING.method,
+      return Expressions.call(BuiltInMethod.HOPPING.method,
           Expressions.list(
-              Expressions.call(inputEnumerable, BuiltInMethod.ENUMERABLE_ENUMERATOR.method),
+              Expressions.call(inputEnumerable,
+                  BuiltInMethod.ENUMERABLE_ENUMERATOR.method),
               translatedOperands.get(0),
               translatedOperands.get(1),
               translatedOperands.get(2)));
     }
   }
 
-  /** Implements per-key sessionization. */
+  /** Implements the {@code SESSION} table function. */
   private static class SessionImplementor implements TableFunctionCallImplementor {
     @Override public Expression implement(RexToLixTranslator translator,
         Expression inputEnumerable, RexCall call, PhysType inputPhysType, PhysType outputPhysType) {
@@ -3284,8 +3435,7 @@ public class RexImpTable {
       translatedOperands.add(keyColIndexExpr);
       translatedOperands.add(gapInterval);
 
-      return Expressions.call(
-          BuiltInMethod.SESSIONIZATION.method,
+      return Expressions.call(BuiltInMethod.SESSIONIZATION.method,
           Expressions.list(
               Expressions.call(inputEnumerable, BuiltInMethod.ENUMERABLE_ENUMERATOR.method),
               translatedOperands.get(0),
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
index 934f982..8670b80 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
@@ -22,6 +22,7 @@ import org.apache.calcite.avatica.util.ByteString;
 import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.BlockStatement;
 import org.apache.calcite.linq4j.tree.CatchBlock;
 import org.apache.calcite.linq4j.tree.ConstantExpression;
 import org.apache.calcite.linq4j.tree.Expression;
@@ -30,7 +31,6 @@ import org.apache.calcite.linq4j.tree.ParameterExpression;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.linq4j.tree.Statement;
 import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexCorrelVariable;
@@ -40,10 +40,15 @@ import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexOver;
+import org.apache.calcite.rex.RexPatternFieldRef;
 import org.apache.calcite.rex.RexProgram;
+import org.apache.calcite.rex.RexRangeRef;
+import org.apache.calcite.rex.RexSubQuery;
+import org.apache.calcite.rex.RexTableInputRef;
+import org.apache.calcite.rex.RexVisitor;
 import org.apache.calcite.runtime.SqlFunctions;
 import org.apache.calcite.sql.SqlIntervalQualifier;
-import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlWindowTableFunction;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -55,22 +60,22 @@ import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TRANSLATE3;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CASE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHARACTER_LENGTH;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHAR_LENGTH;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.PREV;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.SUBSTRING;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.UPPER;
 
@@ -78,7 +83,7 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.UPPER;
  * Translates {@link org.apache.calcite.rex.RexNode REX expressions} to
  * {@link Expression linq4j expressions}.
  */
-public class RexToLixTranslator {
+public class RexToLixTranslator implements RexVisitor<RexToLixTranslator.Result> {
   public static final Map<Method, SqlOperator> JAVA_TO_SQL_METHOD_MAP =
       Util.mapOf(
           findMethod(String.class, "toUpperCase"), UPPER,
@@ -99,10 +104,36 @@ public class RexToLixTranslator {
   private final Expression root;
   final RexToLixTranslator.InputGetter inputGetter;
   private final BlockBuilder list;
-  private final Map<? extends RexNode, Boolean> exprNullableMap;
-  private final RexToLixTranslator parent;
   private final Function1<String, InputGetter> correlates;
 
+  /**
+   * Map from RexLiteral's variable name to its literal, which is often a
+   * ({@link org.apache.calcite.linq4j.tree.ConstantExpression}))
+   * It is used in the some {@code RexCall}'s implementors, such as
+   * {@code ExtractImplementor}.
+   *
+   * @see #getLiteral
+   * @see #getLiteralValue
+   */
+  private final Map<Expression, Expression> literalMap = new HashMap<>();
+
+  /** For {@code RexCall}, keep the list of its operand's {@code Result}.
+   * It is useful when creating a {@code CallImplementor}. */
+  private final Map<RexCall, List<Result>> callOperandResultMap =
+      new HashMap<>();
+
+  /** Map from RexNode under specific storage type to its Result, to avoid
+   * generating duplicate code. For {@code RexInputRef}, {@code RexDynamicParam}
+   * and {@code RexFieldAccess}. */
+  private final Map<Pair<RexNode, Type>, Result> rexWithStorageTypeResultMap =
+      new HashMap<>();
+
+  /** Map from RexNode to its Result, to avoid generating duplicate code.
+   * For {@code RexLiteral} and {@code RexCall}. */
+  private final Map<RexNode, Result> rexResultMap = new HashMap<>();
+
+  private Type currentStorageType;
+
   private static Method findMethod(
       Class<?> clazz, String name, Class... parameterTypes) {
     try {
@@ -117,10 +148,8 @@ public class RexToLixTranslator {
       Expression root,
       InputGetter inputGetter,
       BlockBuilder list,
-      Map<? extends RexNode, Boolean> exprNullableMap,
       RexBuilder builder,
       SqlConformance conformance,
-      RexToLixTranslator parent,
       Function1<String, InputGetter> correlates) {
     this.program = program; // may be null
     this.typeFactory = Objects.requireNonNull(typeFactory);
@@ -128,9 +157,7 @@ public class RexToLixTranslator {
     this.root = Objects.requireNonNull(root);
     this.inputGetter = inputGetter;
     this.list = Objects.requireNonNull(list);
-    this.exprNullableMap = Objects.requireNonNull(exprNullableMap);
     this.builder = Objects.requireNonNull(builder);
-    this.parent = parent; // may be null
     this.correlates = correlates; // may be null
   }
 
@@ -162,8 +189,7 @@ public class RexToLixTranslator {
       }
     }
     return new RexToLixTranslator(program, typeFactory, root, inputGetter,
-        list, Collections.emptyMap(), new RexBuilder(typeFactory), conformance,
-        null, null)
+        list, new RexBuilder(typeFactory), conformance, null)
         .setCorrelates(correlates)
         .translateList(program.getProjectList(), storageTypes);
   }
@@ -173,8 +199,7 @@ public class RexToLixTranslator {
       Expression root, RexCall rexCall, Expression inputEnumerable,
       PhysType inputPhysType, PhysType outputPhysType) {
     return new RexToLixTranslator(null, typeFactory, root, null,
-        blockBuilder, Collections.emptyMap(), new RexBuilder(typeFactory), conformance,
-        null, null)
+        blockBuilder, new RexBuilder(typeFactory), conformance, null)
         .translateTableFunction(rexCall, inputEnumerable, inputPhysType, outputPhysType);
   }
 
@@ -183,8 +208,7 @@ public class RexToLixTranslator {
       BlockBuilder list, InputGetter inputGetter, SqlConformance conformance) {
     final ParameterExpression root = DataContext.ROOT;
     return new RexToLixTranslator(null, typeFactory, root, inputGetter, list,
-        Collections.emptyMap(), new RexBuilder(typeFactory), conformance, null,
-        null);
+        new RexBuilder(typeFactory), conformance, null);
   }
 
   Expression translate(RexNode expr) {
@@ -205,10 +229,17 @@ public class RexToLixTranslator {
 
   Expression translate(RexNode expr, RexImpTable.NullAs nullAs,
       Type storageType) {
-    Expression expression = translate0(expr, nullAs, storageType);
-    expression = EnumUtils.toInternal(expression, storageType);
-    assert expression != null;
-    return list.append("v", expression);
+    currentStorageType = storageType;
+    final Result result = expr.accept(this);
+    final Expression translated =
+        EnumUtils.toInternal(result.valueVariable, storageType);
+    assert translated != null;
+    // When we asked for not null input that would be stored as box, avoid unboxing
+    if (RexImpTable.NullAs.NOT_POSSIBLE == nullAs
+        && translated.type.equals(storageType)) {
+      return translated;
+    }
+    return nullAs.handle(translated);
   }
 
   Expression translateCast(
@@ -582,57 +613,6 @@ public class RexToLixTranslator {
     return scaleIntervalToNumber(sourceType, targetType, convert);
   }
 
-  private Expression handleNullUnboxingIfNecessary(
-      Expression input,
-      RexImpTable.NullAs nullAs,
-      Type storageType) {
-    if (RexImpTable.NullAs.NOT_POSSIBLE == nullAs && input.type.equals(storageType)) {
-      // When we asked for not null input that would be stored as box, avoid
-      // unboxing which may occur in the handleNull method below.
-      return input;
-    }
-    return handleNull(input, nullAs);
-  }
-
-  /** Adapts an expression with "normal" result to one that adheres to
-   * this particular policy. Wraps the result expression into a new
-   * parameter if need be.
-   *
-   * @param input Expression
-   * @param nullAs If false, if expression is definitely not null at
-   *   runtime. Therefore we can optimize. For example, we can cast to int
-   *   using x.intValue().
-   * @return Translated expression
-   */
-  public Expression handleNull(Expression input, RexImpTable.NullAs nullAs) {
-    final Expression nullHandled = nullAs.handle(input);
-
-    // If we get ConstantExpression, just return it (i.e. primitive false)
-    if (nullHandled instanceof ConstantExpression) {
-      return nullHandled;
-    }
-
-    // if nullHandled expression is the same as "input",
-    // then we can just reuse it
-    if (nullHandled == input) {
-      return input;
-    }
-
-    // If nullHandled is different, then it might be unsafe to compute
-    // early (i.e. unbox of null value should not happen _before_ ternary).
-    // Thus we wrap it into brand-new ParameterExpression,
-    // and we are guaranteed that ParameterExpression will not be shared
-    String unboxVarName = "v_unboxed";
-    if (input instanceof ParameterExpression) {
-      unboxVarName = ((ParameterExpression) input).name + "_unboxed";
-    }
-    ParameterExpression unboxed = Expressions.parameter(nullHandled.getType(),
-        list.newName(unboxVarName));
-    list.add(Expressions.declare(Modifier.FINAL, unboxed, nullHandled));
-
-    return unboxed;
-  }
-
   /**
    * Handle checked Exceptions declared in Method. In such case,
    * method call should be wrapped in a try...catch block.
@@ -659,88 +639,6 @@ public class RexToLixTranslator {
     return methodCall;
   }
 
-  /** Translates an expression that is not in the cache.
-   *
-   * @param expr Expression
-   * @param nullAs If false, if expression is definitely not null at
-   *   runtime. Therefore we can optimize. For example, we can cast to int
-   *   using x.intValue().
-   * @return Translated expression
-   */
-  private Expression translate0(RexNode expr, RexImpTable.NullAs nullAs,
-      Type storageType) {
-    if (nullAs == RexImpTable.NullAs.NULL && !expr.getType().isNullable()) {
-      nullAs = RexImpTable.NullAs.NOT_POSSIBLE;
-    }
-    switch (expr.getKind()) {
-    case INPUT_REF: {
-      final int index = ((RexInputRef) expr).getIndex();
-      Expression x = inputGetter.field(list, index, storageType);
-
-      Expression input = list.append("inp" + index + "_", x); // safe to share
-      return handleNullUnboxingIfNecessary(input, nullAs, storageType);
-    }
-    case PATTERN_INPUT_REF: {
-      final int index = ((RexInputRef) expr).getIndex();
-      Expression x = inputGetter.field(list, index, storageType);
-
-      Expression input = list.append("inp" + index + "_", x); // safe to share
-      return handleNullUnboxingIfNecessary(input, nullAs, storageType);
-    }
-    case LOCAL_REF:
-      return translate(
-          deref(expr),
-          nullAs,
-          storageType);
-    case LITERAL:
-      return translateLiteral(
-          (RexLiteral) expr,
-          nullifyType(
-              expr.getType(),
-              isNullable(expr)
-                  && nullAs != RexImpTable.NullAs.NOT_POSSIBLE),
-          typeFactory,
-          nullAs);
-    case DYNAMIC_PARAM:
-      return translateParameter((RexDynamicParam) expr, nullAs, storageType);
-    case CORREL_VARIABLE:
-      throw new RuntimeException("Cannot translate " + expr + ". Correlated"
-          + " variables should always be referenced by field access");
-    case FIELD_ACCESS: {
-      RexFieldAccess fieldAccess = (RexFieldAccess) expr;
-      RexNode target = deref(fieldAccess.getReferenceExpr());
-      int fieldIndex = fieldAccess.getField().getIndex();
-      String fieldName = fieldAccess.getField().getName();
-      switch (target.getKind()) {
-      case CORREL_VARIABLE:
-        if (correlates == null) {
-          throw new RuntimeException("Cannot translate " + expr + " since "
-              + "correlate variables resolver is not defined");
-        }
-        InputGetter getter =
-            correlates.apply(((RexCorrelVariable) target).getName());
-        Expression y = getter.field(list, fieldIndex, storageType);
-        Expression input = list.append("corInp" + fieldIndex + "_", y); // safe to share
-        return handleNullUnboxingIfNecessary(input, nullAs, storageType);
-      default:
-        RexNode rxIndex = builder.makeLiteral(fieldIndex, typeFactory.createType(int.class), true);
-        RexNode rxName = builder.makeLiteral(fieldName, typeFactory.createType(String.class), true);
-        RexCall accessCall = (RexCall) builder.makeCall(
-            fieldAccess.getType(),
-            SqlStdOperatorTable.STRUCT_ACCESS,
-            ImmutableList.of(target, rxIndex, rxName));
-        return translateCall(accessCall, nullAs);
-      }
-    }
-    default:
-      if (expr instanceof RexCall) {
-        return translateCall((RexCall) expr, nullAs);
-      }
-      throw new RuntimeException(
-          "cannot translate expression " + expr);
-    }
-  }
-
   /** Dereferences an expression if it is a
    * {@link org.apache.calcite.rex.RexLocalRef}. */
   public RexNode deref(RexNode expr) {
@@ -754,33 +652,9 @@ public class RexToLixTranslator {
     }
   }
 
-  /** Translates a call to an operator or function. */
-  private Expression translateCall(RexCall call, RexImpTable.NullAs nullAs) {
-    final SqlOperator operator = call.getOperator();
-    CallImplementor implementor =
-        RexImpTable.INSTANCE.get(operator);
-    if (implementor == null) {
-      throw new RuntimeException("cannot translate call " + call);
-    }
-    return implementor.implement(this, call, nullAs);
-  }
-
-  /** Translates a parameter. */
-  private Expression translateParameter(RexDynamicParam expr,
-      RexImpTable.NullAs nullAs, Type storageType) {
-    if (storageType == null) {
-      storageType = typeFactory.getJavaClass(expr.getType());
-    }
-    return nullAs.handle(
-        EnumUtils.convert(
-            Expressions.call(root, BuiltInMethod.DATA_CONTEXT_GET.method,
-                Expressions.constant("?" + expr.getIndex())),
-            storageType));
-  }
-
   /** Translates a literal.
    *
-   * @throws AlwaysNull if literal is null but {@code nullAs} is
+   * @throws ControlFlowException if literal is null but {@code nullAs} is
    * {@link org.apache.calcite.adapter.enumerable.RexImpTable.NullAs#NOT_POSSIBLE}.
    */
   public static Expression translateLiteral(
@@ -797,7 +671,7 @@ public class RexToLixTranslator {
       case IS_NOT_NULL:
         return RexImpTable.FALSE_EXPR;
       case NOT_POSSIBLE:
-        throw AlwaysNull.INSTANCE;
+        throw new ControlFlowException();
       case NULL:
       default:
         return RexImpTable.NULL_EXPR;
@@ -967,105 +841,24 @@ public class RexToLixTranslator {
     final ParameterExpression root = DataContext.ROOT;
     RexToLixTranslator translator =
         new RexToLixTranslator(program, typeFactory, root, inputGetter, list,
-            Collections.emptyMap(), new RexBuilder(typeFactory), conformance,
-            null, null);
+            new RexBuilder(typeFactory), conformance, null);
     translator = translator.setCorrelates(correlates);
     return translator.translate(
         program.getCondition(),
         RexImpTable.NullAs.FALSE);
   }
 
-  public Expression translateConstructor(
-      List<RexNode> operandList, SqlKind kind) {
-    switch (kind) {
-    case MAP_VALUE_CONSTRUCTOR:
-      Expression map =
-          list.append(
-              "map",
-              Expressions.new_(LinkedHashMap.class),
-              false);
-      for (int i = 0; i < operandList.size(); i++) {
-        RexNode key = operandList.get(i++);
-        RexNode value = operandList.get(i);
-        list.add(
-            Expressions.statement(
-                Expressions.call(
-                    map,
-                    BuiltInMethod.MAP_PUT.method,
-                    Expressions.box(translate(key)),
-                    Expressions.box(translate(value)))));
-      }
-      return map;
-    case ARRAY_VALUE_CONSTRUCTOR:
-      Expression lyst =
-          list.append(
-              "list",
-              Expressions.new_(ArrayList.class),
-              false);
-      for (RexNode value : operandList) {
-        list.add(
-            Expressions.statement(
-                Expressions.call(
-                    lyst,
-                    BuiltInMethod.COLLECTION_ADD.method,
-                    Expressions.box(translate(value)))));
-      }
-      return lyst;
-    default:
-      throw new AssertionError("unexpected: " + kind);
-    }
-  }
-
-  /** Returns whether an expression is nullable. Even if its type says it is
-   * nullable, if we have previously generated a check to make sure that it is
-   * not null, we will say so.
-   *
-   * <p>For example, {@code WHERE a == b} translates to
-   * {@code a != null && b != null && a.equals(b)}. When translating the
-   * 3rd part of the disjunction, we already know a and b are not null.</p>
-   *
+  /** Returns whether an expression is nullable.
    * @param e Expression
-   * @return Whether expression is nullable in the current translation context
+   * @return Whether expression is nullable
    */
   public boolean isNullable(RexNode e) {
-    if (!e.getType().isNullable()) {
-      return false;
-    }
-    final Boolean b = isKnownNullable(e);
-    return b == null || b;
+    return e.getType().isNullable();
   }
 
-  /**
-   * Walks parent translator chain and verifies if the expression is nullable.
-   *
-   * @param node RexNode to check if it is nullable or not
-   * @return null when nullability is not known, true or false otherwise
-   */
-  protected Boolean isKnownNullable(RexNode node) {
-    if (!exprNullableMap.isEmpty()) {
-      Boolean nullable = exprNullableMap.get(node);
-      if (nullable != null) {
-        return nullable;
-      }
-    }
-    return parent == null ? null : parent.isKnownNullable(node);
-  }
-
-  /** Creates a read-only copy of this translator that records that a given
-   * expression is nullable. */
-  public RexToLixTranslator setNullable(RexNode e, boolean nullable) {
-    return setNullable(Collections.singletonMap(e, nullable));
-  }
-
-  /** Creates a read-only copy of this translator that records that a given
-   * expression is nullable. */
   public RexToLixTranslator setNullable(
       Map<? extends RexNode, Boolean> nullable) {
-    if (nullable == null || nullable.isEmpty()) {
-      return this;
-    }
-    return new RexToLixTranslator(program, typeFactory, root, inputGetter, list,
-        nullable, builder, conformance, this, correlates);
+    return this;
   }
 
   public RexToLixTranslator setBlock(BlockBuilder block) {
@@ -1073,7 +866,7 @@ public class RexToLixTranslator {
       return this;
     }
     return new RexToLixTranslator(program, typeFactory, root, inputGetter,
-        block, ImmutableMap.of(), builder, conformance, this, correlates);
+        block, builder, conformance, correlates);
   }
 
   public RexToLixTranslator setCorrelates(
@@ -1082,33 +875,7 @@ public class RexToLixTranslator {
       return this;
     }
     return new RexToLixTranslator(program, typeFactory, root, inputGetter, list,
-        Collections.emptyMap(), builder, conformance, this, correlates);
-  }
-
-  private RexToLixTranslator withConformance(SqlConformance conformance) {
-    if (conformance == this.conformance) {
-      return this;
-    }
-    return new RexToLixTranslator(program, typeFactory, root, inputGetter, list,
-        Collections.emptyMap(), builder, conformance, this, correlates);
-  }
-
-  public RelDataType nullifyType(RelDataType type, boolean nullable) {
-    if (!nullable) {
-      final Primitive primitive = javaPrimitive(type);
-      if (primitive != null) {
-        return typeFactory.createJavaType(primitive.primitiveClass);
-      }
-    }
-    return typeFactory.createTypeWithNullability(type, nullable);
-  }
-
-  private Primitive javaPrimitive(RelDataType type) {
-    if (type instanceof RelDataTypeFactoryImpl.JavaType) {
-      return Primitive.ofBox(
-          ((RelDataTypeFactoryImpl.JavaType) type).getJavaClass());
-    }
-    return null;
+        builder, conformance, correlates);
   }
 
   public Expression getRoot() {
@@ -1145,6 +912,462 @@ public class RexToLixTranslator {
     return operand;
   }
 
+  /**
+   * Visit {@code RexInputRef}. If it has never been visited
+   * under current storage type before, {@code RexToLixTranslator}
+   * generally produces three lines of code.
+   * For example, when visiting a column (named commission) in
+   * table Employee, the generated code snippet is:
+   * {@code
+   *   final Employee current =(Employee) inputEnumerator.current();
+       final Integer input_value = current.commission;
+       final boolean input_isNull = input_value == null;
+   * }
+   */
+  @Override public Result visitInputRef(RexInputRef inputRef) {
+    final Pair<RexNode, Type> key = Pair.of(inputRef, currentStorageType);
+    // If the RexInputRef has been visited under current storage type already,
+    // it is not necessary to visit it again, just return the result.
+    if (rexWithStorageTypeResultMap.containsKey(key)) {
+      return rexWithStorageTypeResultMap.get(key);
+    }
+    // Generate one line of code to get the input, e.g.,
+    // "final Employee current =(Employee) inputEnumerator.current();"
+    final Expression valueExpression = inputGetter.field(
+        list, inputRef.getIndex(), currentStorageType);
+
+    // Generate one line of code for the value of RexInputRef, e.g.,
+    // "final Integer input_value = current.commission;"
+    final ParameterExpression valueVariable =
+        Expressions.parameter(
+            valueExpression.getType(), list.newName("input_value"));
+    list.add(Expressions.declare(Modifier.FINAL, valueVariable, valueExpression));
+
+    // Generate one line of code to check whether RexInputRef is null, e.g.,
+    // "final boolean input_isNull = input_value == null;"
+    final Expression isNullExpression = checkNull(valueVariable);
+    final ParameterExpression isNullVariable =
+        Expressions.parameter(
+            Boolean.TYPE, list.newName("input_isNull"));
+    list.add(Expressions.declare(Modifier.FINAL, isNullVariable, isNullExpression));
+
+    final Result result = new Result(isNullVariable, valueVariable);
+
+    // Cache <RexInputRef, currentStorageType>'s result
+    // Note: EnumerableMatch's PrevInputGetter changes index each time,
+    // it is not right to reuse the result under such case.
+    if (!(inputGetter instanceof EnumerableMatch.PrevInputGetter)) {
+      rexWithStorageTypeResultMap.put(key, result);
+    }
+    return new Result(isNullVariable, valueVariable);
+  }
+
+  @Override public Result visitLocalRef(RexLocalRef localRef) {
+    return deref(localRef).accept(this);
+  }
+
+  /**
+   * Visit {@code RexLiteral}. If it has never been visited before,
+   * {@code RexToLixTranslator} will generate two lines of code. For example,
+   * when visiting a primitive int (10), the generated code snippet is:
+   * {@code
+   *   final int literal_value = 10;
+   *   final boolean literal_isNull = false;
+   * }
+   */
+  @Override public Result visitLiteral(RexLiteral literal) {
+    // If the RexLiteral has been visited already, just return the result
+    if (rexResultMap.containsKey(literal)) {
+      return rexResultMap.get(literal);
+    }
+    // Generate one line of code for the value of RexLiteral, e.g.,
+    // "final int literal_value = 10;"
+    final Expression valueExpression = literal.isNull()
+        // Note: even for null literal, we can't loss its type information
+        ? getTypedNullLiteral(literal)
+        : translateLiteral(literal, literal.getType(),
+            typeFactory, RexImpTable.NullAs.NOT_POSSIBLE);
+    final ParameterExpression valueVariable =
+        Expressions.parameter(valueExpression.getType(),
+            list.newName("literal_value"));
+    list.add(Expressions.declare(Modifier.FINAL, valueVariable, valueExpression));
+
+    // Generate one line of code to check whether RexLiteral is null, e.g.,
+    // "final boolean literal_isNull = false;"
+    final Expression isNullExpression =
+        literal.isNull() ? RexImpTable.TRUE_EXPR : RexImpTable.FALSE_EXPR;
+    final ParameterExpression isNullVariable = Expressions.parameter(
+        Boolean.TYPE, list.newName("literal_isNull"));
+    list.add(Expressions.declare(Modifier.FINAL, isNullVariable, isNullExpression));
+
+    // Maintain the map from valueVariable (ParameterExpression) to real Expression
+    literalMap.put(valueVariable, valueExpression);
+    final Result result = new Result(isNullVariable, valueVariable);
+    // Cache RexLiteral's result
+    rexResultMap.put(literal, result);
+    return result;
+  }
+
+  /**
+   * Returns an {@code Expression} for null literal without losing its type
+   * information.
+   */
+  private ConstantExpression getTypedNullLiteral(RexLiteral literal) {
+    assert literal.isNull();
+    Type javaClass = typeFactory.getJavaClass(literal.getType());
+    switch (literal.getType().getSqlTypeName()) {
+    case DATE:
+    case TIME:
+    case TIME_WITH_LOCAL_TIME_ZONE:
+    case INTERVAL_YEAR:
+    case INTERVAL_YEAR_MONTH:
+    case INTERVAL_MONTH:
+      javaClass = Integer.class;
+      break;
+    case TIMESTAMP:
+    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+    case INTERVAL_DAY:
+    case INTERVAL_DAY_HOUR:
+    case INTERVAL_DAY_MINUTE:
+    case INTERVAL_DAY_SECOND:
+    case INTERVAL_HOUR:
+    case INTERVAL_HOUR_MINUTE:
+    case INTERVAL_HOUR_SECOND:
+    case INTERVAL_MINUTE:
+    case INTERVAL_MINUTE_SECOND:
+    case INTERVAL_SECOND:
+      javaClass = Long.class;
+      break;
+    }
+    return javaClass == null || javaClass == Void.class
+        ? RexImpTable.NULL_EXPR
+        : Expressions.constant(null, javaClass);
+  }
+
+  /**
+   * Visit {@code RexCall}. For most {@code SqlOperator}s, we can get the implementor
+   * from {@code RexImpTable}. Several operators (e.g., CaseWhen) with special semantics
+   * need to be implemented separately.
+   */
+  @Override public Result visitCall(RexCall call) {
+    if (rexResultMap.containsKey(call)) {
+      return rexResultMap.get(call);
+    }
+    final SqlOperator operator = call.getOperator();
+    if (operator == PREV) {
+      return implementPrev(call);
+    }
+    if (operator == CASE) {
+      return implementCaseWhen(call);
+    }
+    final RexImpTable.RexCallImplementor implementor =
+        RexImpTable.INSTANCE.get(operator);
+    if (implementor == null) {
+      throw new RuntimeException("cannot translate call " + call);
+    }
+    final List<RexNode> operandList = call.getOperands();
+    final List<Type> storageTypes = EnumUtils.internalTypes(operandList);
+    final List<Result> operandResults = new ArrayList<>();
+    for (int i = 0; i < operandList.size(); i++) {
+      final Result operandResult =
+          implementCallOperand(operandList.get(i), storageTypes.get(i), this);
+      operandResults.add(operandResult);
+    }
+    callOperandResultMap.put(call, operandResults);
+    final Result result = implementor.implement(this, call, operandResults);
+    rexResultMap.put(call, result);
+    return result;
+  }
+
+  private static Result implementCallOperand(final RexNode operand,
+      final Type storageType, final RexToLixTranslator translator) {
+    final Type originalStorageType = translator.currentStorageType;
+    translator.currentStorageType = storageType;
+    Result operandResult = operand.accept(translator);
+    if (storageType != null) {
+      operandResult = translator.toInnerStorageType(operandResult, storageType);
+    }
+    translator.currentStorageType = originalStorageType;
+    return operandResult;
+  }
+
+  private static Expression implementCallOperand2(final RexNode operand,
+      final Type storageType, final RexToLixTranslator translator) {
+    final Type originalStorageType = translator.currentStorageType;
+    translator.currentStorageType = storageType;
+    final Expression result =  translator.translate(operand);
+    translator.currentStorageType = originalStorageType;
+    return result;
+  }
+
+  /**
+   * For {@code PREV} operator, the offset of {@code inputGetter}
+   * should be set first
+   */
+  private Result implementPrev(RexCall call) {
+    final RexNode node = call.getOperands().get(0);
+    final RexNode offset = call.getOperands().get(1);
+    final Expression offs = Expressions.multiply(translate(offset),
+            Expressions.constant(-1));
+    ((EnumerableMatch.PrevInputGetter) inputGetter).setOffset(offs);
+    return node.accept(this);
+  }
+
+  /**
+   * The CASE operator is SQL’s way of handling if/then logic.
+   * Different with other {@code RexCall}s, it is not safe to
+   * implement its operands first.
+   * For example: {@code
+   *   select case when s=0 then false
+   *          else 100/s > 0 end
+   *   from (values (1),(0)) ax(s);
+   * }
+   */
+  private Result implementCaseWhen(RexCall call) {
+    final Type returnType = typeFactory.getJavaClass(call.getType());
+    final ParameterExpression valueVariable =
+        Expressions.parameter(returnType,
+            list.newName("case_when_value"));
+    list.add(Expressions.declare(0, valueVariable, null));
+    final List<RexNode> operandList = call.getOperands();
+    implementRecursively(this, operandList, valueVariable, 0);
+    final Expression isNullExpression = checkNull(valueVariable);
+    final ParameterExpression isNullVariable =
+        Expressions.parameter(
+            Boolean.TYPE, list.newName("case_when_isNull"));
+    list.add(Expressions.declare(Modifier.FINAL, isNullVariable, isNullExpression));
+    final Result result = new Result(isNullVariable, valueVariable);
+    rexResultMap.put(call, result);
+    return result;
+  }
+
+  /**
+   * Case statements of the form:
+   * {@code CASE WHEN a THEN b [WHEN c THEN d]* [ELSE e] END}.
+   * When {@code a = true}, returns {@code b};
+   * when {@code c = true}, returns {@code d};
+   * else returns {@code e}.
+   *
+   * <p>We generate code that looks like:
+   *
+   * <blockquote><pre>
+   *      int case_when_value;
+   *      ......code for a......
+   *      if (!a_isNull && a_value) {
+   *          ......code for b......
+   *          case_when_value = res(b_isNull, b_value);
+   *      } else {
+   *          ......code for c......
+   *          if (!c_isNull && c_value) {
+   *              ......code for d......
+   *              case_when_value = res(d_isNull, d_value);
+   *          } else {
+   *              ......code for e......
+   *              case_when_value = res(e_isNull, e_value);
+   *          }
+   *      }
+   * </pre></blockquote>
+   */
+  private void implementRecursively(final RexToLixTranslator currentTranslator,
+      final List<RexNode> operandList, final ParameterExpression valueVariable, int pos) {
+    final BlockBuilder currentBlockBuilder = currentTranslator.getBlockBuilder();
+    final List<Type> storageTypes = EnumUtils.internalTypes(operandList);
+    // [ELSE] clause
+    if (pos == operandList.size() - 1) {
+      Expression res = implementCallOperand2(operandList.get(pos),
+          storageTypes.get(pos), currentTranslator);
+      currentBlockBuilder.add(
+          Expressions.statement(
+              Expressions.assign(valueVariable,
+                  EnumUtils.convert(res, valueVariable.getType()))));
+      return;
+    }
+    // Condition code: !a_isNull && a_value
+    final RexNode testerNode = operandList.get(pos);
+    final Result testerResult = implementCallOperand(testerNode,
+        storageTypes.get(pos), currentTranslator);
+    final Expression tester = Expressions.andAlso(
+        Expressions.not(testerResult.isNullVariable),
+        testerResult.valueVariable);
+    // Code for {if} branch
+    final RexNode ifTrueNode = operandList.get(pos + 1);
+    final BlockBuilder ifTrueBlockBuilder =
+        new BlockBuilder(true, currentBlockBuilder);
+    final RexToLixTranslator ifTrueTranslator =
+        currentTranslator.setBlock(ifTrueBlockBuilder);
+    final Expression ifTrueRes = implementCallOperand2(ifTrueNode,
+        storageTypes.get(pos + 1), ifTrueTranslator);
+    // Assign the value: case_when_value = ifTrueRes
+    ifTrueBlockBuilder.add(
+        Expressions.statement(
+            Expressions.assign(valueVariable,
+                EnumUtils.convert(ifTrueRes, valueVariable.getType()))));
+    final BlockStatement ifTrue = ifTrueBlockBuilder.toBlock();
+    // There is no [ELSE] clause
+    if (pos + 1 == operandList.size() - 1) {
+      currentBlockBuilder.add(
+          Expressions.ifThen(tester, ifTrue));
+      return;
+    }
+    // Generate code for {else} branch recursively
+    final BlockBuilder ifFalseBlockBuilder =
+        new BlockBuilder(true, currentBlockBuilder);
+    final RexToLixTranslator ifFalseTranslator =
+        currentTranslator.setBlock(ifFalseBlockBuilder);
+    implementRecursively(ifFalseTranslator, operandList, valueVariable, pos + 2);
+    final BlockStatement ifFalse = ifFalseBlockBuilder.toBlock();
+    currentBlockBuilder.add(
+        Expressions.ifThenElse(tester, ifTrue, ifFalse));
+  }
+
+  private Result toInnerStorageType(final Result result, final Type storageType) {
+    final Expression valueExpression =
+        EnumUtils.toInternal(result.valueVariable, storageType);
+    if (valueExpression.equals(result.valueVariable)) {
+      return result;
+    }
+    final ParameterExpression valueVariable =
+        Expressions.parameter(
+            valueExpression.getType(),
+            list.newName(result.valueVariable.name + "_inner_type"));
+    list.add(Expressions.declare(Modifier.FINAL, valueVariable, valueExpression));
+    final ParameterExpression isNullVariable = result.isNullVariable;
+    return new Result(isNullVariable, valueVariable);
+  }
+
+  @Override public Result visitDynamicParam(RexDynamicParam dynamicParam) {
+    final Pair<RexNode, Type> key = Pair.of(dynamicParam, currentStorageType);
+    if (rexWithStorageTypeResultMap.containsKey(key)) {
+      return rexWithStorageTypeResultMap.get(key);
+    }
+    final Type storageType = currentStorageType != null
+        ? currentStorageType : typeFactory.getJavaClass(dynamicParam.getType());
+    final Expression valueExpression = EnumUtils.convert(
+        Expressions.call(root, BuiltInMethod.DATA_CONTEXT_GET.method,
+            Expressions.constant("?" + dynamicParam.getIndex())),
+        storageType);
+    final ParameterExpression valueVariable =
+        Expressions.parameter(valueExpression.getType(), list.newName("value_dynamic_param"));
+    list.add(Expressions.declare(Modifier.FINAL, valueVariable, valueExpression));
+    final ParameterExpression isNullVariable =
+        Expressions.parameter(Boolean.TYPE, list.newName("isNull_dynamic_param"));
+    list.add(Expressions.declare(Modifier.FINAL, isNullVariable, checkNull(valueVariable)));
+    final Result result = new Result(isNullVariable, valueVariable);
+    rexWithStorageTypeResultMap.put(key, result);
+    return result;
+  }
+
+  @Override public Result visitFieldAccess(RexFieldAccess fieldAccess) {
+    final Pair<RexNode, Type> key = Pair.of(fieldAccess, currentStorageType);
+    if (rexWithStorageTypeResultMap.containsKey(key)) {
+      return rexWithStorageTypeResultMap.get(key);
+    }
+    final RexNode target = deref(fieldAccess.getReferenceExpr());
+    int fieldIndex = fieldAccess.getField().getIndex();
+    String fieldName = fieldAccess.getField().getName();
+    switch (target.getKind()) {
+    case CORREL_VARIABLE:
+      if (correlates == null) {
+        throw new RuntimeException("Cannot translate " + fieldAccess
+            + " since correlate variables resolver is not defined");
+      }
+      final RexToLixTranslator.InputGetter getter =
+          correlates.apply(((RexCorrelVariable) target).getName());
+      final Expression input = getter.field(
+          list, fieldIndex, currentStorageType);
+      final Expression condition = checkNull(input);
+      final ParameterExpression valueVariable =
+          Expressions.parameter(input.getType(), list.newName("corInp_value"));
+      list.add(Expressions.declare(Modifier.FINAL, valueVariable, input));
+      final ParameterExpression isNullVariable =
+          Expressions.parameter(Boolean.TYPE, list.newName("corInp_isNull"));
+      final Expression isNullExpression = Expressions.condition(
+          condition,
+          RexImpTable.TRUE_EXPR,
+          checkNull(valueVariable));
+      list.add(Expressions.declare(Modifier.FINAL, isNullVariable, isNullExpression));
+      final Result result1 = new Result(isNullVariable, valueVariable);
+      rexWithStorageTypeResultMap.put(key, result1);
+      return result1;
+    default:
+      RexNode rxIndex =
+          builder.makeLiteral(fieldIndex, typeFactory.createType(int.class), true);
+      RexNode rxName =
+          builder.makeLiteral(fieldName, typeFactory.createType(String.class), true);
+      RexCall accessCall = (RexCall) builder.makeCall(
+          fieldAccess.getType(), SqlStdOperatorTable.STRUCT_ACCESS,
+          ImmutableList.of(target, rxIndex, rxName));
+      final Result result2 = accessCall.accept(this);
+      rexWithStorageTypeResultMap.put(key, result2);
+      return result2;
+    }
+  }
+
+  @Override public Result visitOver(RexOver over) {
+    throw new RuntimeException("cannot translate expression " + over);
+  }
+
+  @Override public Result visitCorrelVariable(RexCorrelVariable correlVariable) {
+    throw new RuntimeException("Cannot translate " + correlVariable
+        + ". Correlated variables should always be referenced by field access");
+  }
+
+  @Override public Result visitRangeRef(RexRangeRef rangeRef) {
+    throw new RuntimeException("cannot translate expression " + rangeRef);
+  }
+
+  @Override public Result visitSubQuery(RexSubQuery subQuery) {
+    throw new RuntimeException("cannot translate expression " + subQuery);
+  }
+
+  @Override public Result visitTableInputRef(RexTableInputRef fieldRef) {
+    throw new RuntimeException("cannot translate expression " + fieldRef);
+  }
+
+  @Override public Result visitPatternFieldRef(RexPatternFieldRef fieldRef) {
+    return visitInputRef(fieldRef);
+  }
+
+  Expression checkNull(Expression expr) {
+    if (Primitive.flavor(expr.getType())
+        == Primitive.Flavor.PRIMITIVE) {
+      return RexImpTable.FALSE_EXPR;
+    }
+    return Expressions.equal(expr, RexImpTable.NULL_EXPR);
+  }
+
+  Expression checkNotNull(Expression expr) {
+    if (Primitive.flavor(expr.getType())
+        == Primitive.Flavor.PRIMITIVE) {
+      return RexImpTable.TRUE_EXPR;
+    }
+    return Expressions.notEqual(expr, RexImpTable.NULL_EXPR);
+  }
+
+  BlockBuilder getBlockBuilder() {
+    return list;
+  }
+
+  Expression getLiteral(Expression literalVariable) {
+    return literalMap.get(literalVariable);
+  }
+
+  /** Returns the value of a literal. */
+  Object getLiteralValue(Expression expr) {
+    if (expr instanceof ParameterExpression) {
+      final Expression constantExpr = literalMap.get(expr);
+      return getLiteralValue(constantExpr);
+    }
+    if (expr instanceof ConstantExpression) {
+      return ((ConstantExpression) expr).value;
+    }
+    return null;
+  }
+
+  List<Result> getCallOperandResult(RexCall call) {
+    return callOperandResultMap.get(call);
+  }
+
   /** Translates a field of an input to an expression. */
   public interface InputGetter {
     Expression field(BlockBuilder list, int index, Type storageType);
@@ -1175,14 +1398,15 @@ public class RexToLixTranslator {
     }
   }
 
-  /** Thrown in the unusual (but not erroneous) situation where the expression
-   * we are translating is the null literal but we have already checked that
-   * it is not null. It is easier to throw (and caller will always handle)
-   * than to check exhaustively beforehand. */
-  static class AlwaysNull extends ControlFlowException {
-    @SuppressWarnings("ThrowableInstanceNeverThrown")
-    public static final AlwaysNull INSTANCE = new AlwaysNull();
+  /** Result of translating a {@code RexNode}. */
+  public static class Result {
+    final ParameterExpression isNullVariable;
+    final ParameterExpression valueVariable;
 
-    private AlwaysNull() {}
+    public Result(ParameterExpression isNullVariable,
+        ParameterExpression valueVariable) {
+      this.isNullVariable = isNullVariable;
+      this.valueVariable = valueVariable;
+    }
   }
 }
diff --git a/core/src/main/java/org/apache/calcite/rex/RexCall.java b/core/src/main/java/org/apache/calcite/rex/RexCall.java
index 9ebe524..701df4d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCall.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java
@@ -327,7 +327,8 @@ public class RexCall extends RexNode {
     }
     RexCall rexCall = (RexCall) o;
     return op.equals(rexCall.op)
-        && operands.equals(rexCall.operands);
+        && operands.equals(rexCall.operands)
+        && type.equals(rexCall.type);
   }
 
   @Override public int hashCode() {
diff --git a/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java b/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java
index 0b5933a..295d690 100644
--- a/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java
+++ b/core/src/main/java/org/apache/calcite/schema/impl/TableFunctionImpl.java
@@ -137,7 +137,7 @@ public class TableFunctionImpl extends ReflectiveFunctionBase implements
             }
             return expr;
           }
-        }, NullPolicy.ANY, false);
+        }, NullPolicy.NONE, false);
   }
 
   private Table apply(List<Object> arguments) {
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 37b3e0b..1bff997 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
@@ -4693,6 +4693,12 @@ public abstract class SqlOperatorBaseTest {
   }
 
   @Test void testJsonValue() {
+    if (false) {
+      tester.checkFails("json_value('{\"foo\":100}', 'lax $.foo1' error on empty)",
+          "(?s).*Empty result of JSON_VALUE function is not allowed.*",
+          true);
+    }
+
     // type casting test
     tester.checkString("json_value('{\"foo\":100}', 'strict $.foo')",
         "100", "VARCHAR(2000)");
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index fa5b501..89e9f91 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -2493,15 +2493,23 @@ public class JdbcTest {
   }
 
   @Test void testReuseExpressionWhenNullChecking() {
+    final String sql = "select upper((case when \"empid\">\"deptno\"*10"
+        + " then 'y' else null end)) T\n"
+        + "from \"hr\".\"emps\"";
+    final String plan = ""
+        + "      String case_when_value;\n"
+        + "              final org.apache.calcite.test.JdbcTest.Employee current = (org.apache"
+        + ".calcite.test.JdbcTest.Employee) inputEnumerator.current();\n"
+        + "              if (current.empid > current.deptno * 10) {\n"
+        + "                case_when_value = \"y\";\n"
+        + "              } else {\n"
+        + "                case_when_value = (String) null;\n"
+        + "              }\n"
+        + "              return case_when_value == null ? (String) null : org.apache.calcite"
+        + ".runtime.SqlFunctions.upper(case_when_value);";
     CalciteAssert.hr()
-        .query(
-            "select upper((case when \"empid\">\"deptno\"*10 then 'y' else null end)) T from \"hr\".\"emps\"")
-        .planContains("static final String "
-            + "$L4J$C$org_apache_calcite_runtime_SqlFunctions_upper_y_ = "
-            + "org.apache.calcite.runtime.SqlFunctions.upper(\"y\");")
-        .planContains("return current.empid <= current.deptno * 10 "
-            + "? (String) null "
-            + ": $L4J$C$org_apache_calcite_runtime_SqlFunctions_upper_y_;")
+        .query(sql)
+        .planContains(plan)
         .returns("T=null\n"
             + "T=null\n"
             + "T=Y\n"
@@ -2509,15 +2517,23 @@ public class JdbcTest {
   }
 
   @Test void testReuseExpressionWhenNullChecking2() {
+    final String sql = "select upper((case when \"empid\">\"deptno\"*10"
+        + " then \"name\" end)) T\n"
+        + "from \"hr\".\"emps\"";
+    final String plan = ""
+        + "      String case_when_value;\n"
+        + "              final org.apache.calcite.test.JdbcTest.Employee current = (org.apache"
+        + ".calcite.test.JdbcTest.Employee) inputEnumerator.current();\n"
+        + "              if (current.empid > current.deptno * 10) {\n"
+        + "                case_when_value = current.name;\n"
+        + "              } else {\n"
+        + "                case_when_value = (String) null;\n"
+        + "              }\n"
+        + "              return case_when_value == null ? (String) null : org.apache.calcite"
+        + ".runtime.SqlFunctions.upper(case_when_value);";
     CalciteAssert.hr()
-        .query(
-            "select upper((case when \"empid\">\"deptno\"*10 then \"name\" end)) T from \"hr\".\"emps\"")
-        .planContains(
-            "final String inp2_ = current.name;")
-        .planContains("return current.empid <= current.deptno * 10 "
-            + "|| inp2_ == null "
-            + "? (String) null "
-            + ": org.apache.calcite.runtime.SqlFunctions.upper(inp2_);")
+        .query(sql)
+        .planContains(plan)
         .returns("T=null\n"
             + "T=null\n"
             + "T=SEBASTIAN\n"
@@ -2525,52 +2541,89 @@ public class JdbcTest {
   }
 
   @Test void testReuseExpressionWhenNullChecking3() {
+    final String sql = "select substring(\"name\",\n"
+        + " \"deptno\"+case when CURRENT_PATH <> '' then 1 end)\n"
+        + "from \"hr\".\"emps\"";
+    final String plan = ""
+        + "              final org.apache.calcite.test.JdbcTest.Employee current"
+        + " = (org.apache.calcite.test.JdbcTest.Employee) inputEnumerator.current();\n"
+        + "              final String input_value = current.name;\n"
+        + "              Integer case_when_value;\n"
+        + "              if ($L4J$C$org_apache_calcite_runtime_SqlFunctions_ne_) {\n"
+        + "                case_when_value = $L4J$C$Integer_valueOf_1_;\n"
+        + "              } else {\n"
+        + "                case_when_value = (Integer) null;\n"
+        + "              }\n"
+        + "              final Integer binary_call_value0 = "
+        + "case_when_value == null ? (Integer) null : "
+        + "Integer.valueOf(current.deptno + case_when_value.intValue());\n"
+        + "              return input_value == null || binary_call_value0 == null"
+        + " ? (String) null"
+        + " : org.apache.calcite.runtime.SqlFunctions.substring(input_value, "
+        + "binary_call_value0.intValue());\n";
     CalciteAssert.hr()
-        .query(
-            "select substring(\"name\", \"deptno\"+case when CURRENT_PATH <> '' then 1 end) from \"hr\".\"emps\"")
-        .planContains(
-            "final String inp2_ = current.name;")
-        .planContains("static final boolean "
-            + "$L4J$C$org_apache_calcite_runtime_SqlFunctions_ne_ = "
-            + "org.apache.calcite.runtime.SqlFunctions.ne(\"\", \"\");")
-        .planContains("static final boolean "
-            + "$L4J$C$_org_apache_calcite_runtime_SqlFunctions_ne_ = "
-            + "!$L4J$C$org_apache_calcite_runtime_SqlFunctions_ne_;")
-        .planContains("return inp2_ == null "
-            + "|| $L4J$C$_org_apache_calcite_runtime_SqlFunctions_ne_ ? (String) null"
-            + " : org.apache.calcite.runtime.SqlFunctions.substring(inp2_, "
-            + "Integer.valueOf(current.deptno + 1).intValue());");
+        .query(sql)
+        .planContains(plan);
   }
 
   @Test void testReuseExpressionWhenNullChecking4() {
+    final String sql = "select substring(trim(\n"
+        + "substring(\"name\",\n"
+        + "  \"deptno\"*0+case when CURRENT_PATH = '' then 1 end)\n"
+        + "), case when \"empid\">\"deptno\" then 4\n" /* diff from 5 */
+        + "   else\n"
+        + "     case when \"deptno\"*8>8 then 5 end\n"
+        + "   end-2) T\n"
+        + "from\n"
+        + "\"hr\".\"emps\"";
+    final String plan = ""
+        + "              final org.apache.calcite.test.JdbcTest.Employee current ="
+        + " (org.apache.calcite.test.JdbcTest.Employee) inputEnumerator.current();\n"
+        + "              final String input_value = current.name;\n"
+        + "              final int input_value0 = current.deptno;\n"
+        + "              Integer case_when_value;\n"
+        + "              if ($L4J$C$org_apache_calcite_runtime_SqlFunctions_eq_) {\n"
+        + "                case_when_value = $L4J$C$Integer_valueOf_1_;\n"
+        + "              } else {\n"
+        + "                case_when_value = (Integer) null;\n"
+        + "              }\n"
+        + "              final Integer binary_call_value1 = "
+        + "case_when_value == null"
+        + " ? (Integer) null"
+        + " : Integer.valueOf(input_value0 * 0 + case_when_value.intValue());\n"
+        + "              final String method_call_value = "
+        + "input_value == null || binary_call_value1 == null"
+        + " ? (String) null"
+        + " : org.apache.calcite.runtime.SqlFunctions.substring(input_value, "
+        + "binary_call_value1.intValue());\n"
+        + "              final String trim_value = "
+        + "method_call_value == null"
+        + " ? (String) null"
+        + " : org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", "
+        + "method_call_value, true);\n"
+        + "              Integer case_when_value0;\n"
+        + "              if (current.empid > input_value0) {\n"
+        + "                case_when_value0 = $L4J$C$Integer_valueOf_4_;\n"
+        + "              } else {\n"
+        + "                Integer case_when_value1;\n"
+        + "                if (current.deptno * 8 > 8) {\n"
+        + "                  case_when_value1 = $L4J$C$Integer_valueOf_5_;\n"
+        + "                } else {\n"
+        + "                  case_when_value1 = (Integer) null;\n"
+        + "                }\n"
+        + "                case_when_value0 = case_when_value1;\n"
+        + "              }\n"
+        + "              final Integer binary_call_value3 = "
+        + "case_when_value0 == null"
+        + " ? (Integer) null"
+        + " : Integer.valueOf(case_when_value0.intValue() - 2);\n"
+        + "              return trim_value == null || binary_call_value3 == null"
+        + " ? (String) null"
+        + " : org.apache.calcite.runtime.SqlFunctions.substring(trim_value, "
+        + "binary_call_value3.intValue());\n";
     CalciteAssert.hr()
-        .query("select substring(trim(\n"
-            + "substring(\"name\",\n"
-            + "  \"deptno\"*0+case when CURRENT_PATH = '' then 1 end)\n"
-            + "), case when \"empid\">\"deptno\" then 4\n" /* diff from 5 */
-            + "   else\n"
-            + "     case when \"deptno\"*8>8 then 5 end\n"
-            + "   end-2) T\n"
-            + "from\n"
-            + "\"hr\".\"emps\"")
-        .planContains(
-            "final String inp2_ = current.name;")
-        .planContains(
-            "final int inp1_ = current.deptno;")
-        .planContains("static final boolean "
-            + "$L4J$C$org_apache_calcite_runtime_SqlFunctions_eq_ = "
-            + "org.apache.calcite.runtime.SqlFunctions.eq(\"\", \"\");")
-        .planContains("static final boolean "
-            + "$L4J$C$_org_apache_calcite_runtime_SqlFunctions_eq_ = "
-            + "!$L4J$C$org_apache_calcite_runtime_SqlFunctions_eq_;")
-        .planContains("return inp2_ == null "
-            + "|| $L4J$C$_org_apache_calcite_runtime_SqlFunctions_eq_ "
-            + "|| !v5 && inp1_ * 8 <= 8 "
-            + "? (String) null "
-            + ": org.apache.calcite.runtime.SqlFunctions.substring("
-            + "org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", "
-            + "org.apache.calcite.runtime.SqlFunctions.substring(inp2_, "
-            + "Integer.valueOf(inp1_ * 0 + 1).intValue()), true), Integer.valueOf((v5 ? 4 : 5) - 2).intValue());")
+        .query(sql)
+        .planContains(plan)
         .returns("T=ill\n"
             + "T=ric\n"
             + "T=ebastian\n"
@@ -2578,46 +2631,71 @@ public class JdbcTest {
   }
 
   @Test void testReuseExpressionWhenNullChecking5() {
+    final String sql = "select substring(trim(\n"
+        + "substring(\"name\",\n"
+        + "  \"deptno\"*0+case when CURRENT_PATH = '' then 1 end)\n"
+        + "), case when \"empid\">\"deptno\" then 5\n" /* diff from 4 */
+        + "   else\n"
+        + "     case when \"deptno\"*8>8 then 5 end\n"
+        + "   end-2) T\n"
+        + "from\n"
+        + "\"hr\".\"emps\"";
+    final String plan = ""
+        + "              final org.apache.calcite.test.JdbcTest.Employee current ="
+        + " (org.apache.calcite.test.JdbcTest.Employee) inputEnumerator.current();\n"
+        + "              final String input_value = current.name;\n"
+        + "              final int input_value0 = current.deptno;\n"
+        + "              Integer case_when_value;\n"
+        + "              if ($L4J$C$org_apache_calcite_runtime_SqlFunctions_eq_) {\n"
+        + "                case_when_value = $L4J$C$Integer_valueOf_1_;\n"
+        + "              } else {\n"
+        + "                case_when_value = (Integer) null;\n"
+        + "              }\n"
+        + "              final Integer binary_call_value1 = "
+        + "case_when_value == null"
+        + " ? (Integer) null"
+        + " : Integer.valueOf(input_value0 * 0 + case_when_value.intValue());\n"
+        + "              final String method_call_value = "
+        + "input_value == null || binary_call_value1 == null"
+        + " ? (String) null"
+        + " : org.apache.calcite.runtime.SqlFunctions.substring(input_value, "
+        + "binary_call_value1.intValue());\n"
+        + "              final String trim_value = "
+        + "method_call_value == null"
+        + " ? (String) null"
+        + " : org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", "
+        + "method_call_value, true);\n"
+        + "              Integer case_when_value0;\n"
+        + "              if (current.empid > input_value0) {\n"
+        + "                case_when_value0 = $L4J$C$Integer_valueOf_5_;\n"
+        + "              } else {\n"
+        + "                Integer case_when_value1;\n"
+        + "                if (current.deptno * 8 > 8) {\n"
+        + "                  case_when_value1 = $L4J$C$Integer_valueOf_5_;\n"
+        + "                } else {\n"
+        + "                  case_when_value1 = (Integer) null;\n"
+        + "                }\n"
+        + "                case_when_value0 = case_when_value1;\n"
+        + "              }\n"
+        + "              final Integer binary_call_value3 = "
+        + "case_when_value0 == null"
+        + " ? (Integer) null"
+        + " : Integer.valueOf(case_when_value0.intValue() - 2);\n"
+        + "              return trim_value == null || binary_call_value3 == null"
+        + " ? (String) null"
+        + " : org.apache.calcite.runtime.SqlFunctions.substring(trim_value, "
+        + "binary_call_value3.intValue());";
     CalciteAssert.hr()
-        .query("select substring(trim(\n"
-            + "substring(\"name\",\n"
-            + "  \"deptno\"*0+case when CURRENT_PATH = '' then 1 end)\n"
-            + "), case when \"empid\">\"deptno\" then 5\n" /* diff from 4 */
-            + "   else\n"
-            + "     case when \"deptno\"*8>8 then 5 end\n"
-            + "   end-2) T\n"
-            + "from\n"
-            + "\"hr\".\"emps\"")
-        .planContains(
-            "final String inp2_ = current.name;")
-        .planContains(
-            "final int inp1_ = current.deptno;")
-        .planContains(
-            "static final int $L4J$C$5_2 = 5 - 2;")
-        .planContains(
-            "static final Integer $L4J$C$Integer_valueOf_5_2_ = Integer.valueOf($L4J$C$5_2);")
-        .planContains(
-            "static final int $L4J$C$Integer_valueOf_5_2_intValue_ = $L4J$C$Integer_valueOf_5_2_.intValue();")
-        .planContains("static final boolean "
-            + "$L4J$C$org_apache_calcite_runtime_SqlFunctions_eq_ = "
-            + "org.apache.calcite.runtime.SqlFunctions.eq(\"\", \"\");")
-        .planContains("static final boolean "
-            + "$L4J$C$_org_apache_calcite_runtime_SqlFunctions_eq_ = "
-            + "!$L4J$C$org_apache_calcite_runtime_SqlFunctions_eq_;")
-        .planContains("return inp2_ == null "
-            + "|| $L4J$C$_org_apache_calcite_runtime_SqlFunctions_eq_ "
-            + "|| current.empid <= inp1_ && inp1_ * 8 <= 8 "
-            + "? (String) null "
-            + ": org.apache.calcite.runtime.SqlFunctions.substring("
-            + "org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", "
-            + "org.apache.calcite.runtime.SqlFunctions.substring(inp2_, "
-            + "Integer.valueOf(inp1_ * 0 + 1).intValue()), true), $L4J$C$Integer_valueOf_5_2_intValue_);")
+        .query(sql)
+        .planContains(plan)
         .returns("T=ll\n"
             + "T=ic\n"
             + "T=bastian\n"
             + "T=eodore\n");
   }
 
+
+
   @Test void testValues() {
     CalciteAssert.that()
         .query("values (1), (2)")
@@ -3663,11 +3741,16 @@ public class JdbcTest {
             + "        a1w0,\n"
             + "        a2w0,\n"
             + "        a3w0});")
+        .planContains("      Float case_when_value;\n"
+            + "              if (org.apache.calcite.runtime.SqlFunctions.toLong(current[4]) > 0L) {\n"
+            + "                case_when_value = Float.valueOf(org.apache.calcite.runtime.SqlFunctions.toFloat(current[5]));\n"
+            + "              } else {\n"
+            + "                case_when_value = (Float) null;\n"
+            + "              }")
         .planContains("return new Object[] {\n"
             + "                  current[1],\n"
             + "                  current[0],\n"
-            // Float.valueOf(SqlFunctions.toFloat(current[5])) comes from SUM0
-            + "                  org.apache.calcite.runtime.SqlFunctions.toLong(current[4]) > 0L ? Float.valueOf(org.apache.calcite.runtime.SqlFunctions.toFloat(current[5])) : (Float) null,\n"
+            + "                  case_when_value,\n"
             + "                  5,\n"
             + "                  current[6],\n"
             + "                  current[7]};\n");
@@ -4310,9 +4393,9 @@ public class JdbcTest {
   @Test void testMethodParameterTypeMatch() {
     CalciteAssert.that()
         .query("SELECT mod(12.5, cast(3 as bigint))")
-        .planContains("final java.math.BigDecimal v = "
+        .planContains("final java.math.BigDecimal literal_value = "
             + "$L4J$C$new_java_math_BigDecimal_12_5_")
-        .planContains("org.apache.calcite.runtime.SqlFunctions.mod(v, "
+        .planContains("org.apache.calcite.runtime.SqlFunctions.mod(literal_value, "
             + "$L4J$C$new_java_math_BigDecimal_3L_)")
         .returns("EXPR$0=0.5\n");
   }
@@ -6194,8 +6277,6 @@ public class JdbcTest {
             + "  select cast(null as timestamp) as time0,"
             + "         cast(null as timestamp) as time1"
             + ") calcs")
-        .planContains("org.apache.calcite.runtime.SqlFunctions.eq(inp0_, inp1_)")
-        .planContains("org.apache.calcite.runtime.SqlFunctions.ne(inp0_, inp1_)")
         .returns("EXPR$0=true; EXPR$1=false\n"
             + "EXPR$0=null; EXPR$1=null\n");
   }
@@ -7223,7 +7304,7 @@ public class JdbcTest {
             + "new java.math.BigDecimal(\n"
             + "              1)")
         .planContains("org.apache.calcite.runtime.GeoFunctions.ST_MakePoint("
-            + "$L4J$C$new_java_math_BigDecimal_1_, v)")
+            + "$L4J$C$new_java_math_BigDecimal_1_, literal_value0)")
         .returns("EXPR$0={\"x\":1,\"y\":2.1}\n");
   }
 
diff --git a/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java
index 58019b4..8e45d23 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializedViewRelOptRulesTest.java
@@ -821,7 +821,8 @@ public class MaterializedViewRelOptRulesTest extends AbstractMaterializedViewTes
         .withChecker(
             resultContains(""
             + "EnumerableCalc(expr#0=[{inputs}], expr#1=[CAST($t0):JavaType(int) NOT NULL], "
-            + "expr#2=[1], expr#3=[=($t2, $t0)], EXPR$0=[$t1], $condition=[$t3])\n"
+            + "expr#2=[1], expr#3=[CAST($t1):INTEGER NOT NULL], expr#4=[=($t2, $t3)], "
+            + "EXPR$0=[$t1], $condition=[$t4])\n"
             + "  EnumerableTableScan(table=[[hr, MV0]])"))
         .ok();
   }
diff --git a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
index cc5578d..f0540f7 100644
--- a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
+++ b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
@@ -585,10 +585,9 @@ public class ReflectiveSchemaTest {
     with.query("select \"wrapperLong\" / \"primitiveLong\" as c\n"
         + " from \"s\".\"everyTypes\" where \"primitiveLong\" <> 0")
         .planContains(
-            "final Long inp13_ = current.wrapperLong;")
+            "final Long input_value = current.wrapperLong;")
         .planContains(
-            "return inp13_ == null ? (Long) null "
-                + ": Long.valueOf(inp13_.longValue() / current.primitiveLong);")
+            "return input_value == null ? (Long) null : Long.valueOf(input_value.longValue() / current.primitiveLong);")
         .returns("C=null\n");
   }
 
@@ -606,10 +605,9 @@ public class ReflectiveSchemaTest {
     with.query("select \"wrapperLong\" / \"wrapperLong\" as c\n"
         + " from \"s\".\"everyTypes\" where \"primitiveLong\" <> 0")
         .planContains(
-            "final Long inp13_ = ((org.apache.calcite.test.ReflectiveSchemaTest.EveryType) inputEnumerator.current()).wrapperLong;")
+            "final Long input_value = ((org.apache.calcite.test.ReflectiveSchemaTest.EveryType) inputEnumerator.current()).wrapperLong;")
         .planContains(
-            "return inp13_ == null ? (Long) null "
-                + ": Long.valueOf(inp13_.longValue() / inp13_.longValue());")
+            "return input_value == null ? (Long) null : Long.valueOf(input_value.longValue() / input_value.longValue());")
         .returns("C=null\n");
   }
 
@@ -620,11 +618,11 @@ public class ReflectiveSchemaTest {
         + "+ \"wrapperLong\" / \"wrapperLong\" as c\n"
         + " from \"s\".\"everyTypes\" where \"primitiveLong\" <> 0")
         .planContains(
-            "final Long inp13_ = ((org.apache.calcite.test.ReflectiveSchemaTest.EveryType) inputEnumerator.current()).wrapperLong;")
+            "final Long input_value = ((org.apache.calcite.test.ReflectiveSchemaTest.EveryType) inputEnumerator.current()).wrapperLong;")
         .planContains(
-            "return inp13_ == null ? (Long) null "
-                + ": Long.valueOf(Long.valueOf(inp13_.longValue() / inp13_.longValue()).longValue() "
-                + "+ Long.valueOf(inp13_.longValue() / inp13_.longValue()).longValue());")
+            "final Long binary_call_value = input_value == null ? (Long) null : Long.valueOf(input_value.longValue() / input_value.longValue());")
+        .planContains(
+            "return binary_call_value == null ? (Long) null : Long.valueOf(binary_call_value.longValue() + binary_call_value.longValue());")
         .returns("C=null\n");
   }
 
diff --git a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCalcTest.java b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCalcTest.java
index ef2d726..de35291 100644
--- a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCalcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableCalcTest.java
@@ -47,7 +47,7 @@ class EnumerableCalcTest {
                     builder.field("commission"),
                     builder.literal(0)))
                 .build())
-        .planContains("inp4_ != null ? inp4_.intValue() : 0;")
+        .planContains("input_value != null ? input_value : 0")
         .returnsUnordered(
             "$f0=0",
             "$f0=250",
diff --git a/core/src/test/resources/sql/misc.iq b/core/src/test/resources/sql/misc.iq
index dfd07e4..d30fd56 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -2247,4 +2247,56 @@ EXPR$0, EXPR$1
 2019-12-31, 2019-12-31
 !ok
 
+# [CALCITE-3142] An NPE when rounding a nullable numeric
+SELECT ROUND(CAST((X/Y) AS NUMERIC), 2)
+FROM (VALUES (1, 2), (NULLIF(5, 5), NULLIF(5, 5))) A(X, Y);
+EXPR$0
+0.00
+null
+!ok
+
+# [CALCITE-3143]Dividing NULLIF clause may cause Division by zero error
+SELECT CASE WHEN "Z" < 77 AND "Z" > 0 THEN 99 ELSE 88 END
+FROM (
+  SELECT SUM("X") / NULLIF(SUM(0),0) AS Z
+  FROM (VALUES (1.1, 2.5), (4.51, 32.5)) A(X, Y)
+  GROUP BY "Y");
+EXPR$0
+88
+88
+!ok
+
+# [CALCITE-3150] NPE in UPPER when repeated and combine with LIKE
+SELECT "NAME"
+FROM (VALUES ('Bill'), NULLIF('x', 'x'), ('Eric')) A(NAME)
+WHERE UPPER("NAME") LIKE 'B%' AND UPPER("NAME") LIKE '%L';
+NAME
+Bill
+!ok
+
+# [CALCITE-3717] Query fails with "division by zero" exception
+SELECT
+  CASE WHEN A=0 THEN (B+C+D)*1.0
+  WHEN B=0 THEN 1.0/A+(C+D)*1.0
+  WHEN C=0 THEN 1.0/A+1.0/B+D*1.0
+  WHEN D=0 THEN 1.0/A+1.0/B+1.0/C
+  ELSE 1.0/A+1.0/B+1.0/C+1.0/D
+  END AS V
+FROM (VALUES (0, 2, 4, 8),
+   (1, 0, 4, 8),
+   (1, 2, 0, 8),
+   (1, 2, 4, 0),
+   (0, 0, 0, 0),
+   (1, 2, 4, 8),
+   (CAST(null as int), CAST(null as int), CAST(null as int), CAST(null as int))) AS T(A,B,C,D);
+V
+14.0
+13.0
+9.5
+1.75
+0.0
+1.875
+null
+!ok
+
 # End misc.iq


[calcite] 03/03: [CALCITE-4090] When generating SQL for DB2, a complex SELECT above a sub-query generates a bad table alias (Steven Talbot)

Posted by jh...@apache.org.
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 7e557390a08a583e07faf51ee2c8e03829b57b06
Author: Steven Talbot <st...@looker.com>
AuthorDate: Wed Jun 24 16:57:33 2020 -0700

    [CALCITE-4090] When generating SQL for DB2, a complex SELECT above a sub-query generates a bad table alias (Steven Talbot)
    
    Close apache/calcite#2045
---
 .../apache/calcite/rel/rel2sql/SqlImplementor.java |  5 ++-
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 51 ++++++++++++++++++++++
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 2396c3f..8d2739f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -1359,6 +1359,7 @@ public abstract class SqlImplementor {
       }
       clauseList.appendAll(clauses);
       final Context newContext;
+      Map<String, RelDataType> newAliases = null;
       final SqlNodeList selectList = select.getSelectList();
       if (selectList != null) {
         newContext = new Context(dialect, selectList.size()) {
@@ -1408,7 +1409,7 @@ public abstract class SqlImplementor {
         if (needNew
             && neededAlias != null
             && (aliases.size() != 1 || !aliases.containsKey(neededAlias))) {
-          final Map<String, RelDataType> newAliases =
+          newAliases =
               ImmutableMap.of(neededAlias, rel.getInput(0).getRowType());
           newContext = aliasContext(newAliases, qualified);
         } else {
@@ -1416,7 +1417,7 @@ public abstract class SqlImplementor {
         }
       }
       return new Builder(rel, clauseList, select, newContext, isAnon(),
-          needNew ? null : aliases);
+          needNew && !aliases.containsKey(neededAlias) ? newAliases : aliases);
     }
 
     /** Returns whether a new sub-query is required. */
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index d8681db..b134a16 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -2514,6 +2514,57 @@ class RelToSqlConverterTest {
     sql(query).withDb2().ok(expected);
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-4090">[CALCITE-4090]
+   * DB2 aliasing breaks with a complex SELECT above a sub-query</a>. */
+  @Test void testDb2SubQueryAlias() {
+    String query = "select count(foo), \"units_per_case\"\n"
+        + "from (select \"units_per_case\", \"cases_per_pallet\",\n"
+        + "      \"product_id\", 1 as foo\n"
+        + "  from \"product\")\n"
+        + "where \"cases_per_pallet\" > 100\n"
+        + "group by \"product_id\", \"units_per_case\"\n"
+        + "order by \"units_per_case\" desc";
+    final String expected = "SELECT COUNT(*), t.units_per_case\n"
+        + "FROM (SELECT product.units_per_case, product.cases_per_pallet, "
+        + "product.product_id, 1 AS FOO\n"
+        + "FROM foodmart.product AS product) AS t\n"
+        + "WHERE t.cases_per_pallet > 100\n"
+        + "GROUP BY t.product_id, t.units_per_case\n"
+        + "ORDER BY t.units_per_case DESC";
+    sql(query).withDb2().ok(expected);
+  }
+
+  @Test void testDb2SubQueryFromUnion() {
+    String query = "select count(foo), \"units_per_case\"\n"
+        + "from (select \"units_per_case\", \"cases_per_pallet\",\n"
+        + "      \"product_id\", 1 as foo\n"
+        + "  from \"product\"\n"
+        + "  where \"cases_per_pallet\" > 100\n"
+        + "  union all\n"
+        + "  select \"units_per_case\", \"cases_per_pallet\",\n"
+        + "      \"product_id\", 1 as foo\n"
+        + "  from \"product\"\n"
+        + "  where \"cases_per_pallet\" < 100)\n"
+        + "where \"cases_per_pallet\" > 100\n"
+        + "group by \"product_id\", \"units_per_case\"\n"
+        + "order by \"units_per_case\" desc";
+    final String expected = "SELECT COUNT(*), t3.units_per_case\n"
+        + "FROM (SELECT product.units_per_case, product.cases_per_pallet, "
+        + "product.product_id, 1 AS FOO\n"
+        + "FROM foodmart.product AS product\n"
+        + "WHERE product.cases_per_pallet > 100\n"
+        + "UNION ALL\n"
+        + "SELECT product0.units_per_case, product0.cases_per_pallet, "
+        + "product0.product_id, 1 AS FOO\n"
+        + "FROM foodmart.product AS product0\n"
+        + "WHERE product0.cases_per_pallet < 100) AS t3\n"
+        + "WHERE t3.cases_per_pallet > 100\n"
+        + "GROUP BY t3.product_id, t3.units_per_case\n"
+        + "ORDER BY t3.units_per_case DESC";
+    sql(query).withDb2().ok(expected);
+  }
+
   @Test void testDb2DialectSelectQueryWithGroup() {
     String query = "select count(*), sum(\"employee_id\") "
         + "from \"reserve_employee\" "


[calcite] 02/03: [CALCITE-4089] In Babel, allow 'CAST(integer AS DATE)' even though it is illegal in Calcite SQL

Posted by jh...@apache.org.
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 d839a57fc35a4bd46d1ef4d03e67e22db2e802d7
Author: Julian Hyde <jh...@apache.org>
AuthorDate: Thu Jun 25 09:34:42 2020 -0700

    [CALCITE-4089] In Babel, allow 'CAST(integer AS DATE)' even though it is illegal in Calcite SQL
    
    Add BIG_QUERY (abbreviation 'b', and 'fun=bigquery' in
    connect string) as a function library.
    
    Add functions: DATE, DATE_FROM_UNIX_DATE, TIMESTAMP_MICROS,
    TIMESTAMP_MILLIS, TIMESTAMP_SECONDS, UNIX_DATE, UNIX_MICROS,
    UNIX_MILLIS, UNIX_SECONDS.
---
 .../org/apache/calcite/test/BabelQuidemTest.java   |  10 ++
 babel/src/test/resources/sql/big-query.iq          | 137 +++++++++++++++++++++
 .../calcite/adapter/enumerable/EnumUtils.java      |   8 +-
 .../calcite/adapter/enumerable/RexImpTable.java    |  18 +++
 .../calcite/config/CalciteConnectionProperty.java  |   4 +-
 .../org/apache/calcite/runtime/SqlFunctions.java   |  49 +++++++-
 .../org/apache/calcite/sql/fun/SqlLibrary.java     |  20 +--
 .../calcite/sql/fun/SqlLibraryOperators.java       |  72 +++++++++++
 .../org/apache/calcite/sql/type/OperandTypes.java  |   6 +
 .../org/apache/calcite/sql/type/ReturnTypes.java   |  28 ++++-
 .../calcite/sql/test/SqlOperatorBaseTest.java      |  36 +++++-
 site/_docs/reference.md                            |  14 ++-
 12 files changed, 379 insertions(+), 23 deletions(-)

diff --git a/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java b/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java
index 1a83439..506ad0e 100644
--- a/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java
+++ b/babel/src/test/java/org/apache/calcite/test/BabelQuidemTest.java
@@ -101,6 +101,16 @@ class BabelQuidemTest extends QuidemTest {
                   SqlConformanceEnum.BABEL)
               .with(CalciteConnectionProperty.LENIENT_OPERATOR_LOOKUP, true)
               .connect();
+        case "scott-big-query":
+          return CalciteAssert.that()
+              .with(CalciteAssert.Config.SCOTT)
+              .with(CalciteConnectionProperty.FUN, "standard,bigquery")
+              .with(CalciteConnectionProperty.PARSER_FACTORY,
+                  SqlBabelParserImpl.class.getName() + "#FACTORY")
+              .with(CalciteConnectionProperty.CONFORMANCE,
+                  SqlConformanceEnum.BABEL)
+              .with(CalciteConnectionProperty.LENIENT_OPERATOR_LOOKUP, true)
+              .connect();
         default:
           return super.connect(name, reference);
         }
diff --git a/babel/src/test/resources/sql/big-query.iq b/babel/src/test/resources/sql/big-query.iq
new file mode 100755
index 0000000..6792fb6
--- /dev/null
+++ b/babel/src/test/resources/sql/big-query.iq
@@ -0,0 +1,137 @@
+# big-query.iq - Babel test for BigQuery dialect of SQL
+#
+# 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.
+#
+!use scott-big-query
+!set outputformat csv
+
+# TIMESTAMP_SECONDS, TIMESTAMP_MILLIS, TIMESTAMP_MICROS
+select v,
+  timestamp_seconds(v) as t0,
+  timestamp_millis(v * 1000) as t1,
+  timestamp_micros(v * 1000 * 1000) as t2
+from (values cast(0 as bigint),
+   cast(null as bigint),
+   cast(1230219000 as bigint),
+   cast(-1230219000 as bigint)) as t (v)
+order by v;
+V, T0, T1, T2
+-1230219000, 1931-01-07 08:30:00, 1931-01-07 08:30:00, 1931-01-07 08:30:00
+0, 1970-01-01 00:00:00, 1970-01-01 00:00:00, 1970-01-01 00:00:00
+1230219000, 2008-12-25 15:30:00, 2008-12-25 15:30:00, 2008-12-25 15:30:00
+null, null, null, null
+!ok
+
+select timestamp_seconds(1234567890) as t;
+T
+2009-02-13 23:31:30
+!ok
+
+select timestamp_millis(1234567890) as t;
+T
+1970-01-15 06:56:07
+!ok
+
+select timestamp_micros(1234567890) as t;
+T
+1970-01-01 00:20:34
+!ok
+
+# UNIX_SECONDS, UNIX_MILLIS, UNIX_MICROS
+select v,
+  unix_seconds(v) as t0,
+  unix_millis(v) as t1,
+  unix_micros(v) as t2
+from (values TIMESTAMP '1970-01-01 00:00:00',
+   cast(null as timestamp),
+   TIMESTAMP '2008-12-25 15:30:00',
+   TIMESTAMP '1931-01-07 08:30:00') as t (v)
+order by v;
+V, T0, T1, T2
+1931-01-07 08:30:00, -1230219000, -1230219000000, -1230219000000000
+1970-01-01 00:00:00, 0, 0, 0
+2008-12-25 15:30:00, 1230219000, 1230219000000, 1230219000000000
+null, null, null, null
+!ok
+
+select unix_seconds(timestamp '2008-12-25 15:30:00') as t;
+T
+1230219000
+!ok
+
+select unix_millis(timestamp '2008-12-25 15:30:00') as t;
+T
+1230219000000
+!ok
+
+select unix_micros(timestamp '2008-12-25 15:30:00') as t;
+T
+1230219000000000
+!ok
+
+# DATE_FROM_UNIX_DATE
+select v,
+  date_from_unix_date(v) as d
+from (values 0,
+   cast(null as integer),
+   1230219000 / 86400,
+   -1230219000 / 86400) as t (v)
+order by v;
+V, D
+-14238, 1931-01-08
+0, 1970-01-01
+14238, 2008-12-25
+null, null
+!ok
+
+select date_from_unix_date(14238);
+EXPR$0
+2008-12-25
+!ok
+
+# UNIX_DATE
+select v,
+  unix_date(v) as d
+from (values date '1970-01-01',
+   cast(null as date),
+   DATE '2008-12-25',
+   DATE '1931-01-07') as t (v)
+order by v;
+V, D
+1931-01-07, -14239
+1970-01-01, 0
+2008-12-25, 14238
+null, null
+!ok
+
+select unix_date(timestamp '2008-12-25');
+EXPR$0
+14238
+!ok
+
+# DATE
+# 'date(x) is shorthand for 'cast(x as date)'
+select date('1970-01-01') as d;
+D
+1970-01-01
+!ok
+
+select date(cast(null as varchar(10))) as d;
+D
+null
+!ok
+
+# End big-query.iq
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java
index edf544b..cd192e1 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java
@@ -292,12 +292,16 @@ public class EnumUtils {
   }
 
   private static Type toInternal(RelDataType type) {
+    return toInternal(type, false);
+  }
+
+  static Type toInternal(RelDataType type, boolean forceNotNull) {
     switch (type.getSqlTypeName()) {
     case DATE:
     case TIME:
-      return type.isNullable() ? Integer.class : int.class;
+      return type.isNullable() && !forceNotNull ? Integer.class : int.class;
     case TIMESTAMP:
-      return type.isNullable() ? Long.class : long.class;
+      return type.isNullable() && !forceNotNull ? Long.class : long.class;
     default:
       return null; // we don't care; use the default storage type
     }
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index e0457e9..e315aa8 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -106,6 +106,8 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.COMPRESS;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT2;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_FUNCTION;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.COSH;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATE;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.DATE_FROM_UNIX_DATE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.DAYNAME;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.DIFFERENCE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.EXISTS_NODE;
@@ -132,8 +134,15 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.SOUNDEX;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.SPACE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.STRCMP;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TANH;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIMESTAMP_MICROS;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIMESTAMP_MILLIS;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIMESTAMP_SECONDS;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TO_BASE64;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TRANSLATE3;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.UNIX_DATE;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.UNIX_MICROS;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.UNIX_MILLIS;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.UNIX_SECONDS;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.XML_TRANSFORM;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ABS;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ACOS;
@@ -430,6 +439,14 @@ public class RexImpTable {
         new PeriodNameImplementor("monthName",
             BuiltInMethod.MONTHNAME_WITH_TIMESTAMP,
             BuiltInMethod.MONTHNAME_WITH_DATE));
+    defineMethod(TIMESTAMP_SECONDS, "timestampSeconds", NullPolicy.STRICT);
+    defineMethod(TIMESTAMP_MILLIS, "timestampMillis", NullPolicy.STRICT);
+    defineMethod(TIMESTAMP_MICROS, "timestampMicros", NullPolicy.STRICT);
+    defineMethod(UNIX_SECONDS, "unixSeconds", NullPolicy.STRICT);
+    defineMethod(UNIX_MILLIS, "unixMillis", NullPolicy.STRICT);
+    defineMethod(UNIX_MICROS, "unixMicros", NullPolicy.STRICT);
+    defineMethod(DATE_FROM_UNIX_DATE, "dateFromUnixDate", NullPolicy.STRICT);
+    defineMethod(UNIX_DATE, "unixDate", NullPolicy.STRICT);
 
     map.put(IS_NULL, new IsNullImplementor());
     map.put(IS_NOT_NULL, new IsNotNullImplementor());
@@ -498,6 +515,7 @@ public class RexImpTable {
 
     map.put(COALESCE, new CoalesceImplementor());
     map.put(CAST, new CastImplementor());
+    map.put(DATE, new CastImplementor());
 
     map.put(REINTERPRET, new ReinterpretImplementor());
 
diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
index f09b052..adb9833 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
@@ -78,8 +78,8 @@ public enum CalciteConnectionProperty implements ConnectionProperty {
   LEX("lex", Type.ENUM, Lex.ORACLE, false),
 
   /** Collection of built-in functions and operators. Valid values include
-   * "standard", "mysql", "oracle", "postgresql" and "spatial", and also
-   * comma-separated lists, for example "oracle,spatial". */
+   * "standard", "bigquery", "mysql", "oracle", "postgresql" and "spatial",
+   * and also comma-separated lists, for example "oracle,spatial". */
   FUN("fun", Type.STRING, "standard", true),
 
   /** How identifiers are quoted.
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 18c9312..248990e 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -33,6 +33,7 @@ import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.function.NonDeterministic;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.runtime.FlatLists.ComparableList;
+import org.apache.calcite.sql.fun.SqlLibraryOperators;
 import org.apache.calcite.util.NumberUtil;
 import org.apache.calcite.util.TimeWithTimeZoneString;
 import org.apache.calcite.util.TimestampWithTimeZoneString;
@@ -1774,7 +1775,7 @@ public class SqlFunctions {
 
   @NonDeterministic
   private static Object cannotConvert(Object o, Class toType) {
-    throw RESOURCE.cannotConvert(o.toString(), toType.toString()).ex();
+    throw RESOURCE.cannotConvert(String.valueOf(o), toType.toString()).ex();
   }
 
   /** CAST(VARCHAR AS BOOLEAN). */
@@ -2099,6 +2100,52 @@ public class SqlFunctions {
         .getMillisOfDay();
   }
 
+  /** For {@link SqlLibraryOperators#TIMESTAMP_SECONDS}. */
+  public static long timestampSeconds(long v) {
+    return v * 1000;
+  }
+
+  /** For {@link SqlLibraryOperators#TIMESTAMP_MILLIS}. */
+  public static long timestampMillis(long v) {
+    // translation is trivial, because Calcite represents TIMESTAMP values as
+    // millis since epoch
+    return v;
+  }
+
+  /** For {@link SqlLibraryOperators#TIMESTAMP_MICROS}. */
+  public static long timestampMicros(long v) {
+    return v / 1000;
+  }
+
+  /** For {@link SqlLibraryOperators#UNIX_SECONDS}. */
+  public static long unixSeconds(long v) {
+    return v / 1000;
+  }
+
+  /** For {@link SqlLibraryOperators#UNIX_MILLIS}. */
+  public static long unixMillis(long v) {
+    // translation is trivial, because Calcite represents TIMESTAMP values as
+    // millis since epoch
+    return v;
+  }
+
+  /** For {@link SqlLibraryOperators#UNIX_MICROS}. */
+  public static long unixMicros(long v) {
+    return v * 1000;
+  }
+
+  /** For {@link SqlLibraryOperators#DATE_FROM_UNIX_DATE}. */
+  public static int dateFromUnixDate(int v) {
+    // translation is trivial, because Calcite represents dates as Unix integers
+    return v;
+  }
+
+  /** For {@link SqlLibraryOperators#UNIX_DATE}. */
+  public static int unixDate(int v) {
+    // translation is trivial, because Calcite represents dates as Unix integers
+    return v;
+  }
+
   public static Long toTimestampWithLocalTimeZone(String v) {
     return v == null ? null : new TimestampWithTimeZoneString(v)
         .withTimeZone(DateTimeUtils.UTC_ZONE)
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java
index 9722977..10e9d66 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibrary.java
@@ -18,6 +18,7 @@ package org.apache.calcite.sql.fun;
 
 import org.apache.calcite.config.CalciteConnectionProperty;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -41,16 +42,19 @@ import java.util.Objects;
  */
 public enum SqlLibrary {
   /** The standard operators. */
-  STANDARD(""),
+  STANDARD("", "standard"),
   /** Geospatial operators. */
-  SPATIAL("s"),
+  SPATIAL("s", "spatial"),
+  /** A collection of operators that are in Google BigQuery but not in standard
+   * SQL. */
+  BIG_QUERY("b", "bigquery"),
   /** A collection of operators that are in MySQL but not in standard SQL. */
-  MYSQL("m"),
+  MYSQL("m", "mysql"),
   /** A collection of operators that are in Oracle but not in standard SQL. */
-  ORACLE("o"),
+  ORACLE("o", "oracle"),
   /** A collection of operators that are in PostgreSQL but not in standard
    * SQL. */
-  POSTGRESQL("p");
+  POSTGRESQL("p", "postgresql");
 
   /** Abbreviation for the library used in SQL reference. */
   public final String abbrev;
@@ -59,9 +63,11 @@ public enum SqlLibrary {
    * see {@link CalciteConnectionProperty#FUN}. */
   public final String fun;
 
-  SqlLibrary(String abbrev) {
+  SqlLibrary(String abbrev, String fun) {
     this.abbrev = Objects.requireNonNull(abbrev);
-    this.fun = name().toLowerCase(Locale.ROOT);
+    this.fun = Objects.requireNonNull(fun);
+    Preconditions.checkArgument(
+        fun.equals(name().toLowerCase(Locale.ROOT).replace("_", "")));
   }
 
   /** Looks up a value.
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 26589d2..f23c884 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -33,6 +33,7 @@ import org.apache.calcite.sql.type.SqlTypeTransforms;
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.apache.calcite.sql.fun.SqlLibrary.BIG_QUERY;
 import static org.apache.calcite.sql.fun.SqlLibrary.MYSQL;
 import static org.apache.calcite.sql.fun.SqlLibrary.ORACLE;
 import static org.apache.calcite.sql.fun.SqlLibrary.POSTGRESQL;
@@ -202,6 +203,29 @@ public abstract class SqlLibraryOperators {
       ReturnTypes.cascade(ReturnTypes.INTEGER_NULLABLE, SqlTypeTransforms.FORCE_NULLABLE),
       null, OperandTypes.STRING_STRING_OPTIONAL_STRING, SqlFunctionCategory.SYSTEM);
 
+  /** The "DATE(string)" function, equivalent to "CAST(string AS DATE). */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction DATE =
+      new SqlFunction("DATE", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.DATE_NULLABLE, null, OperandTypes.STRING,
+          SqlFunctionCategory.TIMEDATE);
+
+  /** The "DATE_FROM_UNIX_DATE(integer)" function; returns a DATE value
+   * a given number of seconds after 1970-01-01. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction DATE_FROM_UNIX_DATE =
+      new SqlFunction("DATE_FROM_UNIX_DATE", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.DATE_NULLABLE, null, OperandTypes.INTEGER,
+          SqlFunctionCategory.TIMEDATE);
+
+  /** The "UNIX_DATE(date)" function; returns the number of days since
+   * 1970-01-01. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction UNIX_DATE =
+      new SqlFunction("UNIX_DATE", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.INTEGER_NULLABLE, null, OperandTypes.DATE,
+          SqlFunctionCategory.TIMEDATE);
+
   /** The "MONTHNAME(datetime)" function; returns the name of the month,
    * in the current locale, of a TIMESTAMP or DATE argument. */
   @LibraryOperator(libraries = {MYSQL})
@@ -350,6 +374,54 @@ public abstract class SqlLibraryOperators {
           OperandTypes.STRING_STRING,
           SqlFunctionCategory.TIMEDATE);
 
+  /** The "TIMESTAMP_SECONDS(bigint)" function; returns a TIMESTAMP value
+   * a given number of seconds after 1970-01-01 00:00:00. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction TIMESTAMP_SECONDS =
+      new SqlFunction("TIMESTAMP_SECONDS", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.TIMESTAMP_NULLABLE, null, OperandTypes.INTEGER,
+          SqlFunctionCategory.TIMEDATE);
+
+  /** The "TIMESTAMP_MILLIS(bigint)" function; returns a TIMESTAMP value
+   * a given number of milliseconds after 1970-01-01 00:00:00. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction TIMESTAMP_MILLIS =
+      new SqlFunction("TIMESTAMP_MILLIS", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.TIMESTAMP_NULLABLE, null, OperandTypes.INTEGER,
+          SqlFunctionCategory.TIMEDATE);
+
+  /** The "TIMESTAMP_MICROS(bigint)" function; returns a TIMESTAMP value
+   * a given number of micro-seconds after 1970-01-01 00:00:00. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction TIMESTAMP_MICROS =
+      new SqlFunction("TIMESTAMP_MICROS", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.TIMESTAMP_NULLABLE, null, OperandTypes.INTEGER,
+          SqlFunctionCategory.TIMEDATE);
+
+  /** The "UNIX_SECONDS(bigint)" function; returns the number of seconds
+   * since 1970-01-01 00:00:00. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction UNIX_SECONDS =
+      new SqlFunction("UNIX_SECONDS", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.BIGINT_NULLABLE, null, OperandTypes.TIMESTAMP,
+          SqlFunctionCategory.TIMEDATE);
+
+  /** The "UNIX_MILLIS(bigint)" function; returns the number of milliseconds
+   * since 1970-01-01 00:00:00. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction UNIX_MILLIS =
+      new SqlFunction("UNIX_MILLIS", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.BIGINT_NULLABLE, null, OperandTypes.TIMESTAMP,
+          SqlFunctionCategory.TIMEDATE);
+
+  /** The "UNIX_MICROS(bigint)" function; returns the number of microseconds
+   * since 1970-01-01 00:00:00. */
+  @LibraryOperator(libraries = {BIG_QUERY})
+  public static final SqlFunction UNIX_MICROS =
+      new SqlFunction("UNIX_MICROS", SqlKind.OTHER_FUNCTION,
+          ReturnTypes.BIGINT_NULLABLE, null, OperandTypes.TIMESTAMP,
+          SqlFunctionCategory.TIMEDATE);
+
   @LibraryOperator(libraries = {ORACLE})
   public static final SqlFunction CHR =
       new SqlFunction("CHR",
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index eddba18..b601aa3 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -242,6 +242,12 @@ public abstract class OperandTypes {
   public static final SqlSingleOperandTypeChecker DATETIME =
       family(SqlTypeFamily.DATETIME);
 
+  public static final SqlSingleOperandTypeChecker DATE =
+      family(SqlTypeFamily.DATE);
+
+  public static final SqlSingleOperandTypeChecker TIMESTAMP =
+      family(SqlTypeFamily.TIMESTAMP);
+
   public static final SqlSingleOperandTypeChecker INTERVAL =
       family(SqlTypeFamily.DATETIME_INTERVAL);
 
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index 4d5a905..1a31917 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -210,35 +210,51 @@ public abstract class ReturnTypes {
       cascade(BOOLEAN, SqlTypeTransforms.FORCE_NULLABLE);
 
   /**
-   * Type-inference strategy whereby the result type of a call is Boolean
-   * not null.
+   * Type-inference strategy whereby the result type of a call is BOOLEAN
+   * NOT NULL.
    */
   public static final SqlReturnTypeInference BOOLEAN_NOT_NULL =
       cascade(BOOLEAN, SqlTypeTransforms.TO_NOT_NULLABLE);
+
   /**
-   * Type-inference strategy whereby the result type of a call is Date.
+   * Type-inference strategy whereby the result type of a call is DATE.
    */
   public static final SqlReturnTypeInference DATE =
       explicit(SqlTypeName.DATE);
 
   /**
    * Type-inference strategy whereby the result type of a call is nullable
-   * Date.
+   * DATE.
    */
   public static final SqlReturnTypeInference DATE_NULLABLE =
       cascade(DATE, SqlTypeTransforms.TO_NULLABLE);
 
   /**
-   * Type-inference strategy whereby the result type of a call is Time(0).
+   * Type-inference strategy whereby the result type of a call is TIME(0).
    */
   public static final SqlReturnTypeInference TIME =
       explicit(SqlTypeName.TIME, 0);
+
   /**
    * Type-inference strategy whereby the result type of a call is nullable
-   * Time(0).
+   * TIME(0).
    */
   public static final SqlReturnTypeInference TIME_NULLABLE =
       cascade(TIME, SqlTypeTransforms.TO_NULLABLE);
+
+  /**
+   * Type-inference strategy whereby the result type of a call is TIMESTAMP.
+   */
+  public static final SqlReturnTypeInference TIMESTAMP =
+      explicit(SqlTypeName.TIMESTAMP);
+
+  /**
+   * Type-inference strategy whereby the result type of a call is nullable
+   * TIMESTAMP.
+   */
+  public static final SqlReturnTypeInference TIMESTAMP_NULLABLE =
+      cascade(TIMESTAMP, SqlTypeTransforms.TO_NULLABLE);
+
   /**
    * Type-inference strategy whereby the result type of a call is Double.
    */
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 1bff997..18a3313 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
@@ -324,15 +324,23 @@ public abstract class SqlOperatorBaseTest {
     tester.setFor(null);
   }
 
-  protected SqlTester oracleTester() {
+  private SqlTester oracleTester() {
+    return libraryTester(SqlLibrary.ORACLE);
+  }
+
+  private SqlTester bigQueryTester() {
+    return libraryTester(SqlLibrary.BIG_QUERY);
+  }
+
+  protected SqlTester libraryTester(SqlLibrary library) {
     return tester.withOperatorTable(
             SqlLibraryOperatorTableFactory.INSTANCE
-                .getOperatorTable(SqlLibrary.STANDARD, SqlLibrary.ORACLE))
+                .getOperatorTable(SqlLibrary.STANDARD, library))
         .withConnectionFactory(
             CalciteAssert.EMPTY_CONNECTION_FACTORY
                 .with(new CalciteAssert
                     .AddSchemaSpecPostProcessor(CalciteAssert.SchemaSpec.HR))
-                .with(CalciteConnectionProperty.FUN, "oracle"));
+                .with(CalciteConnectionProperty.FUN, library.fun));
   }
 
   protected SqlTester oracleTester(SqlConformance conformance) {
@@ -5599,6 +5607,28 @@ public abstract class SqlOperatorBaseTest {
     tester.checkScalar("rand_integer(2, 11)", 1, "INTEGER NOT NULL");
   }
 
+  /** Tests {@code UNIX_SECONDS} and other datetime functions from BigQuery. */
+  @Test void testUnixSecondsFunc() {
+    SqlTester tester = bigQueryTester();
+    tester.setFor(SqlLibraryOperators.UNIX_SECONDS);
+    tester.checkScalar("unix_seconds(timestamp '1970-01-01 00:00:00')", 0,
+        "BIGINT NOT NULL");
+    tester.checkNull("unix_seconds(cast(null as timestamp))");
+    tester.checkNull("unix_millis(cast(null as timestamp))");
+    tester.checkNull("unix_micros(cast(null as timestamp))");
+    tester.checkScalar("timestamp_seconds(0)", "1970-01-01 00:00:00",
+        "TIMESTAMP(0) NOT NULL");
+    tester.checkNull("timestamp_seconds(cast(null as bigint))");
+    tester.checkNull("timestamp_millis(cast(null as bigint))");
+    tester.checkNull("timestamp_micros(cast(null as bigint))");
+    tester.checkScalar("date_from_unix_date(0)", "1970-01-01", "DATE NOT NULL");
+
+    // Have to quote the "DATE" function because we're not using the Babel
+    // parser. In the regular parser, DATE is a reserved keyword.
+    tester.checkNull("\"DATE\"(null)");
+    tester.checkScalar("\"DATE\"('1985-12-06')", "1985-12-06", "DATE NOT NULL");
+  }
+
   @Test void testAbsFunc() {
     tester.setFor(SqlStdOperatorTable.ABS);
     tester.checkScalarExact("abs(-1)", "1");
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 7fd2930..5682677 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2340,6 +2340,7 @@ To enable an operator table, set the
 connect string parameter.
 
 The 'C' (compatibility) column contains value
+'b' for BigQuery ('fun=bigquery' in the connect string),
 'm' for MySQL ('fun=mysql' in the connect string),
 'o' for Oracle ('fun=oracle' in the connect string),
 'p' for PostgreSQL ('fun=postgresql' in the connect string).
@@ -2357,6 +2358,8 @@ semantics.
 | m | COMPRESS(string)                               | Compresses a string using zlib compression and returns the result as a binary string.
 | p | CONVERT_TIMEZONE(tz1, tz2, datetime)           | Converts the timezone of *datetime* from *tz1* to *tz2*
 | m | DAYNAME(datetime)                              | Returns the name, in the connection's locale, of the weekday in *datetime*; for example, it returns '星期日' for both DATE '2020-02-10' and TIMESTAMP '2020-02-10 10:10:10'
+| b | DATE(string)                                   | Equivalent to `CAST(string AS DATE)`
+| b | DATE_FROM_UNIX_DATE(integer)                   | Returns the DATE that is *integer* days after 1970-01-01
 | o | DECODE(value, value1, result1 [, valueN, resultN ]* [, default ]) | Compares *value* to each *valueN* value one by one; if *value* is equal to a *valueN*, returns the corresponding *resultN*, else returns *default*, or NULL if *default* is not specified
 | p | DIFFERENCE(string, string)                     | Returns a measure of the similarity of two strings, namely the number of character positions that their `SOUNDEX` values have in common: 4 if the `SOUNDEX` values are same and 0 if the `SOUNDEX` values are totally different
 | o | EXTRACT(xml, xpath, [, namespaces ])           | Returns the xml fragment of the element or elements matched by the XPath expression. The optional namespace value that specifies a default mapping or namespace mapping for prefixes, which is used when evaluating the XPath expression
@@ -2388,12 +2391,19 @@ semantics.
 | m o p | SOUNDEX(string)                            | Returns the phonetic representation of *string*; throws if *string* is encoded with multi-byte encoding such as UTF-8
 | m | SPACE(integer)                                 | Returns a string of *integer* spaces; returns an empty string if *integer* is less than 1
 | o | SUBSTR(string, position [, substringLength ]) | Returns a portion of *string*, beginning at character *position*, *substringLength* characters long. SUBSTR calculates lengths using characters as defined by the input character set
-| m | STRCMP(string, string)                         | Returns 0 if both of the strings are same and returns -1 when the first argument is smaller than the second and 1 when the second one is smaller the first one.
+| m | STRCMP(string, string)                         | Returns 0 if both of the strings are same and returns -1 when the first argument is smaller than the second and 1 when the second one is smaller than the first one
 | o | TANH(numeric)                                  | Returns the hyperbolic tangent of *numeric*
+| b | TIMESTAMP_MICROS(integer)                      | Returns the TIMESTAMP that is *integer* microseconds after 1970-01-01 00:00:00
+| b | TIMESTAMP_MILLIS(integer)                      | Returns the TIMESTAMP that is *integer* milliseconds after 1970-01-01 00:00:00
+| b | TIMESTAMP_SECONDS(integer)                     | Returns the TIMESTAMP that is *integer* seconds after 1970-01-01 00:00:00
 | o p | TO_DATE(string, format)                      | Converts *string* to a date using the format *format*
 | o p | TO_TIMESTAMP(string, format)                 | Converts *string* to a timestamp using the format *format*
 | o p | TRANSLATE(expr, fromString, toString)        | Returns *expr* with all occurrences of each character in *fromString* replaced by its corresponding character in *toString*. Characters in *expr* that are not in *fromString* are not replaced
-| o | XMLTRANSFORM(xml, xslt)                        | Returns a string after applying xslt to supplied xml.
+| b | UNIX_MICROS(timestamp)                         | Returns the number of microseconds since 1970-01-01 00:00:00
+| b | UNIX_MILLIS(timestamp)                         | Returns the number of milliseconds since 1970-01-01 00:00:00
+| b | UNIX_SECONDS(timestamp)                        | Returns the number of seconds since 1970-01-01 00:00:00
+| b | UNIX_DATE(date)                                | Returns the number of days since 1970-01-01
+| o | XMLTRANSFORM(xml, xslt)                        | Returns a string after applying xslt to supplied XML
 
 Note: