You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by sn...@apache.org on 2026/06/22 15:52:47 UTC

(flink) branch master updated (9bc156b7faf -> 316d926c34e)

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

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


    from 9bc156b7faf [FLINK-39909][state] Fix heap state backend savepoint NPE with null map state values
     new d2ddbe0f18b [FLINK-39817][table] Upgrade Calcite to 1.39.0
     new 0e852ba33e1 [FLINK-39817][table] Do not use deprecated `SqlPrettyWriter` constructor
     new 160aba5fd4a [FLINK-39817][table] Replace deprecated `LogicalProject.create`
     new 316d926c34e [FLINK-39817][table] Replace deprecated `JavaConversions` in `TemporalJoinUtil`

The 4 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:
 flink-table/flink-sql-parser/pom.xml               |    8 +-
 .../src/main/codegen/data/Parser.tdd               |    4 +
 .../src/main/codegen/templates/Parser.jj           |  109 +-
 .../org/apache/calcite/sql/type/SqlTypeFamily.java |  292 --
 .../org/apache/calcite/sql/type/SqlTypeName.java   | 1078 -----
 .../sql/parser/validate/FlinkSqlConformance.java   |   15 +
 .../sql/parser/ExtendedSqlRowTypeNameSpecTest.java |   17 +-
 .../flink/sql/parser/FlinkDDLDataTypeTest.java     |    6 +-
 flink-table/flink-table-calcite-bridge/pom.xml     |    8 +-
 .../apache/calcite/jdbc/SimpleCalciteSchema.java   |   56 +-
 .../java/org/apache/calcite/plan/RelOptUtil.java   | 4596 --------------------
 .../calcite/rel/rules/SubQueryRemoveRule.java      |  115 +-
 .../calcite/rel/type/RelDataTypeFactoryImpl.java   |   18 +-
 .../java/org/apache/calcite/rex/RexBuilder.java    |   47 +-
 .../java/org/apache/calcite/rex/RexChecker.java    |  203 -
 .../org/apache/calcite/rex/RexFieldAccess.java     |  141 -
 .../java/org/apache/calcite/rex/RexProgram.java    |  984 -----
 .../java/org/apache/calcite/rex/RexShuttle.java    |  284 --
 .../main/java/org/apache/calcite/rex/RexUtil.java  |   18 +-
 .../org/apache/calcite/runtime/SqlFunctions.java   |  384 +-
 .../apache/calcite/sql/SqlIntervalQualifier.java   |   56 +-
 .../main/java/org/apache/calcite/sql/SqlUtil.java  |    8 +-
 .../calcite/sql/fun/SqlStdOperatorTable.java       |  127 +-
 .../org/apache/calcite/sql/type/BasicSqlType.java  |    2 +-
 .../calcite/sql/type/SqlTypeAssignmentRule.java    |    4 +-
 .../calcite/sql/type/SqlTypeFactoryImpl.java       |   70 +-
 .../org/apache/calcite/sql/type/SqlTypeUtil.java   |   86 +-
 .../calcite/sql/validate/SqlValidatorImpl.java     |  438 +-
 .../org/apache/calcite/sql2rel/AggConverter.java   |    4 +-
 .../apache/calcite/sql2rel/RelDecorrelator.java    |  492 ++-
 .../apache/calcite/sql2rel/SqlToRelConverter.java  |  520 ++-
 .../planner/calcite/FlinkCalciteSqlValidator.java  |    8 +
 .../plan/rules/logical/WindowGroupReorderRule.java |    5 +-
 .../src/main/resources/META-INF/NOTICE             |    6 +-
 .../planner/plan/utils/TemporalJoinUtil.scala      |   18 +-
 .../table/planner/plan/batch/sql/CalcTest.xml      |    2 +-
 .../planner/plan/batch/sql/DeadlockBreakupTest.xml |    2 +-
 .../plan/batch/sql/DynamicFunctionPlanTest.xml     |    9 +-
 .../planner/plan/batch/sql/SetOperatorsTest.xml    |    4 +-
 .../ReplaceIntersectWithSemiJoinRuleTest.xml       |    2 +-
 .../logical/ReplaceMinusWithAntiJoinRuleTest.xml   |    2 +-
 .../rules/logical/WindowGroupReorderRuleTest.xml   |    2 +-
 .../logical/subquery/SubQueryAntiJoinTest.xml      |  180 +
 .../logical/subquery/SubQuerySemiJoinTest.xml      |  109 +
 .../PushCalcPastChangelogNormalizeRuleTest.xml     |   18 +-
 .../table/planner/plan/stream/sql/CalcTest.xml     |    2 +-
 .../planner/plan/stream/sql/SetOperatorsTest.xml   |    4 +-
 .../table/planner/plan/stream/table/ValuesTest.xml |    2 +-
 .../planner/expressions/SqlExpressionTest.scala    |    1 +
 .../logical/subquery/SubQueryAntiJoinTest.scala    |   16 +-
 .../logical/subquery/SubQuerySemiJoinTest.scala    |   12 +-
 flink-table/pom.xml                                |    2 +-
 52 files changed, 2088 insertions(+), 8508 deletions(-)
 delete mode 100644 flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
 delete mode 100644 flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
 delete mode 100644 flink-table/flink-table-planner/src/main/java/org/apache/calcite/plan/RelOptUtil.java
 delete mode 100644 flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexChecker.java
 delete mode 100644 flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexFieldAccess.java
 delete mode 100644 flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexProgram.java
 delete mode 100644 flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexShuttle.java


(flink) 01/04: [FLINK-39817][table] Upgrade Calcite to 1.39.0

Posted by sn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git

commit d2ddbe0f18b9f87f1cba9f2de12c373ca3b0e82e
Author: Sergey Nuyanzin <sn...@gmail.com>
AuthorDate: Thu May 28 15:54:22 2026 +0200

    [FLINK-39817][table] Upgrade Calcite to 1.39.0
    
    This closes #28290.
---
 flink-table/flink-sql-parser/pom.xml               |    8 +-
 .../src/main/codegen/data/Parser.tdd               |    4 +
 .../src/main/codegen/templates/Parser.jj           |  109 +-
 .../org/apache/calcite/sql/type/SqlTypeFamily.java |  292 --
 .../org/apache/calcite/sql/type/SqlTypeName.java   | 1078 -----
 .../sql/parser/validate/FlinkSqlConformance.java   |   15 +
 flink-table/flink-table-calcite-bridge/pom.xml     |    8 +-
 .../apache/calcite/jdbc/SimpleCalciteSchema.java   |   56 +-
 .../java/org/apache/calcite/plan/RelOptUtil.java   | 4596 --------------------
 .../calcite/rel/rules/SubQueryRemoveRule.java      |  115 +-
 .../calcite/rel/type/RelDataTypeFactoryImpl.java   |   18 +-
 .../java/org/apache/calcite/rex/RexBuilder.java    |   47 +-
 .../java/org/apache/calcite/rex/RexChecker.java    |  203 -
 .../org/apache/calcite/rex/RexFieldAccess.java     |  141 -
 .../java/org/apache/calcite/rex/RexProgram.java    |  984 -----
 .../java/org/apache/calcite/rex/RexShuttle.java    |  284 --
 .../main/java/org/apache/calcite/rex/RexUtil.java  |   18 +-
 .../org/apache/calcite/runtime/SqlFunctions.java   |  384 +-
 .../apache/calcite/sql/SqlIntervalQualifier.java   |   56 +-
 .../main/java/org/apache/calcite/sql/SqlUtil.java  |    8 +-
 .../calcite/sql/fun/SqlStdOperatorTable.java       |  127 +-
 .../org/apache/calcite/sql/type/BasicSqlType.java  |    2 +-
 .../calcite/sql/type/SqlTypeAssignmentRule.java    |    4 +-
 .../calcite/sql/type/SqlTypeFactoryImpl.java       |   70 +-
 .../org/apache/calcite/sql/type/SqlTypeUtil.java   |   86 +-
 .../calcite/sql/validate/SqlValidatorImpl.java     |  438 +-
 .../org/apache/calcite/sql2rel/AggConverter.java   |    4 +-
 .../apache/calcite/sql2rel/RelDecorrelator.java    |  492 ++-
 .../apache/calcite/sql2rel/SqlToRelConverter.java  |  520 ++-
 .../planner/calcite/FlinkCalciteSqlValidator.java  |    8 +
 .../src/main/resources/META-INF/NOTICE             |    6 +-
 .../planner/plan/utils/TemporalJoinUtil.scala      |    3 -
 .../table/planner/plan/batch/sql/CalcTest.xml      |    2 +-
 .../planner/plan/batch/sql/DeadlockBreakupTest.xml |    2 +-
 .../plan/batch/sql/DynamicFunctionPlanTest.xml     |    9 +-
 .../planner/plan/batch/sql/SetOperatorsTest.xml    |    4 +-
 .../ReplaceIntersectWithSemiJoinRuleTest.xml       |    2 +-
 .../logical/ReplaceMinusWithAntiJoinRuleTest.xml   |    2 +-
 .../rules/logical/WindowGroupReorderRuleTest.xml   |    2 +-
 .../logical/subquery/SubQueryAntiJoinTest.xml      |  180 +
 .../logical/subquery/SubQuerySemiJoinTest.xml      |  109 +
 .../PushCalcPastChangelogNormalizeRuleTest.xml     |   18 +-
 .../table/planner/plan/stream/sql/CalcTest.xml     |    2 +-
 .../planner/plan/stream/sql/SetOperatorsTest.xml   |    4 +-
 .../table/planner/plan/stream/table/ValuesTest.xml |    2 +-
 .../planner/expressions/SqlExpressionTest.scala    |    1 +
 .../logical/subquery/SubQueryAntiJoinTest.scala    |   16 +-
 .../logical/subquery/SubQuerySemiJoinTest.scala    |   12 +-
 flink-table/pom.xml                                |    2 +-
 49 files changed, 2063 insertions(+), 8490 deletions(-)

diff --git a/flink-table/flink-sql-parser/pom.xml b/flink-table/flink-sql-parser/pom.xml
index 0264583bfec..8be0c191ffb 100644
--- a/flink-table/flink-sql-parser/pom.xml
+++ b/flink-table/flink-sql-parser/pom.xml
@@ -68,11 +68,11 @@ under the License.
 			<version>${calcite.version}</version>
 			<exclusions>
 				<!--
-				"mvn dependency:tree" as of Calcite 1.38.0:
+				"mvn dependency:tree" as of Calcite 1.39.0:
 
-				[INFO] +- org.apache.calcite:calcite-core:jar:1.38.0:compile
-				[INFO] |  +- org.apache.calcite:calcite-linq4j:jar:1.38.0:compile
-				[INFO] |  +- org.apache.calcite.avatica:avatica-core:jar:1.25.0:compile
+				[INFO] +- org.apache.calcite:calcite-core:jar:1.39.0:compile
+				[INFO] |  +- org.apache.calcite:calcite-linq4j:jar:1.39.0:compile
+				[INFO] |  +- org.apache.calcite.avatica:avatica-core:jar:1.26.0:compile
 				[INFO] |  +- org.apiguardian:apiguardian-api:jar:1.1.2:compile
 				[INFO] |  +- org.checkerframework:checker-qual:jar:3.43.0:compile
 				[INFO] |  \- org.apache.commons:commons-math3:jar:3.6.1:runtime
diff --git a/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd b/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd
index 74339361d79..84b69813a52 100644
--- a/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd
+++ b/flink-table/flink-sql-parser/src/main/codegen/data/Parser.tdd
@@ -633,6 +633,7 @@
     "OVERWRITING"
     "PARTITIONED"
     "PARTITIONS"
+    "UUID"
     "VIRTUAL"
     "ANALYZE"
     "COMPUTE"
@@ -772,6 +773,9 @@
   joinTypes: [
   ]
 
+  # Method for parsing "SET [OR RESET]" calls.
+  setOptionParserMethod: "SqlSetOption"
+
   includePosixOperators: false
   includeCompoundIdentifier: true
   includeBraces: true
diff --git a/flink-table/flink-sql-parser/src/main/codegen/templates/Parser.jj b/flink-table/flink-sql-parser/src/main/codegen/templates/Parser.jj
index 2aa24e5f215..15d4882c71e 100644
--- a/flink-table/flink-sql-parser/src/main/codegen/templates/Parser.jj
+++ b/flink-table/flink-sql-parser/src/main/codegen/templates/Parser.jj
@@ -1159,7 +1159,7 @@ SqlNode SqlStmt() :
         LOOKAHEAD(2) stmt = ${method}
     |
 </#list>
-        stmt = SqlSetOption(Span.of(), null)
+        stmt = ${parser.setOptionParserMethod!default.parser.setOptionParserMethod}(Span.of(), null)
     |
         stmt = SqlAlter()
     |
@@ -4398,7 +4398,7 @@ SqlSetOption SqlSetOption(Span s, String scope) :
             }
         )
         {
-            return new SqlSetOption(s.end(val), scope, name, val);
+            return new SqlSetOption(s.end(val), scope, (SqlNode) name, val);
         }
     |
         <RESET> {
@@ -4413,7 +4413,7 @@ SqlSetOption SqlSetOption(Span s, String scope) :
             }
         )
         {
-            return new SqlSetOption(s.end(name), scope, name, null);
+            return new SqlSetOption(s.end(name), scope, (SqlNode) name, null);
         }
     )
 }
@@ -4437,8 +4437,7 @@ SqlAlter SqlAlter() :
         alterNode = ${method}(s, scope)
     |
 </#list>
-
-        alterNode = SqlSetOption(s, scope)
+        alterNode = ${parser.setOptionParserMethod!default.parser.setOptionParserMethod}(s, scope)
     )
     {
         return alterNode;
@@ -4891,6 +4890,11 @@ SqlLiteral DateTimeLiteral() :
     <TIME> { s = span(); } p = SimpleStringLiteral() {
       return SqlLiteral.createUnknown("TIME", p, s.end(this));
     }
+|
+    LOOKAHEAD(2)
+    <UUID> { s = span(); } p = SimpleStringLiteral() {
+        return SqlLiteral.createUnknown("UUID", p, s.end(this));
+    }
 |
     LOOKAHEAD(2)
     <TIMESTAMP> { s = span(); } p = SimpleStringLiteral() {
@@ -5927,6 +5931,8 @@ SqlTypeNameSpec SqlTypeName1(Span s) :
         <FLOAT> { s.add(this); sqlTypeName = SqlTypeName.FLOAT; }
     |
         <VARIANT> { s.add(this); sqlTypeName = SqlTypeName.VARIANT; }
+    |
+        <UUID> { s.add(this); sqlTypeName = SqlTypeName.UUID; }
     )
     {
         return new SqlBasicTypeNameSpec(sqlTypeName, s.end(this));
@@ -6348,10 +6354,17 @@ SqlNode BuiltinFunctionCall() :
                 }
             |
                 <COMMA> e = SimpleIdentifier() { args.add(e); }
-                <COMMA> e = SimpleIdentifier() { args.add(e); }
-                <RPAREN> {
-                    return SqlStdOperatorTable.CONVERT.createCall(s.end(this), args);
-                }
+                (
+                    <COMMA> e = SimpleIdentifier() { args.add(e); }
+                    <RPAREN> {
+                        SqlOperator op = SqlStdOperatorTable.getConvertFuncByConformance(this.conformance);
+                        return op.createCall(s.end(this), args);
+                    }
+                |
+                    <RPAREN> {
+                        return SqlLibraryOperators.CONVERT_ORACLE.createCall(s.end(this), args);
+                    }
+                )
             )
         |
             // MSSql CONVERT(type, val [,style])
@@ -6428,59 +6441,56 @@ SqlNode BuiltinFunctionCall() :
         <TRIM> {
             SqlLiteral flag = null;
             SqlNode trimChars = null;
+            SqlParserPos fromPos = SqlParserPos.ZERO;
             s = span();
         }
         <LPAREN>
-        [
-            LOOKAHEAD(2)
-            [
-                <BOTH> {
-                    s.add(this);
-                    flag = SqlTrimFunction.Flag.BOTH.symbol(getPos());
-                }
-            |
-                <TRAILING> {
-                    s.add(this);
-                    flag = SqlTrimFunction.Flag.TRAILING.symbol(getPos());
-                }
-            |
-                <LEADING> {
-                    s.add(this);
-                    flag = SqlTrimFunction.Flag.LEADING.symbol(getPos());
-                }
-            ]
-            [ trimChars = Expression(ExprContext.ACCEPT_SUB_QUERY) ]
+        (
             (
-                <FROM> {
-                    if (null == flag && null == trimChars) {
-                        throw SqlUtil.newContextException(getPos(),
-                            RESOURCE.illegalFromEmpty());
+                (
+                    <BOTH> {
+                        s.add(this);
+                        flag = SqlTrimFunction.Flag.BOTH.symbol(getPos());
                     }
-                }
-            |
-                <RPAREN> {
-                    // This is to handle the case of TRIM(x)
-                    // (FRG-191).
-                    if (flag == null) {
-                        flag = SqlTrimFunction.Flag.BOTH.symbol(SqlParserPos.ZERO);
+                |
+                    <TRAILING> {
+                        s.add(this);
+                        flag = SqlTrimFunction.Flag.TRAILING.symbol(getPos());
                     }
-                    args.add(flag);
-                    args.add(null); // no trim chars
-                    args.add(trimChars); // reinterpret trimChars as source
-                    return SqlStdOperatorTable.TRIM.createCall(s.end(this),
-                        args);
-                }
+                |
+                    <LEADING> {
+                        s.add(this);
+                        flag = SqlTrimFunction.Flag.LEADING.symbol(getPos());
+                    }
+                )
+                (
+                    <FROM> { fromPos = getPos() ;}
+                    e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+                |
+                    e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+                    [
+                        <FROM> { trimChars = e; fromPos = getPos(); }
+                        e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+                    ]
+                )
             )
-        ]
-        e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
+            |
+            (
+                e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+                [
+                    <FROM> { trimChars = e; fromPos = getPos(); }
+                    e = Expression(ExprContext.ACCEPT_SUB_QUERY)
+                ]
+            )
+        )
+        <RPAREN> {
             if (flag == null) {
                 flag = SqlTrimFunction.Flag.BOTH.symbol(SqlParserPos.ZERO);
             }
             args.add(flag);
+            // trimChars can be null
             args.add(trimChars);
             args.add(e);
-        }
-        <RPAREN> {
             return SqlStdOperatorTable.TRIM.createCall(s.end(this), args);
         }
     |
@@ -8735,6 +8745,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < UTF8: "UTF8" >
 |   < UTF16: "UTF16" >
 |   < UTF32: "UTF32" >
+|   < UUID: "UUID" >
 |   < VALUE: "VALUE" >
 |   < VALUES: "VALUES" > { afterTableName(); }
 |   < VALUE_OF: "VALUE_OF" >
diff --git a/flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java b/flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
deleted file mode 100644
index b755fc4d6f4..00000000000
--- a/flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.sql.type;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import org.apache.calcite.avatica.util.TimeUnit;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelDataTypeFamily;
-import org.apache.calcite.sql.SqlIntervalQualifier;
-import org.apache.calcite.sql.SqlWindow;
-import org.apache.calcite.sql.parser.SqlParserPos;
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-import java.sql.Types;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * SqlTypeFamily provides SQL type categorization.
- *
- * <p>The <em>primary</em> family categorization is a complete disjoint partitioning of SQL types
- * into families, where two types are members of the same primary family iff instances of the two
- * types can be the operands of an SQL equality predicate such as <code>WHERE v1 = v2</code>.
- * Primary families are returned by RelDataType.getFamily().
- *
- * <p>There is also a <em>secondary</em> family categorization which overlaps with the primary
- * categorization. It is used in type strategies for more specific or more general categorization
- * than the primary families. Secondary families are never returned by RelDataType.getFamily().
- *
- * <p>This class was copied over from Calcite to support variant type(CALCITE-4918). When upgrading
- * to Calcite 1.39.0 version, please remove the entire class.
- */
-public enum SqlTypeFamily implements RelDataTypeFamily {
-    // Primary families.
-    CHARACTER,
-    BINARY,
-    NUMERIC,
-    DATE,
-    TIME,
-    TIMESTAMP,
-    BOOLEAN,
-    INTERVAL_YEAR_MONTH,
-    INTERVAL_DAY_TIME,
-
-    // Secondary families.
-
-    STRING,
-    APPROXIMATE_NUMERIC,
-    EXACT_NUMERIC,
-    DECIMAL,
-    INTEGER,
-    DATETIME,
-    DATETIME_INTERVAL,
-    MULTISET,
-    ARRAY,
-    MAP,
-    NULL,
-    ANY,
-    CURSOR,
-    COLUMN_LIST,
-    GEO,
-    VARIANT,
-    FUNCTION,
-    /** Like ANY, but do not even validate the operand. It may not be an expression. */
-    IGNORE;
-
-    private static final Map<Integer, SqlTypeFamily> JDBC_TYPE_TO_FAMILY =
-            ImmutableMap.<Integer, SqlTypeFamily>builder()
-                    // Not present:
-                    // SqlTypeName.MULTISET shares Types.ARRAY with SqlTypeName.ARRAY;
-                    // SqlTypeName.MAP has no corresponding JDBC type
-                    // SqlTypeName.COLUMN_LIST has no corresponding JDBC type
-                    .put(Types.BIT, NUMERIC)
-                    .put(Types.TINYINT, NUMERIC)
-                    .put(Types.SMALLINT, NUMERIC)
-                    .put(Types.BIGINT, NUMERIC)
-                    .put(Types.INTEGER, NUMERIC)
-                    .put(Types.NUMERIC, NUMERIC)
-                    .put(Types.DECIMAL, NUMERIC)
-                    .put(Types.FLOAT, NUMERIC)
-                    .put(Types.REAL, NUMERIC)
-                    .put(Types.DOUBLE, NUMERIC)
-                    .put(Types.CHAR, CHARACTER)
-                    .put(Types.VARCHAR, CHARACTER)
-                    .put(Types.LONGVARCHAR, CHARACTER)
-                    .put(Types.CLOB, CHARACTER)
-                    .put(Types.BINARY, BINARY)
-                    .put(Types.VARBINARY, BINARY)
-                    .put(Types.LONGVARBINARY, BINARY)
-                    .put(Types.BLOB, BINARY)
-                    .put(Types.DATE, DATE)
-                    .put(Types.TIME, TIME)
-                    .put(ExtraSqlTypes.TIME_WITH_TIMEZONE, TIME)
-                    .put(Types.TIMESTAMP, TIMESTAMP)
-                    .put(ExtraSqlTypes.TIMESTAMP_WITH_TIMEZONE, TIMESTAMP)
-                    .put(Types.BOOLEAN, BOOLEAN)
-                    .put(ExtraSqlTypes.REF_CURSOR, CURSOR)
-                    .put(Types.ARRAY, ARRAY)
-                    .put(Types.JAVA_OBJECT, VARIANT)
-                    .build();
-
-    /**
-     * Gets the primary family containing a JDBC type.
-     *
-     * @param jdbcType the JDBC type of interest
-     * @return containing family
-     */
-    public static @Nullable SqlTypeFamily getFamilyForJdbcType(int jdbcType) {
-        return JDBC_TYPE_TO_FAMILY.get(jdbcType);
-    }
-
-    /**
-     * For this type family, returns the allow types of the difference between two values of this
-     * family.
-     *
-     * <p>Equivalently, given an {@code ORDER BY} expression with one key, returns the allowable
-     * type families of the difference between two keys.
-     *
-     * <p>Example 1. For {@code ORDER BY empno}, a NUMERIC, the difference between two {@code empno}
-     * values is also NUMERIC.
-     *
-     * <p>Example 2. For {@code ORDER BY hireDate}, a DATE, the difference between two {@code
-     * hireDate} values might be an INTERVAL_DAY_TIME or INTERVAL_YEAR_MONTH.
-     *
-     * <p>The result determines whether a {@link SqlWindow} with a {@code RANGE} is valid (for
-     * example, {@code OVER (ORDER BY empno RANGE 10} is valid because {@code 10} is numeric); and
-     * whether a call to {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#PERCENTILE_CONT
-     * PERCENTILE_CONT} is valid (for example, {@code PERCENTILE_CONT(0.25)} ORDER BY (hireDate)} is
-     * valid because {@code hireDate} values may be interpolated by adding values of type {@code
-     * INTERVAL_DAY_TIME}.
-     */
-    public List<SqlTypeFamily> allowableDifferenceTypes() {
-        switch (this) {
-            case NUMERIC:
-                return ImmutableList.of(NUMERIC);
-            case DATE:
-            case TIME:
-            case TIMESTAMP:
-                return ImmutableList.of(INTERVAL_DAY_TIME, INTERVAL_YEAR_MONTH);
-            default:
-                return ImmutableList.of();
-        }
-    }
-
-    /** Returns the collection of {@link SqlTypeName}s included in this family. */
-    public Collection<SqlTypeName> getTypeNames() {
-        switch (this) {
-            case CHARACTER:
-                return SqlTypeName.CHAR_TYPES;
-            case BINARY:
-                return SqlTypeName.BINARY_TYPES;
-            case NUMERIC:
-                return SqlTypeName.NUMERIC_TYPES;
-            case DECIMAL:
-                return ImmutableList.of(SqlTypeName.DECIMAL);
-            case DATE:
-                return ImmutableList.of(SqlTypeName.DATE);
-            case TIME:
-                return ImmutableList.of(
-                        SqlTypeName.TIME,
-                        SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
-                        SqlTypeName.TIME_TZ);
-            case TIMESTAMP:
-                return ImmutableList.of(
-                        SqlTypeName.TIMESTAMP,
-                        SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
-                        SqlTypeName.TIMESTAMP_TZ);
-            case BOOLEAN:
-                return SqlTypeName.BOOLEAN_TYPES;
-            case INTERVAL_YEAR_MONTH:
-                return SqlTypeName.YEAR_INTERVAL_TYPES;
-            case INTERVAL_DAY_TIME:
-                return SqlTypeName.DAY_INTERVAL_TYPES;
-            case STRING:
-                return SqlTypeName.STRING_TYPES;
-            case APPROXIMATE_NUMERIC:
-                return SqlTypeName.APPROX_TYPES;
-            case EXACT_NUMERIC:
-                return SqlTypeName.EXACT_TYPES;
-            case INTEGER:
-                return SqlTypeName.INT_TYPES;
-            case DATETIME:
-                return SqlTypeName.DATETIME_TYPES;
-            case DATETIME_INTERVAL:
-                return SqlTypeName.INTERVAL_TYPES;
-            case GEO:
-                return SqlTypeName.GEOMETRY_TYPES;
-            case MULTISET:
-                return ImmutableList.of(SqlTypeName.MULTISET);
-            case ARRAY:
-                return ImmutableList.of(SqlTypeName.ARRAY);
-            case MAP:
-                return ImmutableList.of(SqlTypeName.MAP);
-            case NULL:
-                return ImmutableList.of(SqlTypeName.NULL);
-            case ANY:
-                return SqlTypeName.ALL_TYPES;
-            case CURSOR:
-                return ImmutableList.of(SqlTypeName.CURSOR);
-            case COLUMN_LIST:
-                return ImmutableList.of(SqlTypeName.COLUMN_LIST);
-            case FUNCTION:
-                return ImmutableList.of(SqlTypeName.FUNCTION);
-            case VARIANT:
-                return ImmutableList.of(SqlTypeName.VARIANT);
-            default:
-                throw new IllegalArgumentException();
-        }
-    }
-
-    /** Return the default {@link RelDataType} that belongs to this family. */
-    public @Nullable RelDataType getDefaultConcreteType(RelDataTypeFactory factory) {
-        switch (this) {
-            case CHARACTER:
-                return factory.createSqlType(SqlTypeName.VARCHAR);
-            case BINARY:
-                return factory.createSqlType(SqlTypeName.VARBINARY);
-            case NUMERIC:
-                return SqlTypeUtil.getMaxPrecisionScaleDecimal(factory);
-            case DATE:
-                return factory.createSqlType(SqlTypeName.DATE);
-            case TIME:
-                return factory.createSqlType(SqlTypeName.TIME);
-            case TIMESTAMP:
-                return factory.createSqlType(SqlTypeName.TIMESTAMP);
-            case BOOLEAN:
-                return factory.createSqlType(SqlTypeName.BOOLEAN);
-            case STRING:
-                return factory.createSqlType(SqlTypeName.VARCHAR);
-            case APPROXIMATE_NUMERIC:
-                return factory.createSqlType(SqlTypeName.DOUBLE);
-            case EXACT_NUMERIC:
-                return SqlTypeUtil.getMaxPrecisionScaleDecimal(factory);
-            case INTEGER:
-                return factory.createSqlType(SqlTypeName.BIGINT);
-            case DECIMAL:
-                return factory.createSqlType(SqlTypeName.DECIMAL);
-            case DATETIME:
-                return factory.createSqlType(SqlTypeName.TIMESTAMP);
-            case INTERVAL_DAY_TIME:
-                return factory.createSqlIntervalType(
-                        new SqlIntervalQualifier(TimeUnit.DAY, TimeUnit.SECOND, SqlParserPos.ZERO));
-            case INTERVAL_YEAR_MONTH:
-                return factory.createSqlIntervalType(
-                        new SqlIntervalQualifier(TimeUnit.YEAR, TimeUnit.MONTH, SqlParserPos.ZERO));
-            case GEO:
-                return factory.createSqlType(SqlTypeName.GEOMETRY);
-            case MULTISET:
-                return factory.createMultisetType(factory.createSqlType(SqlTypeName.ANY), -1);
-            case ARRAY:
-                return factory.createArrayType(factory.createSqlType(SqlTypeName.ANY), -1);
-            case MAP:
-                return factory.createMapType(
-                        factory.createSqlType(SqlTypeName.ANY),
-                        factory.createSqlType(SqlTypeName.ANY));
-            case NULL:
-                return factory.createSqlType(SqlTypeName.NULL);
-            case CURSOR:
-                return factory.createSqlType(SqlTypeName.CURSOR);
-            case COLUMN_LIST:
-                return factory.createSqlType(SqlTypeName.COLUMN_LIST);
-            case FUNCTION:
-                return factory.createFunctionSqlType(
-                        factory.createStructType(ImmutableList.of(), ImmutableList.of()),
-                        factory.createSqlType(SqlTypeName.ANY));
-            default:
-                return null;
-        }
-    }
-
-    public boolean contains(RelDataType type) {
-        return SqlTypeUtil.isOfSameTypeName(getTypeNames(), type);
-    }
-}
diff --git a/flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
deleted file mode 100644
index 5f76b4493b1..00000000000
--- a/flink-table/flink-sql-parser/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
+++ /dev/null
@@ -1,1078 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.sql.type;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import org.apache.calcite.avatica.util.TimeUnit;
-import org.apache.calcite.sql.SqlLiteral;
-import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.util.DateString;
-import org.apache.calcite.util.TimeString;
-import org.apache.calcite.util.TimestampString;
-import org.apache.calcite.util.Util;
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-import java.math.BigDecimal;
-import java.sql.Types;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Enumeration of the type names which can be used to construct a SQL type. Rationale for this
- * class's existence (instead of just using the standard java.sql.Type ordinals):
- *
- * <ul>
- *   <li>{@link Types} does not include all SQL2003 data-types;
- *   <li>SqlTypeName provides a type-safe enumeration;
- *   <li>SqlTypeName provides a place to hang extra information such as whether the type carries
- *       precision and scale.
- * </ul>
- *
- * <p>This class was copied over from Calcite to support variant type(CALCITE-4918). When upgrading
- * to Calcite 1.39.0 version, please remove the entire class.
- */
-public enum SqlTypeName {
-    BOOLEAN(PrecScale.NO_NO, false, Types.BOOLEAN, SqlTypeFamily.BOOLEAN),
-    TINYINT(PrecScale.NO_NO, false, Types.TINYINT, SqlTypeFamily.NUMERIC),
-    SMALLINT(PrecScale.NO_NO, false, Types.SMALLINT, SqlTypeFamily.NUMERIC),
-    INTEGER(PrecScale.NO_NO, false, Types.INTEGER, SqlTypeFamily.NUMERIC),
-    BIGINT(PrecScale.NO_NO, false, Types.BIGINT, SqlTypeFamily.NUMERIC),
-    DECIMAL(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.DECIMAL,
-            SqlTypeFamily.NUMERIC),
-    FLOAT(PrecScale.NO_NO, false, Types.FLOAT, SqlTypeFamily.NUMERIC),
-    REAL(PrecScale.NO_NO, false, Types.REAL, SqlTypeFamily.NUMERIC),
-    DOUBLE(PrecScale.NO_NO, false, Types.DOUBLE, SqlTypeFamily.NUMERIC),
-    DATE(PrecScale.NO_NO, false, Types.DATE, SqlTypeFamily.DATE),
-    TIME(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIME, SqlTypeFamily.TIME),
-    TIME_WITH_LOCAL_TIME_ZONE(
-            PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIME, SqlTypeFamily.TIME),
-    TIME_TZ(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIME, SqlTypeFamily.TIME),
-    TIMESTAMP(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIMESTAMP, SqlTypeFamily.TIMESTAMP),
-    TIMESTAMP_WITH_LOCAL_TIME_ZONE(
-            PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIMESTAMP, SqlTypeFamily.TIMESTAMP),
-    TIMESTAMP_TZ(
-            PrecScale.NO_NO | PrecScale.YES_NO, false, Types.TIMESTAMP, SqlTypeFamily.TIMESTAMP),
-    INTERVAL_YEAR(PrecScale.NO_NO, false, Types.OTHER, SqlTypeFamily.INTERVAL_YEAR_MONTH),
-    INTERVAL_YEAR_MONTH(PrecScale.NO_NO, false, Types.OTHER, SqlTypeFamily.INTERVAL_YEAR_MONTH),
-    INTERVAL_MONTH(PrecScale.NO_NO, false, Types.OTHER, SqlTypeFamily.INTERVAL_YEAR_MONTH),
-    INTERVAL_DAY(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_DAY_HOUR(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_DAY_MINUTE(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_DAY_SECOND(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_HOUR(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_HOUR_MINUTE(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_HOUR_SECOND(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_MINUTE(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_MINUTE_SECOND(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    INTERVAL_SECOND(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            false,
-            Types.OTHER,
-            SqlTypeFamily.INTERVAL_DAY_TIME),
-    CHAR(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.CHAR, SqlTypeFamily.CHARACTER),
-    VARCHAR(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.VARCHAR, SqlTypeFamily.CHARACTER),
-    BINARY(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.BINARY, SqlTypeFamily.BINARY),
-    VARBINARY(PrecScale.NO_NO | PrecScale.YES_NO, false, Types.VARBINARY, SqlTypeFamily.BINARY),
-    NULL(PrecScale.NO_NO, true, Types.NULL, SqlTypeFamily.NULL),
-    UNKNOWN(PrecScale.NO_NO, true, Types.NULL, SqlTypeFamily.NULL),
-    ANY(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            true,
-            Types.JAVA_OBJECT,
-            SqlTypeFamily.ANY),
-    SYMBOL(PrecScale.NO_NO, true, Types.OTHER, null),
-    MULTISET(PrecScale.NO_NO, false, Types.ARRAY, SqlTypeFamily.MULTISET),
-    ARRAY(PrecScale.NO_NO, false, Types.ARRAY, SqlTypeFamily.ARRAY),
-    MAP(PrecScale.NO_NO, false, Types.OTHER, SqlTypeFamily.MAP),
-    DISTINCT(PrecScale.NO_NO, false, Types.DISTINCT, null),
-    STRUCTURED(PrecScale.NO_NO, false, Types.STRUCT, null),
-    ROW(PrecScale.NO_NO, false, Types.STRUCT, null),
-    OTHER(PrecScale.NO_NO, false, Types.OTHER, null),
-    CURSOR(PrecScale.NO_NO, false, ExtraSqlTypes.REF_CURSOR, SqlTypeFamily.CURSOR),
-    COLUMN_LIST(PrecScale.NO_NO, false, Types.OTHER + 2, SqlTypeFamily.COLUMN_LIST),
-    DYNAMIC_STAR(
-            PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES,
-            true,
-            Types.JAVA_OBJECT,
-            SqlTypeFamily.ANY),
-    /**
-     * Spatial type. Though not standard, it is common to several DBs, so we do not flag it
-     * 'special' (internal).
-     */
-    GEOMETRY(PrecScale.NO_NO, false, ExtraSqlTypes.GEOMETRY, SqlTypeFamily.GEO),
-    MEASURE(PrecScale.NO_NO, true, Types.OTHER, SqlTypeFamily.ANY),
-    FUNCTION(PrecScale.NO_NO, true, Types.OTHER, SqlTypeFamily.FUNCTION),
-    SARG(PrecScale.NO_NO, true, Types.OTHER, SqlTypeFamily.ANY),
-    /**
-     * VARIANT data type, a dynamically-typed value that can have at runtime any of the other data
-     * types in this table.
-     */
-    VARIANT(PrecScale.NO_NO, false, Types.OTHER, SqlTypeFamily.VARIANT);
-
-    public static final int MAX_DATETIME_PRECISION = 3;
-
-    // Minimum and default interval precisions are  defined by SQL2003
-    // Maximum interval precisions are implementation dependent,
-    //  but must be at least the default value
-    public static final int DEFAULT_INTERVAL_START_PRECISION = 2;
-    public static final int DEFAULT_INTERVAL_FRACTIONAL_SECOND_PRECISION = 6;
-    public static final int MIN_INTERVAL_START_PRECISION = 1;
-    public static final int MIN_INTERVAL_FRACTIONAL_SECOND_PRECISION = 0;
-    public static final int MAX_INTERVAL_START_PRECISION = 10;
-    public static final int MAX_INTERVAL_FRACTIONAL_SECOND_PRECISION = 9;
-
-    // Cached map of enum values
-    private static final Map<String, SqlTypeName> VALUES_MAP =
-            Util.enumConstants(SqlTypeName.class);
-
-    // categorizations used by SqlTypeFamily definitions
-
-    // you probably want to use JDK 1.5 support for treating enumeration
-    // as collection instead; this is only here to support
-    // SqlTypeFamily.ANY
-    public static final List<SqlTypeName> ALL_TYPES =
-            ImmutableList.of(
-                    BOOLEAN,
-                    INTEGER,
-                    VARCHAR,
-                    DATE,
-                    TIME,
-                    TIMESTAMP,
-                    NULL,
-                    DECIMAL,
-                    ANY,
-                    CHAR,
-                    BINARY,
-                    VARBINARY,
-                    TINYINT,
-                    SMALLINT,
-                    BIGINT,
-                    REAL,
-                    DOUBLE,
-                    SYMBOL,
-                    INTERVAL_YEAR,
-                    INTERVAL_YEAR_MONTH,
-                    INTERVAL_MONTH,
-                    INTERVAL_DAY,
-                    INTERVAL_DAY_HOUR,
-                    INTERVAL_DAY_MINUTE,
-                    INTERVAL_DAY_SECOND,
-                    INTERVAL_HOUR,
-                    INTERVAL_HOUR_MINUTE,
-                    INTERVAL_HOUR_SECOND,
-                    INTERVAL_MINUTE,
-                    INTERVAL_MINUTE_SECOND,
-                    INTERVAL_SECOND,
-                    TIME_WITH_LOCAL_TIME_ZONE,
-                    TIME_TZ,
-                    TIMESTAMP_WITH_LOCAL_TIME_ZONE,
-                    TIMESTAMP_TZ,
-                    FLOAT,
-                    MULTISET,
-                    DISTINCT,
-                    STRUCTURED,
-                    ROW,
-                    CURSOR,
-                    COLUMN_LIST,
-                    VARIANT);
-
-    public static final List<SqlTypeName> BOOLEAN_TYPES = ImmutableList.of(BOOLEAN);
-
-    public static final List<SqlTypeName> BINARY_TYPES = ImmutableList.of(BINARY, VARBINARY);
-
-    public static final List<SqlTypeName> INT_TYPES =
-            ImmutableList.of(TINYINT, SMALLINT, INTEGER, BIGINT);
-
-    public static final List<SqlTypeName> EXACT_TYPES =
-            combine(INT_TYPES, ImmutableList.of(DECIMAL));
-
-    public static final List<SqlTypeName> APPROX_TYPES = ImmutableList.of(FLOAT, REAL, DOUBLE);
-
-    public static final List<SqlTypeName> NUMERIC_TYPES = combine(EXACT_TYPES, APPROX_TYPES);
-
-    public static final List<SqlTypeName> FRACTIONAL_TYPES =
-            combine(APPROX_TYPES, ImmutableList.of(DECIMAL));
-
-    public static final List<SqlTypeName> CHAR_TYPES = ImmutableList.of(CHAR, VARCHAR);
-
-    public static final List<SqlTypeName> STRING_TYPES = combine(CHAR_TYPES, BINARY_TYPES);
-
-    public static final List<SqlTypeName> GEOMETRY_TYPES = ImmutableList.of(GEOMETRY);
-
-    public static final List<SqlTypeName> DATETIME_TYPES =
-            ImmutableList.of(
-                    DATE,
-                    TIME,
-                    TIME_WITH_LOCAL_TIME_ZONE,
-                    TIME_TZ,
-                    TIMESTAMP,
-                    TIMESTAMP_WITH_LOCAL_TIME_ZONE,
-                    TIMESTAMP_TZ);
-
-    /** Types that contain time zone information. */
-    public static final List<SqlTypeName> TZ_TYPES =
-            ImmutableList.of(
-                    TIME_WITH_LOCAL_TIME_ZONE,
-                    TIME_TZ,
-                    TIMESTAMP_WITH_LOCAL_TIME_ZONE,
-                    TIMESTAMP_TZ);
-
-    public static final Set<SqlTypeName> YEAR_INTERVAL_TYPES =
-            Sets.immutableEnumSet(
-                    SqlTypeName.INTERVAL_YEAR,
-                    SqlTypeName.INTERVAL_YEAR_MONTH,
-                    SqlTypeName.INTERVAL_MONTH);
-
-    public static final Set<SqlTypeName> DAY_INTERVAL_TYPES =
-            Sets.immutableEnumSet(
-                    SqlTypeName.INTERVAL_DAY,
-                    SqlTypeName.INTERVAL_DAY_HOUR,
-                    SqlTypeName.INTERVAL_DAY_MINUTE,
-                    SqlTypeName.INTERVAL_DAY_SECOND,
-                    SqlTypeName.INTERVAL_HOUR,
-                    SqlTypeName.INTERVAL_HOUR_MINUTE,
-                    SqlTypeName.INTERVAL_HOUR_SECOND,
-                    SqlTypeName.INTERVAL_MINUTE,
-                    SqlTypeName.INTERVAL_MINUTE_SECOND,
-                    SqlTypeName.INTERVAL_SECOND);
-
-    public static final Set<SqlTypeName> INTERVAL_TYPES =
-            Sets.immutableEnumSet(Iterables.concat(YEAR_INTERVAL_TYPES, DAY_INTERVAL_TYPES));
-
-    /** The possible types of a time frame argument to a function such as {@code TIMESTAMP_DIFF}. */
-    public static final Set<SqlTypeName> TIME_FRAME_TYPES =
-            Sets.immutableEnumSet(Iterables.concat(INTERVAL_TYPES, ImmutableList.of(SYMBOL)));
-
-    private static final Map<Integer, SqlTypeName> JDBC_TYPE_TO_NAME =
-            ImmutableMap.<Integer, SqlTypeName>builder()
-                    .put(Types.TINYINT, TINYINT)
-                    .put(Types.SMALLINT, SMALLINT)
-                    .put(Types.BIGINT, BIGINT)
-                    .put(Types.INTEGER, INTEGER)
-                    .put(Types.NUMERIC, DECIMAL) // REVIEW
-                    .put(Types.DECIMAL, DECIMAL)
-                    .put(Types.FLOAT, FLOAT)
-                    .put(Types.REAL, REAL)
-                    .put(Types.DOUBLE, DOUBLE)
-                    .put(Types.CHAR, CHAR)
-                    .put(Types.VARCHAR, VARCHAR)
-
-                    // TODO: provide real support for these eventually
-                    .put(ExtraSqlTypes.NCHAR, CHAR)
-                    .put(ExtraSqlTypes.NVARCHAR, VARCHAR)
-
-                    // TODO: additional types not yet supported. See ExtraSqlTypes.
-                    // .put(Types.LONGVARCHAR, Longvarchar)
-                    // .put(Types.CLOB, Clob)
-                    // .put(Types.LONGVARBINARY, Longvarbinary)
-                    // .put(Types.BLOB, Blob)
-                    // .put(Types.LONGNVARCHAR, Longnvarchar)
-                    // .put(Types.NCLOB, Nclob)
-                    // .put(Types.ROWID, Rowid)
-                    // .put(Types.SQLXML, Sqlxml)
-
-                    .put(Types.BINARY, BINARY)
-                    .put(Types.VARBINARY, VARBINARY)
-                    .put(Types.DATE, DATE)
-                    .put(Types.TIME, TIME)
-                    .put(Types.TIMESTAMP, TIMESTAMP)
-                    .put(Types.BIT, BOOLEAN)
-                    .put(Types.BOOLEAN, BOOLEAN)
-                    .put(Types.DISTINCT, DISTINCT)
-                    .put(Types.STRUCT, STRUCTURED)
-                    .put(Types.ARRAY, ARRAY)
-                    .build();
-
-    /** Bitwise-or of flags indicating allowable precision/scale combinations. */
-    private final int signatures;
-
-    /**
-     * Returns true if not of a "pure" standard sql type. "Inpure" types are {@link #ANY}, {@link
-     * #NULL} and {@link #SYMBOL}
-     */
-    private final boolean special;
-
-    private final int jdbcOrdinal;
-    private final @Nullable SqlTypeFamily family;
-
-    SqlTypeName(int signatures, boolean special, int jdbcType, @Nullable SqlTypeFamily family) {
-        this.signatures = signatures;
-        this.special = special;
-        this.jdbcOrdinal = jdbcType;
-        this.family = family;
-    }
-
-    /**
-     * Looks up a type name from its name.
-     *
-     * @return Type name, or null if not found
-     */
-    public static @Nullable SqlTypeName get(String name) {
-        if (false) {
-            // The following code works OK, but the spurious exceptions are
-            // annoying.
-            try {
-                return SqlTypeName.valueOf(name);
-            } catch (IllegalArgumentException e) {
-                return null;
-            }
-        }
-        return VALUES_MAP.get(name);
-    }
-
-    /**
-     * Returns the SqlTypeName value whose name or {@link #getSpaceName()} matches the given name,
-     * or throws {@link IllegalArgumentException}; never returns null.
-     */
-    public static SqlTypeName lookup(String tag) {
-        // Special handling for TIME WITH TIME ZONE and
-        // TIMESTAMP WITH TIME ZONE, whose type names are TIME_TZ and TIMESTAMP_TZ.
-        // We know that the type name is always uppercase, because it is
-        // inserted in the tag by the parser.
-        final String tag1 = tag.replace("WITH TIME ZONE", "TZ");
-        final String tag2 = tag1.replace(' ', '_');
-        return valueOf(tag2);
-    }
-
-    public boolean allowsNoPrecNoScale() {
-        return (signatures & PrecScale.NO_NO) != 0;
-    }
-
-    public boolean allowsPrecNoScale() {
-        return (signatures & PrecScale.YES_NO) != 0;
-    }
-
-    public boolean allowsPrec() {
-        return allowsPrecScale(true, true) || allowsPrecScale(true, false);
-    }
-
-    public boolean allowsScale() {
-        return allowsPrecScale(true, true);
-    }
-
-    /**
-     * Returns whether this type can be specified with a given combination of precision and scale.
-     * For example,
-     *
-     * <ul>
-     *   <li><code>Varchar.allowsPrecScale(true, false)</code> returns <code>
-     * true</code>, because the VARCHAR type allows a precision parameter, as in <code>VARCHAR(10)
-     *       </code>.
-     *   <li><code>Varchar.allowsPrecScale(true, true)</code> returns <code>
-     * false</code>, because the VARCHAR type does not allow a precision and a scale parameter, as
-     *       in <code>VARCHAR(10, 4)</code>.
-     *   <li><code>allowsPrecScale(false, true)</code> returns <code>false</code> for every type.
-     * </ul>
-     *
-     * @param precision Whether the precision/length field is part of the type specification
-     * @param scale Whether the scale field is part of the type specification
-     * @return Whether this combination of precision/scale is valid
-     */
-    public boolean allowsPrecScale(boolean precision, boolean scale) {
-        int mask =
-                precision
-                        ? (scale ? PrecScale.YES_YES : PrecScale.YES_NO)
-                        : (scale ? 0 : PrecScale.NO_NO);
-        return (signatures & mask) != 0;
-    }
-
-    public boolean isSpecial() {
-        return special;
-    }
-
-    /** Returns the ordinal from {@link java.sql.Types} corresponding to this SqlTypeName. */
-    public int getJdbcOrdinal() {
-        return jdbcOrdinal;
-    }
-
-    private static List<SqlTypeName> combine(List<SqlTypeName> list0, List<SqlTypeName> list1) {
-        return ImmutableList.<SqlTypeName>builder().addAll(list0).addAll(list1).build();
-    }
-
-    /**
-     * Returns the default scale for this type if supported, otherwise -1 if scale is either
-     * unsupported or must be specified explicitly.
-     */
-    public int getDefaultScale() {
-        switch (this) {
-            case DECIMAL:
-                return 0;
-            case INTERVAL_YEAR:
-            case INTERVAL_YEAR_MONTH:
-            case INTERVAL_MONTH:
-            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:
-                return DEFAULT_INTERVAL_FRACTIONAL_SECOND_PRECISION;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * Gets the SqlTypeFamily containing this SqlTypeName.
-     *
-     * @return containing family, or null for none (SYMBOL, DISTINCT, STRUCTURED, ROW, OTHER)
-     */
-    public @Nullable SqlTypeFamily getFamily() {
-        return family;
-    }
-
-    /**
-     * Gets the SqlTypeName corresponding to a JDBC type.
-     *
-     * @param jdbcType the JDBC type of interest
-     * @return corresponding SqlTypeName, or null if the type is not known
-     */
-    public static @Nullable SqlTypeName getNameForJdbcType(int jdbcType) {
-        return JDBC_TYPE_TO_NAME.get(jdbcType);
-    }
-
-    /**
-     * Returns the limit of this datatype. For example,
-     *
-     * <table border="1">
-     * <caption>Datatype limits</caption>
-     * <tr>
-     * <th>Datatype</th>
-     * <th>sign</th>
-     * <th>limit</th>
-     * <th>beyond</th>
-     * <th>precision</th>
-     * <th>scale</th>
-     * <th>Returns</th>
-     * </tr>
-     * <tr>
-     * <td>Integer</td>
-     * <td>true</td>
-     * <td>true</td>
-     * <td>false</td>
-     * <td>-1</td>
-     * <td>-1</td>
-     * <td>2147483647 (2 ^ 31 -1 = MAXINT)</td>
-     * </tr>
-     * <tr>
-     * <td>Integer</td>
-     * <td>true</td>
-     * <td>true</td>
-     * <td>true</td>
-     * <td>-1</td>
-     * <td>-1</td>
-     * <td>2147483648 (2 ^ 31 = MAXINT + 1)</td>
-     * </tr>
-     * <tr>
-     * <td>Integer</td>
-     * <td>false</td>
-     * <td>true</td>
-     * <td>false</td>
-     * <td>-1</td>
-     * <td>-1</td>
-     * <td>-2147483648 (-2 ^ 31 = MININT)</td>
-     * </tr>
-     * <tr>
-     * <td>Boolean</td>
-     * <td>true</td>
-     * <td>true</td>
-     * <td>false</td>
-     * <td>-1</td>
-     * <td>-1</td>
-     * <td>TRUE</td>
-     * </tr>
-     * <tr>
-     * <td>Varchar</td>
-     * <td>true</td>
-     * <td>true</td>
-     * <td>false</td>
-     * <td>10</td>
-     * <td>-1</td>
-     * <td>'ZZZZZZZZZZ'</td>
-     * </tr>
-     * </table>
-     *
-     * @param sign If true, returns upper limit, otherwise lower limit
-     * @param limit If true, returns value at or near to overflow; otherwise value at or near to
-     *     underflow
-     * @param beyond If true, returns the value just beyond the limit, otherwise the value at the
-     *     limit
-     * @param precision Precision, or -1 if not applicable
-     * @param scale Scale, or -1 if not applicable
-     * @return Limit value
-     */
-    public @Nullable Object getLimit(
-            boolean sign, Limit limit, boolean beyond, int precision, int scale) {
-        assert allowsPrecScale(precision != -1, scale != -1) : this;
-        if (limit == Limit.ZERO) {
-            if (beyond) {
-                return null;
-            }
-            sign = true;
-        }
-        Calendar calendar;
-
-        switch (this) {
-            case BOOLEAN:
-                switch (limit) {
-                    case ZERO:
-                        return false;
-                    case UNDERFLOW:
-                        return null;
-                    case OVERFLOW:
-                        if (beyond || !sign) {
-                            return null;
-                        } else {
-                            return true;
-                        }
-                    default:
-                        throw Util.unexpected(limit);
-                }
-
-            case TINYINT:
-                return getNumericLimit(2, 8, sign, limit, beyond);
-
-            case SMALLINT:
-                return getNumericLimit(2, 16, sign, limit, beyond);
-
-            case INTEGER:
-                return getNumericLimit(2, 32, sign, limit, beyond);
-
-            case BIGINT:
-                return getNumericLimit(2, 64, sign, limit, beyond);
-
-            case DECIMAL:
-                BigDecimal decimal = getNumericLimit(10, precision, sign, limit, beyond);
-                if (decimal == null) {
-                    return null;
-                }
-
-                // Decimal values must fit into 64 bits. So, the maximum value of
-                // a DECIMAL(19, 0) is 2^63 - 1, not 10^19 - 1.
-                switch (limit) {
-                    case OVERFLOW:
-                        final BigDecimal other =
-                                (BigDecimal) BIGINT.getLimit(sign, limit, beyond, -1, -1);
-                        if (other != null && decimal.compareTo(other) == (sign ? 1 : -1)) {
-                            decimal = other;
-                        }
-                        break;
-                    default:
-                        break;
-                }
-
-                // Apply scale.
-                if (scale == 0) {
-                    // do nothing
-                } else if (scale > 0) {
-                    decimal = decimal.divide(BigDecimal.TEN.pow(scale));
-                } else {
-                    decimal = decimal.multiply(BigDecimal.TEN.pow(-scale));
-                }
-                return decimal;
-
-            case CHAR:
-            case VARCHAR:
-                if (!sign) {
-                    return null; // this type does not have negative values
-                }
-                StringBuilder buf = new StringBuilder();
-                switch (limit) {
-                    case ZERO:
-                        break;
-                    case UNDERFLOW:
-                        if (beyond) {
-                            // There is no value between the empty string and the
-                            // smallest non-empty string.
-                            return null;
-                        }
-                        buf.append("a");
-                        break;
-                    case OVERFLOW:
-                        for (int i = 0; i < precision; ++i) {
-                            buf.append("Z");
-                        }
-                        if (beyond) {
-                            buf.append("Z");
-                        }
-                        break;
-                    default:
-                        break;
-                }
-                return buf.toString();
-
-            case BINARY:
-            case VARBINARY:
-                if (!sign) {
-                    return null; // this type does not have negative values
-                }
-                byte[] bytes;
-                switch (limit) {
-                    case ZERO:
-                        bytes = new byte[0];
-                        break;
-                    case UNDERFLOW:
-                        if (beyond) {
-                            // There is no value between the empty string and the
-                            // smallest value.
-                            return null;
-                        }
-                        bytes = new byte[] {0x00};
-                        break;
-                    case OVERFLOW:
-                        bytes = new byte[precision + (beyond ? 1 : 0)];
-                        Arrays.fill(bytes, (byte) 0xff);
-                        break;
-                    default:
-                        throw Util.unexpected(limit);
-                }
-                return bytes;
-
-            case DATE:
-                calendar = Util.calendar();
-                switch (limit) {
-                    case ZERO:
-                        // The epoch.
-                        calendar.set(Calendar.YEAR, 1970);
-                        calendar.set(Calendar.MONTH, 0);
-                        calendar.set(Calendar.DAY_OF_MONTH, 1);
-                        break;
-                    case UNDERFLOW:
-                        return null;
-                    case OVERFLOW:
-                        if (beyond) {
-                            // It is impossible to represent an invalid year as a date
-                            // literal. SQL dates are represented as 'yyyy-mm-dd', and
-                            // 1 <= yyyy <= 9999 is valid. There is no year 0: the year
-                            // before 1AD is 1BC, so SimpleDateFormat renders the day
-                            // before 0001-01-01 (AD) as 0001-12-31 (BC), which looks
-                            // like a valid date.
-                            return null;
-                        }
-
-                        // "SQL:2003 6.1 <data type> Access Rules 6" says that year is
-                        // between 1 and 9999, and days/months are the valid Gregorian
-                        // calendar values for these years.
-                        if (sign) {
-                            calendar.set(Calendar.YEAR, 9999);
-                            calendar.set(Calendar.MONTH, 11);
-                            calendar.set(Calendar.DAY_OF_MONTH, 31);
-                        } else {
-                            calendar.set(Calendar.YEAR, 1);
-                            calendar.set(Calendar.MONTH, 0);
-                            calendar.set(Calendar.DAY_OF_MONTH, 1);
-                        }
-                        break;
-                    default:
-                        break;
-                }
-                calendar.set(Calendar.HOUR_OF_DAY, 0);
-                calendar.set(Calendar.MINUTE, 0);
-                calendar.set(Calendar.SECOND, 0);
-                return calendar;
-
-            case TIME:
-                if (!sign) {
-                    return null; // this type does not have negative values
-                }
-                if (beyond) {
-                    return null; // invalid values are impossible to represent
-                }
-                calendar = Util.calendar();
-                switch (limit) {
-                    case ZERO:
-
-                        // The epoch.
-                        calendar.set(Calendar.HOUR_OF_DAY, 0);
-                        calendar.set(Calendar.MINUTE, 0);
-                        calendar.set(Calendar.SECOND, 0);
-                        calendar.set(Calendar.MILLISECOND, 0);
-                        break;
-                    case UNDERFLOW:
-                        return null;
-                    case OVERFLOW:
-                        calendar.set(Calendar.HOUR_OF_DAY, 23);
-                        calendar.set(Calendar.MINUTE, 59);
-                        calendar.set(Calendar.SECOND, 59);
-                        int millis =
-                                (precision >= 3)
-                                        ? 999
-                                        : ((precision == 2) ? 990 : ((precision == 1) ? 900 : 0));
-                        calendar.set(Calendar.MILLISECOND, millis);
-                        break;
-                    default:
-                        break;
-                }
-                return calendar;
-
-            case TIMESTAMP:
-                calendar = Util.calendar();
-                switch (limit) {
-                    case ZERO:
-                        // The epoch.
-                        calendar.set(Calendar.YEAR, 1970);
-                        calendar.set(Calendar.MONTH, 0);
-                        calendar.set(Calendar.DAY_OF_MONTH, 1);
-                        calendar.set(Calendar.HOUR_OF_DAY, 0);
-                        calendar.set(Calendar.MINUTE, 0);
-                        calendar.set(Calendar.SECOND, 0);
-                        calendar.set(Calendar.MILLISECOND, 0);
-                        break;
-                    case UNDERFLOW:
-                        return null;
-                    case OVERFLOW:
-                        if (beyond) {
-                            // It is impossible to represent an invalid year as a date
-                            // literal. SQL dates are represented as 'yyyy-mm-dd', and
-                            // 1 <= yyyy <= 9999 is valid. There is no year 0: the year
-                            // before 1AD is 1BC, so SimpleDateFormat renders the day
-                            // before 0001-01-01 (AD) as 0001-12-31 (BC), which looks
-                            // like a valid date.
-                            return null;
-                        }
-
-                        // "SQL:2003 6.1 <data type> Access Rules 6" says that year is
-                        // between 1 and 9999, and days/months are the valid Gregorian
-                        // calendar values for these years.
-                        if (sign) {
-                            calendar.set(Calendar.YEAR, 9999);
-                            calendar.set(Calendar.MONTH, 11);
-                            calendar.set(Calendar.DAY_OF_MONTH, 31);
-                            calendar.set(Calendar.HOUR_OF_DAY, 23);
-                            calendar.set(Calendar.MINUTE, 59);
-                            calendar.set(Calendar.SECOND, 59);
-                            int millis =
-                                    (precision >= 3)
-                                            ? 999
-                                            : ((precision == 2)
-                                                    ? 990
-                                                    : ((precision == 1) ? 900 : 0));
-                            calendar.set(Calendar.MILLISECOND, millis);
-                        } else {
-                            calendar.set(Calendar.YEAR, 1);
-                            calendar.set(Calendar.MONTH, 0);
-                            calendar.set(Calendar.DAY_OF_MONTH, 1);
-                            calendar.set(Calendar.HOUR_OF_DAY, 0);
-                            calendar.set(Calendar.MINUTE, 0);
-                            calendar.set(Calendar.SECOND, 0);
-                            calendar.set(Calendar.MILLISECOND, 0);
-                        }
-                        break;
-                    default:
-                        break;
-                }
-                return calendar;
-
-            default:
-                throw Util.unexpected(this);
-        }
-    }
-
-    /**
-     * Returns the minimum precision (or length) allowed for this type, or -1 if precision/length
-     * are not applicable for this type.
-     *
-     * @return Minimum allowed precision
-     */
-    public int getMinPrecision() {
-        switch (this) {
-            case DECIMAL:
-            case VARCHAR:
-            case CHAR:
-            case VARBINARY:
-            case BINARY:
-            case TIME:
-            case TIME_WITH_LOCAL_TIME_ZONE:
-            case TIME_TZ:
-            case TIMESTAMP:
-            case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
-            case TIMESTAMP_TZ:
-                return 1;
-            case INTERVAL_YEAR:
-            case INTERVAL_YEAR_MONTH:
-            case INTERVAL_MONTH:
-            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:
-                return MIN_INTERVAL_START_PRECISION;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * Returns the minimum scale (or fractional second precision in the case of intervals) allowed
-     * for this type, or -1 if precision/length are not applicable for this type.
-     *
-     * @return Minimum allowed scale
-     */
-    public int getMinScale() {
-        switch (this) {
-            // TODO: Minimum numeric scale for decimal
-            case INTERVAL_YEAR:
-            case INTERVAL_YEAR_MONTH:
-            case INTERVAL_MONTH:
-            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:
-                return MIN_INTERVAL_FRACTIONAL_SECOND_PRECISION;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * Returns {@code HOUR} for {@code HOUR TO SECOND} and {@code HOUR}, {@code SECOND} for {@code
-     * SECOND}.
-     */
-    public TimeUnit getStartUnit() {
-        switch (this) {
-            case INTERVAL_YEAR:
-            case INTERVAL_YEAR_MONTH:
-                return TimeUnit.YEAR;
-            case INTERVAL_MONTH:
-                return TimeUnit.MONTH;
-            case INTERVAL_DAY:
-            case INTERVAL_DAY_HOUR:
-            case INTERVAL_DAY_MINUTE:
-            case INTERVAL_DAY_SECOND:
-                return TimeUnit.DAY;
-            case INTERVAL_HOUR:
-            case INTERVAL_HOUR_MINUTE:
-            case INTERVAL_HOUR_SECOND:
-                return TimeUnit.HOUR;
-            case INTERVAL_MINUTE:
-            case INTERVAL_MINUTE_SECOND:
-                return TimeUnit.MINUTE;
-            case INTERVAL_SECOND:
-                return TimeUnit.SECOND;
-            default:
-                throw new AssertionError(this);
-        }
-    }
-
-    /** Returns {@code SECOND} for both {@code HOUR TO SECOND} and {@code SECOND}. */
-    public TimeUnit getEndUnit() {
-        switch (this) {
-            case INTERVAL_YEAR:
-                return TimeUnit.YEAR;
-            case INTERVAL_YEAR_MONTH:
-            case INTERVAL_MONTH:
-                return TimeUnit.MONTH;
-            case INTERVAL_DAY:
-                return TimeUnit.DAY;
-            case INTERVAL_DAY_HOUR:
-            case INTERVAL_HOUR:
-                return TimeUnit.HOUR;
-            case INTERVAL_DAY_MINUTE:
-            case INTERVAL_HOUR_MINUTE:
-            case INTERVAL_MINUTE:
-                return TimeUnit.MINUTE;
-            case INTERVAL_DAY_SECOND:
-            case INTERVAL_HOUR_SECOND:
-            case INTERVAL_MINUTE_SECOND:
-            case INTERVAL_SECOND:
-                return TimeUnit.SECOND;
-            default:
-                throw new AssertionError(this);
-        }
-    }
-
-    public boolean isYearMonth() {
-        switch (this) {
-            case INTERVAL_YEAR:
-            case INTERVAL_YEAR_MONTH:
-            case INTERVAL_MONTH:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    /** Limit. */
-    public enum Limit {
-        ZERO,
-        UNDERFLOW,
-        OVERFLOW
-    }
-
-    private static @Nullable BigDecimal getNumericLimit(
-            int radix, int exponent, boolean sign, Limit limit, boolean beyond) {
-        switch (limit) {
-            case OVERFLOW:
-
-                // 2-based schemes run from -2^(N-1) to 2^(N-1)-1 e.g. -128 to +127
-                // 10-based schemas run from -(10^N-1) to 10^N-1 e.g. -99 to +99
-                final BigDecimal bigRadix = BigDecimal.valueOf(radix);
-                if (radix == 2) {
-                    --exponent;
-                }
-                BigDecimal decimal = bigRadix.pow(exponent);
-                if (sign || (radix != 2)) {
-                    decimal = decimal.subtract(BigDecimal.ONE);
-                }
-                if (beyond) {
-                    decimal = decimal.add(BigDecimal.ONE);
-                }
-                if (!sign) {
-                    decimal = decimal.negate();
-                }
-                return decimal;
-            case UNDERFLOW:
-                return beyond ? null : (sign ? BigDecimal.ONE : BigDecimal.ONE.negate());
-            case ZERO:
-                return BigDecimal.ZERO;
-            default:
-                throw Util.unexpected(limit);
-        }
-    }
-
-    public SqlLiteral createLiteral(Object o, SqlParserPos pos) {
-        switch (this) {
-            case BOOLEAN:
-                return SqlLiteral.createBoolean((Boolean) o, pos);
-            case TINYINT:
-            case SMALLINT:
-            case INTEGER:
-            case BIGINT:
-            case DECIMAL:
-                return SqlLiteral.createExactNumeric(o.toString(), pos);
-            case VARCHAR:
-            case CHAR:
-                return SqlLiteral.createCharString((String) o, pos);
-            case VARBINARY:
-            case BINARY:
-                return SqlLiteral.createBinaryString((byte[]) o, pos);
-            case DATE:
-                return SqlLiteral.createDate(
-                        o instanceof Calendar
-                                ? DateString.fromCalendarFields((Calendar) o)
-                                : (DateString) o,
-                        pos);
-            case TIME:
-                return SqlLiteral.createTime(
-                        o instanceof Calendar
-                                ? TimeString.fromCalendarFields((Calendar) o)
-                                : (TimeString) o,
-                        0 /* todo */,
-                        pos);
-            case TIMESTAMP:
-                return SqlLiteral.createTimestamp(
-                        this,
-                        o instanceof Calendar
-                                ? TimestampString.fromCalendarFields((Calendar) o)
-                                : (TimestampString) o,
-                        0 /* todo */,
-                        pos);
-            default:
-                throw Util.unexpected(this);
-        }
-    }
-
-    /** Returns the name of this type. */
-    public String getName() {
-        return name();
-    }
-
-    /**
-     * Returns the name of this type, with underscores converted to spaces, for example "TIMESTAMP
-     * WITH LOCAL TIME ZONE", "DATE".
-     */
-    public String getSpaceName() {
-        return name().replace('_', ' ');
-    }
-
-    /**
-     * Flags indicating precision/scale combinations.
-     *
-     * <p>Note: for intervals:
-     *
-     * <ul>
-     *   <li>precision = start (leading field) precision
-     *   <li>scale = fractional second precision
-     * </ul>
-     */
-    private interface PrecScale {
-        int NO_NO = 1;
-        int YES_NO = 2;
-        int YES_YES = 4;
-    }
-}
diff --git a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/validate/FlinkSqlConformance.java b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/validate/FlinkSqlConformance.java
index a7a58c9958d..696cdd5edcd 100644
--- a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/validate/FlinkSqlConformance.java
+++ b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/validate/FlinkSqlConformance.java
@@ -67,6 +67,11 @@ public enum FlinkSqlConformance implements SqlConformance {
         return false;
     }
 
+    @Override
+    public boolean isSupportedDualTable() {
+        return SqlConformanceEnum.DEFAULT.isSupportedDualTable();
+    }
+
     @Override
     public boolean isFromRequired() {
         return false;
@@ -117,6 +122,11 @@ public enum FlinkSqlConformance implements SqlConformance {
         return true;
     }
 
+    @Override
+    public boolean allowNiladicConstantWithoutParentheses() {
+        return true;
+    }
+
     @Override
     public boolean allowExplicitRowValueConstructor() {
         return true;
@@ -172,6 +182,11 @@ public enum FlinkSqlConformance implements SqlConformance {
         return SqlConformanceEnum.DEFAULT.allowLenientCoercion();
     }
 
+    @Override
+    public boolean checkedArithmetic() {
+        return SqlConformanceEnum.DEFAULT.checkedArithmetic();
+    }
+
     @Override
     public boolean isValueAllowed() {
         return true;
diff --git a/flink-table/flink-table-calcite-bridge/pom.xml b/flink-table/flink-table-calcite-bridge/pom.xml
index de1a516e2af..76652464101 100644
--- a/flink-table/flink-table-calcite-bridge/pom.xml
+++ b/flink-table/flink-table-calcite-bridge/pom.xml
@@ -45,12 +45,12 @@ under the License.
 			<version>${calcite.version}</version>
 			<exclusions>
 				<!--
-				"mvn dependency:tree" as of Calcite 1.38.0:
-				[INFO] +- org.apache.calcite:calcite-core:jar:1.38.0:compile
-				[INFO] |  +- org.apache.calcite:calcite-linq4j:jar:1.38.0:compile
+				"mvn dependency:tree" as of Calcite 1.39.0:
+				[INFO] +- org.apache.calcite:calcite-core:jar:1.39.0:compile
+				[INFO] |  +- org.apache.calcite:calcite-linq4j:jar:1.39.0:compile
 				[INFO] |  +- org.locationtech.jts:jts-core:jar:1.19.0:compile
 				[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.21:compile
-				[INFO] |  +- org.apache.calcite.avatica:avatica-core:jar:1.25.0:compile
+				[INFO] |  +- org.apache.calcite.avatica:avatica-core:jar:1.26.0:compile
 				[INFO] |  +- org.apiguardian:apiguardian-api:jar:1.1.2:compile
 				[INFO] |  +- com.fasterxml.jackson.core:jackson-core:jar:2.21.3:compile
 				[INFO] |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.21.3:compile
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
index 864d50ef5c9..d04374dc546 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
@@ -133,38 +133,8 @@ public class SimpleCalciteSchema extends CalciteSchema {
     }
 
     @Override
-    protected @Nullable CalciteSchema getImplicitSubSchema(
-            String schemaName, boolean caseSensitive) {
-        // Check implicit schemas.
-        final String schemaName2 =
-                caseSensitive
-                        ? schemaName
-                        : caseInsensitiveLookup(schema.getSubSchemaNames(), schemaName);
-        if (schemaName2 == null) {
-            return null;
-        }
-        final Schema s = schema.getSubSchema(schemaName2);
-        if (s == null) {
-            return null;
-        }
-        return new SimpleCalciteSchema(this, s, schemaName2);
-    }
-
-    @Override
-    protected @Nullable TableEntry getImplicitTable(String tableName, boolean caseSensitive) {
-        // Check implicit tables.
-        final String tableName2 =
-                caseSensitive
-                        ? tableName
-                        : caseInsensitiveLookup(schema.getTableNames(), tableName);
-        if (tableName2 == null) {
-            return null;
-        }
-        final Table table = schema.getTable(tableName2);
-        if (table == null) {
-            return null;
-        }
-        return tableEntry(tableName2, table);
+    protected CalciteSchema createSubSchema(Schema schema, String name) {
+        return new SimpleCalciteSchema(this, schema, name);
     }
 
     // ----- FLINK MODIFICATION START -----
@@ -201,28 +171,6 @@ public class SimpleCalciteSchema extends CalciteSchema {
         return typeEntry(name2, type);
     }
 
-    @Override
-    protected void addImplicitSubSchemaToBuilder(
-            ImmutableSortedMap.Builder<String, CalciteSchema> builder) {
-        ImmutableSortedMap<String, CalciteSchema> explicitSubSchemas = builder.build();
-        for (String schemaName : schema.getSubSchemaNames()) {
-            if (explicitSubSchemas.containsKey(schemaName)) {
-                // explicit subschema wins.
-                continue;
-            }
-            Schema s = schema.getSubSchema(schemaName);
-            if (s != null) {
-                CalciteSchema calciteSchema = new SimpleCalciteSchema(this, s, schemaName);
-                builder.put(schemaName, calciteSchema);
-            }
-        }
-    }
-
-    @Override
-    protected void addImplicitTableToBuilder(ImmutableSortedSet.Builder<String> builder) {
-        builder.addAll(schema.getTableNames());
-    }
-
     @Override
     protected void addImplicitFunctionsToBuilder(
             ImmutableList.Builder<Function> builder, String name, boolean caseSensitive) {
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/plan/RelOptUtil.java
deleted file mode 100644
index 4cb6744d859..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ /dev/null
@@ -1,4596 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.plan;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import org.apache.calcite.adapter.enumerable.EnumerableRules;
-import org.apache.calcite.avatica.AvaticaConnection;
-import org.apache.calcite.config.CalciteSystemProperty;
-import org.apache.calcite.interpreter.Bindables;
-import org.apache.calcite.linq4j.Ord;
-import org.apache.calcite.linq4j.function.Experimental;
-import org.apache.calcite.rel.RelCollations;
-import org.apache.calcite.rel.RelHomogeneousShuttle;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.RelShuttle;
-import org.apache.calcite.rel.RelVisitor;
-import org.apache.calcite.rel.RelWriter;
-import org.apache.calcite.rel.core.Aggregate;
-import org.apache.calcite.rel.core.AggregateCall;
-import org.apache.calcite.rel.core.Calc;
-import org.apache.calcite.rel.core.CorrelationId;
-import org.apache.calcite.rel.core.Filter;
-import org.apache.calcite.rel.core.Join;
-import org.apache.calcite.rel.core.JoinRelType;
-import org.apache.calcite.rel.core.Project;
-import org.apache.calcite.rel.core.RelFactories;
-import org.apache.calcite.rel.core.Sort;
-import org.apache.calcite.rel.core.TableScan;
-import org.apache.calcite.rel.externalize.RelDotWriter;
-import org.apache.calcite.rel.externalize.RelJsonWriter;
-import org.apache.calcite.rel.externalize.RelWriterImpl;
-import org.apache.calcite.rel.externalize.RelXmlWriter;
-import org.apache.calcite.rel.hint.HintStrategyTable;
-import org.apache.calcite.rel.hint.Hintable;
-import org.apache.calcite.rel.hint.RelHint;
-import org.apache.calcite.rel.logical.LogicalAggregate;
-import org.apache.calcite.rel.logical.LogicalAsofJoin;
-import org.apache.calcite.rel.logical.LogicalCalc;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.logical.LogicalJoin;
-import org.apache.calcite.rel.logical.LogicalProject;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rel.rules.CoreRules;
-import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
-import org.apache.calcite.rel.rules.MultiJoin;
-import org.apache.calcite.rel.rules.ProjectMergeRule;
-import org.apache.calcite.rel.stream.StreamRules;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
-import org.apache.calcite.rex.LogicVisitor;
-import org.apache.calcite.rex.RexBuilder;
-import org.apache.calcite.rex.RexCall;
-import org.apache.calcite.rex.RexCorrelVariable;
-import org.apache.calcite.rex.RexExecutor;
-import org.apache.calcite.rex.RexExecutorImpl;
-import org.apache.calcite.rex.RexFieldAccess;
-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.RexProgram;
-import org.apache.calcite.rex.RexShuttle;
-import org.apache.calcite.rex.RexSqlStandardConvertletTable;
-import org.apache.calcite.rex.RexSubQuery;
-import org.apache.calcite.rex.RexToSqlNodeConverter;
-import org.apache.calcite.rex.RexToSqlNodeConverterImpl;
-import org.apache.calcite.rex.RexUtil;
-import org.apache.calcite.rex.RexVisitorImpl;
-import org.apache.calcite.runtime.CalciteContextException;
-import org.apache.calcite.runtime.PairList;
-import org.apache.calcite.schema.ModifiableView;
-import org.apache.calcite.sql.SqlExplainFormat;
-import org.apache.calcite.sql.SqlExplainLevel;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlLiteral;
-import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.calcite.sql.type.MultisetSqlType;
-import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.sql.type.SqlTypeUtil;
-import org.apache.calcite.tools.RelBuilder;
-import org.apache.calcite.tools.RelBuilderFactory;
-import org.apache.calcite.util.ImmutableBitSet;
-import org.apache.calcite.util.Litmus;
-import org.apache.calcite.util.Pair;
-import org.apache.calcite.util.Permutation;
-import org.apache.calcite.util.Util;
-import org.apache.calcite.util.mapping.Mapping;
-import org.apache.calcite.util.mapping.MappingType;
-import org.apache.calcite.util.mapping.Mappings;
-import org.checkerframework.checker.initialization.qual.NotOnlyInitialized;
-import org.checkerframework.checker.initialization.qual.UnknownInitialization;
-import org.checkerframework.checker.nullness.qual.Nullable;
-import org.checkerframework.checker.nullness.qual.PolyNull;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.AbstractList;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-import static org.apache.calcite.rel.type.RelDataTypeImpl.NON_NULLABLE_SUFFIX;
-
-/**
- * <code>RelOptUtil</code> defines static utility methods for use in optimizing {@link RelNode}s.
- *
- * <p>FLINK modifications (backport of CALCITE-6764): Lines 2074 ~ 2106
- */
-public abstract class RelOptUtil {
-    // ~ Static fields/initializers ---------------------------------------------
-
-    public static final double EPSILON = 1.0e-5;
-
-    /**
-     * Default amount by which the complexity of a {@link Project} or {@link Filter} may increase
-     * when applying a rule. (Complexity is, roughly, the number of {@link RexNode}s in all
-     * expressions.)
-     *
-     * @see ProjectMergeRule.Config#bloat()
-     * @see FilterProjectTransposeRule.Config#bloat()
-     * @see RelBuilder.Config#bloat()
-     */
-    public static final int DEFAULT_BLOAT = 100;
-
-    @SuppressWarnings("Guava")
-    @Deprecated // to be removed before 2.0
-    public static final com.google.common.base.Predicate<Filter> FILTER_PREDICATE =
-            f -> !f.containsOver();
-
-    @SuppressWarnings("Guava")
-    @Deprecated // to be removed before 2.0
-    public static final com.google.common.base.Predicate<Project> PROJECT_PREDICATE =
-            RelOptUtil::notContainsWindowedAgg;
-
-    @SuppressWarnings("Guava")
-    @Deprecated // to be removed before 2.0
-    public static final com.google.common.base.Predicate<Calc> CALC_PREDICATE =
-            RelOptUtil::notContainsWindowedAgg;
-
-    // ~ Methods ----------------------------------------------------------------
-
-    /** Whether this node is a limit without sort specification. */
-    public static boolean isPureLimit(RelNode rel) {
-        return isLimit(rel) && !isOrder(rel);
-    }
-
-    /** Whether this node is a sort without limit specification. */
-    public static boolean isPureOrder(RelNode rel) {
-        return !isLimit(rel) && isOrder(rel);
-    }
-
-    /** Whether this node contains a limit specification. */
-    public static boolean isLimit(RelNode rel) {
-        return (rel instanceof Sort) && ((Sort) rel).fetch != null;
-    }
-
-    /** Whether this node contains a sort specification. */
-    public static boolean isOrder(RelNode rel) {
-        return (rel instanceof Sort) && !((Sort) rel).getCollation().getFieldCollations().isEmpty();
-    }
-
-    /** Whether this node contains a offset specification. */
-    public static boolean isOffset(RelNode rel) {
-        return (rel instanceof Sort) && ((Sort) rel).offset != null;
-    }
-
-    /** Returns a set of tables used by this expression or its children. */
-    public static Set<RelOptTable> findTables(RelNode rel) {
-        return new LinkedHashSet<>(findAllTables(rel));
-    }
-
-    /** Returns a list of all tables used by this expression or its children. */
-    public static List<RelOptTable> findAllTables(RelNode rel) {
-        final Multimap<Class<? extends RelNode>, RelNode> nodes =
-                rel.getCluster().getMetadataQuery().getNodeTypes(rel);
-        final List<RelOptTable> usedTables = new ArrayList<>();
-        if (nodes == null) {
-            return usedTables;
-        }
-        for (Map.Entry<Class<? extends RelNode>, Collection<RelNode>> e :
-                nodes.asMap().entrySet()) {
-            if (TableScan.class.isAssignableFrom(e.getKey())) {
-                for (RelNode node : e.getValue()) {
-                    TableScan scan = (TableScan) node;
-                    usedTables.add(scan.getTable());
-                }
-            }
-        }
-        return usedTables;
-    }
-
-    /** Returns a list of all table qualified names used by this expression or its children. */
-    public static List<String> findAllTableQualifiedNames(RelNode rel) {
-        return findAllTables(rel).stream()
-                .map(table -> table.getQualifiedName().toString())
-                .collect(Collectors.toList());
-    }
-
-    /** Returns a list of variables set by a relational expression or its descendants. */
-    public static Set<CorrelationId> getVariablesSet(RelNode rel) {
-        VariableSetVisitor visitor = new VariableSetVisitor();
-        go(visitor, rel);
-        return visitor.variables;
-    }
-
-    @Deprecated // to be removed before 2.0
-    @SuppressWarnings("MixedMutabilityReturnType")
-    public static List<CorrelationId> getVariablesSetAndUsed(RelNode rel0, RelNode rel1) {
-        Set<CorrelationId> set = getVariablesSet(rel0);
-        if (set.isEmpty()) {
-            return ImmutableList.of();
-        }
-        Set<CorrelationId> used = getVariablesUsed(rel1);
-        if (used.isEmpty()) {
-            return ImmutableList.of();
-        }
-        final List<CorrelationId> result = new ArrayList<>();
-        for (CorrelationId s : set) {
-            if (used.contains(s) && !result.contains(s)) {
-                result.add(s);
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Returns the set of variables used by a relational expression or its descendants.
-     *
-     * <p>The set may contain "duplicates" (variables with different ids that, when resolved, will
-     * reference the same source relational expression).
-     *
-     * <p>The item type is the same as {@link org.apache.calcite.rex.RexCorrelVariable#id}.
-     */
-    public static Set<CorrelationId> getVariablesUsed(RelNode rel) {
-        CorrelationCollector visitor = new CorrelationCollector();
-        rel.accept(visitor);
-        return visitor.vuv.variables;
-    }
-
-    /**
-     * Returns the set of variables used by the given list of sub-queries and its descendants.
-     *
-     * @param subQueries The sub-queries containing correlation variables
-     * @return A list of correlation identifiers found within the sub-queries. The type of the
-     *     [CorrelationId] parameter corresponds to {@link
-     *     org.apache.calcite.rex.RexCorrelVariable#id}.
-     */
-    public static Set<CorrelationId> getVariablesUsed(List<RexSubQuery> subQueries) {
-        // Internally this function calls getVariablesUsed on a RelNode to get all the
-        // correlated variables in that RelNode
-        Set<CorrelationId> correlationIds = new HashSet<>();
-        for (RexSubQuery subQ : subQueries) {
-            correlationIds.addAll(getVariablesUsed(subQ.rel));
-        }
-        return correlationIds;
-    }
-
-    /** Finds which columns of a correlation variable are used within a relational expression. */
-    public static ImmutableBitSet correlationColumns(CorrelationId id, RelNode rel) {
-        final CorrelationCollector collector = new CorrelationCollector();
-        rel.accept(collector);
-        final ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
-        for (int field : collector.vuv.variableFields.get(id)) {
-            if (field >= 0) {
-                builder.set(field);
-            }
-        }
-        return builder.build();
-    }
-
-    /**
-     * Returns true, and calls {@link Litmus#succeed()} if a given relational expression does not
-     * contain a given correlation.
-     */
-    public static boolean notContainsCorrelation(
-            RelNode r, CorrelationId correlationId, Litmus litmus) {
-        final Set<CorrelationId> set = getVariablesUsed(r);
-        if (!set.contains(correlationId)) {
-            return litmus.succeed();
-        } else {
-            return litmus.fail("contains {}", correlationId);
-        }
-    }
-
-    /** Sets a {@link RelVisitor} going on a given relational expression, and returns the result. */
-    public static void go(RelVisitor visitor, RelNode p) {
-        try {
-            visitor.go(p);
-        } catch (Exception e) {
-            throw new RuntimeException("while visiting tree", e);
-        }
-    }
-
-    /**
-     * Returns a list of the types of the fields in a given struct type. The list is immutable.
-     *
-     * @param type Struct type
-     * @return List of field types
-     * @see org.apache.calcite.rel.type.RelDataType#getFieldNames()
-     */
-    public static List<RelDataType> getFieldTypeList(final RelDataType type) {
-        return Util.transform(type.getFieldList(), RelDataTypeField::getType);
-    }
-
-    public static boolean areRowTypesEqual(
-            RelDataType rowType1, RelDataType rowType2, boolean compareNames) {
-        if (rowType1 == rowType2) {
-            return true;
-        }
-        if (compareNames) {
-            // if types are not identity-equal, then either the names or
-            // the types must be different
-            return false;
-        }
-        if (rowType2.getFieldCount() != rowType1.getFieldCount()) {
-            return false;
-        }
-        final List<RelDataTypeField> f1 = rowType1.getFieldList();
-        final List<RelDataTypeField> f2 = rowType2.getFieldList();
-        for (Pair<RelDataTypeField, RelDataTypeField> pair : Pair.zip(f1, f2)) {
-            final RelDataType type1 = pair.left.getType();
-            final RelDataType type2 = pair.right.getType();
-            // If one of the types is ANY comparison should succeed
-            if (type1.getSqlTypeName() == SqlTypeName.ANY
-                    || type2.getSqlTypeName() == SqlTypeName.ANY) {
-                continue;
-            }
-            if (!type1.equals(type2)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Verifies that a row type being added to an equivalence class matches the existing type,
-     * raising an assertion if this is not the case.
-     *
-     * @param originalRel canonical rel for equivalence class
-     * @param newRel rel being added to equivalence class
-     * @param equivalenceClass object representing equivalence class
-     */
-    public static void verifyTypeEquivalence(
-            RelNode originalRel, RelNode newRel, Object equivalenceClass) {
-        RelDataType expectedRowType = originalRel.getRowType();
-        RelDataType actualRowType = newRel.getRowType();
-
-        // Row types must be the same, except for field names.
-        if (areRowTypesEqual(expectedRowType, actualRowType, false)) {
-            return;
-        }
-
-        String s =
-                "Cannot add expression of different type to set:\n"
-                        + "set type is "
-                        + expectedRowType.getFullTypeString()
-                        + "\nexpression type is "
-                        + actualRowType.getFullTypeString()
-                        + "\nset is "
-                        + equivalenceClass
-                        + "\nexpression is "
-                        + RelOptUtil.toString(newRel)
-                        + getFullTypeDifferenceString(
-                                "rowtype of original rel",
-                                expectedRowType,
-                                "rowtype of new rel",
-                                actualRowType);
-        throw new AssertionError(s);
-    }
-
-    /**
-     * Copy the {@link org.apache.calcite.rel.hint.RelHint}s from {@code originalRel} to {@code
-     * newRel} if both of them are {@link Hintable}.
-     *
-     * <p>The two relational expressions are assumed as semantically equivalent, that means the
-     * hints should be attached to the relational expression that expects to have them.
-     *
-     * <p>Try to propagate the hints to the first relational expression that matches, this is needed
-     * because many planner rules would generate a sub-tree whose root rel type is different with
-     * the original matched rel.
-     *
-     * <p>For the worst case, there is no relational expression that can apply these hints, and the
-     * whole sub-tree would be visited. We add a protection here: if the visiting depth is over than
-     * 3, just returns, because there are rare cases the new created sub-tree has layers bigger than
-     * that.
-     *
-     * <p>This is a best effort, we do not know exactly how the nodes are transformed in all kinds
-     * of planner rules, so for some complex relational expressions, the hints would very probably
-     * lost.
-     *
-     * <p>This function is experimental and would change without any notes.
-     *
-     * @param originalRel Original relational expression
-     * @param equiv New equivalent relational expression
-     * @return A copy of {@code newRel} with attached qualified hints from {@code originalRel}, or
-     *     {@code newRel} directly if one of them are not {@link Hintable}
-     */
-    @Experimental
-    public static RelNode propagateRelHints(RelNode originalRel, RelNode equiv) {
-        if (!(originalRel instanceof Hintable) || ((Hintable) originalRel).getHints().isEmpty()) {
-            return equiv;
-        }
-        final RelShuttle shuttle =
-                new SubTreeHintPropagateShuttle(
-                        originalRel.getCluster().getHintStrategies(),
-                        ((Hintable) originalRel).getHints());
-        return equiv.accept(shuttle);
-    }
-
-    /**
-     * Propagates the relational expression hints from root node to leaf node.
-     *
-     * @param rel The relational expression
-     * @param reset Flag saying if to reset the existing hints before the propagation
-     * @return New relational expression with hints propagated
-     */
-    public static RelNode propagateRelHints(RelNode rel, boolean reset) {
-        if (reset) {
-            rel = rel.accept(new ResetHintsShuttle());
-        }
-        final RelShuttle shuttle =
-                new RelHintPropagateShuttle(rel.getCluster().getHintStrategies());
-        return rel.accept(shuttle);
-    }
-
-    /**
-     * Copy the {@link org.apache.calcite.rel.hint.RelHint}s from {@code originalRel} to {@code
-     * newRel} if both of them are {@link Hintable}.
-     *
-     * <p>The hints would be attached directly(e.g. without any filtering).
-     *
-     * @param originalRel Original relational expression
-     * @param newRel New relational expression
-     * @return A copy of {@code newRel} with attached hints from {@code originalRel}, or {@code
-     *     newRel} directly if one of them are not {@link Hintable}
-     */
-    public static RelNode copyRelHints(RelNode originalRel, RelNode newRel) {
-        return copyRelHints(originalRel, newRel, false);
-    }
-
-    /**
-     * Copy the {@link org.apache.calcite.rel.hint.RelHint}s from {@code originalRel} to {@code
-     * newRel} if both of them are {@link Hintable}.
-     *
-     * <p>The hints would be filtered by the specified hint strategies if {@code filterHints} is
-     * true.
-     *
-     * @param originalRel Original relational expression
-     * @param newRel New relational expression
-     * @param filterHints Flag saying if to filter out unqualified hints for {@code newRel}
-     * @return A copy of {@code newRel} with attached hints from {@code originalRel}, or {@code
-     *     newRel} directly if one of them are not {@link Hintable}
-     */
-    public static RelNode copyRelHints(RelNode originalRel, RelNode newRel, boolean filterHints) {
-        if (originalRel == newRel && !filterHints) {
-            return originalRel;
-        }
-
-        if (originalRel instanceof Hintable
-                && newRel instanceof Hintable
-                && !((Hintable) originalRel).getHints().isEmpty()) {
-            final List<RelHint> hints = ((Hintable) originalRel).getHints();
-            if (filterHints) {
-                HintStrategyTable hintStrategies = originalRel.getCluster().getHintStrategies();
-                return ((Hintable) newRel).attachHints(hintStrategies.apply(hints, newRel));
-            } else {
-                // Keep all the hints if filterHints is false for 2 reasons:
-                // 1. Keep sync with the hints propagation logic,
-                // see RelHintPropagateShuttle for details.
-                // 2. We may re-propagate these hints when decorrelating a query.
-                return ((Hintable) newRel).attachHints(hints);
-            }
-        }
-        return newRel;
-    }
-
-    /**
-     * Returns a permutation describing where output fields come from. In the returned map, value of
-     * {@code map.getTargetOpt(i)} is {@code n} if field {@code i} projects input field {@code n} or
-     * applies a cast on {@code n}, -1 if it is another expression.
-     */
-    public static Mappings.TargetMapping permutationIgnoreCast(
-            List<RexNode> nodes, RelDataType inputRowType) {
-        final Mappings.TargetMapping mapping =
-                Mappings.create(
-                        MappingType.PARTIAL_FUNCTION, nodes.size(), inputRowType.getFieldCount());
-        for (Ord<RexNode> node : Ord.zip(nodes)) {
-            if (node.e instanceof RexInputRef) {
-                mapping.set(node.i, ((RexInputRef) node.e).getIndex());
-            } else if (node.e.isA(SqlKind.CAST)) {
-                final RexNode operand = ((RexCall) node.e).getOperands().get(0);
-                if (operand instanceof RexInputRef) {
-                    mapping.set(node.i, ((RexInputRef) operand).getIndex());
-                }
-            }
-        }
-        return mapping;
-    }
-
-    /**
-     * Returns a permutation describing where output fields come from. In the returned map, value of
-     * {@code map.getTargetOpt(i)} is {@code n} if field {@code i} projects input field {@code n},
-     * -1 if it is an expression.
-     */
-    public static Mappings.TargetMapping permutation(
-            List<RexNode> nodes, RelDataType inputRowType) {
-        final Mappings.TargetMapping mapping =
-                Mappings.create(
-                        MappingType.PARTIAL_FUNCTION, nodes.size(), inputRowType.getFieldCount());
-        for (Ord<RexNode> node : Ord.zip(nodes)) {
-            if (node.e instanceof RexInputRef) {
-                mapping.set(node.i, ((RexInputRef) node.e).getIndex());
-            }
-        }
-        return mapping;
-    }
-
-    /**
-     * Returns a permutation describing where the Project's fields come from after the Project is
-     * pushed down.
-     */
-    public static Mappings.TargetMapping permutationPushDownProject(
-            List<RexNode> nodes, RelDataType inputRowType, int sourceOffset, int targetOffset) {
-        final Mappings.TargetMapping mapping =
-                Mappings.create(
-                        MappingType.PARTIAL_FUNCTION,
-                        inputRowType.getFieldCount() + sourceOffset,
-                        nodes.size() + targetOffset);
-        for (Ord<RexNode> node : Ord.zip(nodes)) {
-            if (node.e instanceof RexInputRef) {
-                mapping.set(
-                        ((RexInputRef) node.e).getIndex() + sourceOffset, node.i + targetOffset);
-            }
-        }
-        return mapping;
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createExistsPlan(
-            RelOptCluster cluster,
-            RelNode seekRel,
-            @Nullable List<RexNode> conditions,
-            @Nullable RexLiteral extraExpr,
-            @Nullable String extraName) {
-        assert extraExpr == null || extraName != null;
-        RelNode ret = seekRel;
-
-        if (conditions != null && !conditions.isEmpty()) {
-            RexNode conditionExp =
-                    RexUtil.composeConjunction(cluster.getRexBuilder(), conditions, true);
-
-            if (conditionExp != null) {
-                final RelFactories.FilterFactory factory = RelFactories.DEFAULT_FILTER_FACTORY;
-                ret = factory.createFilter(ret, conditionExp, ImmutableSet.of());
-            }
-        }
-
-        if (extraExpr != null) {
-            RexBuilder rexBuilder = cluster.getRexBuilder();
-
-            assert extraExpr == rexBuilder.makeLiteral(true);
-
-            // this should only be called for the exists case
-            // first stick an Agg on top of the sub-query
-            // agg does not like no agg functions so just pretend it is
-            // doing a min(TRUE)
-
-            final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(cluster, null);
-            ret =
-                    relBuilder
-                            .push(ret)
-                            .project(extraExpr)
-                            .aggregate(
-                                    relBuilder.groupKey(),
-                                    relBuilder.min(relBuilder.field(0)).as(extraName))
-                            .build();
-        }
-
-        return ret;
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static Exists createExistsPlan(
-            RelNode seekRel, SubQueryType subQueryType, Logic logic, boolean notIn) {
-        final RelBuilder relBuilder =
-                RelFactories.LOGICAL_BUILDER.create(seekRel.getCluster(), null);
-        return createExistsPlan(seekRel, subQueryType, logic, notIn, relBuilder);
-    }
-
-    /**
-     * Creates a plan suitable for use in <code>EXISTS</code> or <code>IN</code> statements.
-     *
-     * @see org.apache.calcite.sql2rel.SqlToRelConverter SqlToRelConverter#convertExists
-     * @param seekRel A query rel, for example the resulting rel from 'select * from emp' or 'values
-     *     (1,2,3)' or '('Foo', 34)'.
-     * @param subQueryType Sub-query type
-     * @param logic Whether to use 2- or 3-valued boolean logic
-     * @param notIn Whether the operator is NOT IN
-     * @param relBuilder Builder for relational expressions
-     * @return A pair of a relational expression which outer joins a boolean condition column, and a
-     *     numeric offset. The offset is 2 if column 0 is the number of rows and column 1 is the
-     *     number of rows with not-null keys; 0 otherwise.
-     */
-    public static Exists createExistsPlan(
-            RelNode seekRel,
-            SubQueryType subQueryType,
-            Logic logic,
-            boolean notIn,
-            RelBuilder relBuilder) {
-        switch (subQueryType) {
-            case SCALAR:
-                return new Exists(seekRel, false, true);
-            default:
-                break;
-        }
-
-        switch (logic) {
-            case TRUE_FALSE_UNKNOWN:
-            case UNKNOWN_AS_TRUE:
-                if (notIn && !containsNullableFields(seekRel)) {
-                    logic = Logic.TRUE_FALSE;
-                }
-                break;
-            default:
-                break;
-        }
-        RelNode ret = seekRel;
-        final RelOptCluster cluster = seekRel.getCluster();
-        final RexBuilder rexBuilder = cluster.getRexBuilder();
-        final int keyCount = ret.getRowType().getFieldCount();
-        final boolean outerJoin = notIn || logic == RelOptUtil.Logic.TRUE_FALSE_UNKNOWN;
-        if (!outerJoin) {
-            final LogicalAggregate aggregate =
-                    LogicalAggregate.create(
-                            ret,
-                            ImmutableList.of(),
-                            ImmutableBitSet.range(keyCount),
-                            null,
-                            ImmutableList.of());
-            return new Exists(aggregate, false, false);
-        }
-
-        // for IN/NOT IN, it needs to output the fields
-        final List<RexNode> exprs = new ArrayList<>();
-        if (subQueryType == SubQueryType.IN) {
-            for (int i = 0; i < keyCount; i++) {
-                exprs.add(rexBuilder.makeInputRef(ret, i));
-            }
-        }
-
-        final int projectedKeyCount = exprs.size();
-        exprs.add(rexBuilder.makeLiteral(true));
-
-        ret =
-                relBuilder
-                        .push(ret)
-                        .project(exprs)
-                        .aggregate(
-                                relBuilder.groupKey(ImmutableBitSet.range(projectedKeyCount)),
-                                relBuilder.min(relBuilder.field(projectedKeyCount)))
-                        .build();
-
-        switch (logic) {
-            case TRUE_FALSE_UNKNOWN:
-            case UNKNOWN_AS_TRUE:
-                return new Exists(ret, true, true);
-            default:
-                return new Exists(ret, false, true);
-        }
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createRenameRel(RelDataType outputType, RelNode rel) {
-        RelDataType inputType = rel.getRowType();
-        List<RelDataTypeField> inputFields = inputType.getFieldList();
-        int n = inputFields.size();
-
-        List<RelDataTypeField> outputFields = outputType.getFieldList();
-        assert outputFields.size() == n
-                : "rename: field count mismatch: in=" + inputType + ", out" + outputType;
-
-        final PairList<RexNode, String> renames = PairList.of();
-        final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
-        Pair.forEach(
-                inputFields,
-                outputFields,
-                (inputField, outputField) -> {
-                    assert inputField.getType().equals(outputField.getType());
-                    renames.add(
-                            rexBuilder.makeInputRef(inputField.getType(), inputField.getIndex()),
-                            outputField.getName());
-                });
-        final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(rel.getCluster(), null);
-        return relBuilder.push(rel).project(renames.leftList(), renames.rightList(), true).build();
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createFilter(RelNode child, RexNode condition) {
-        final RelFactories.FilterFactory factory = RelFactories.DEFAULT_FILTER_FACTORY;
-        return factory.createFilter(child, condition, ImmutableSet.of());
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createFilter(
-            RelNode child, RexNode condition, RelFactories.FilterFactory filterFactory) {
-        return filterFactory.createFilter(child, condition, ImmutableSet.of());
-    }
-
-    /**
-     * Creates a filter, using the default filter factory, or returns the original relational
-     * expression if the condition is trivial.
-     */
-    public static RelNode createFilter(RelNode child, Iterable<? extends RexNode> conditions) {
-        return createFilter(child, conditions, RelFactories.DEFAULT_FILTER_FACTORY);
-    }
-
-    /**
-     * Creates a filter using the default factory, or returns the original relational expression if
-     * the condition is trivial.
-     */
-    public static RelNode createFilter(
-            RelNode child,
-            Iterable<? extends RexNode> conditions,
-            RelFactories.FilterFactory filterFactory) {
-        final RelOptCluster cluster = child.getCluster();
-        final RexNode condition =
-                RexUtil.composeConjunction(cluster.getRexBuilder(), conditions, true);
-        if (condition == null) {
-            return child;
-        } else {
-            return filterFactory.createFilter(child, condition, ImmutableSet.of());
-        }
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createNullFilter(RelNode rel, Integer[] fieldOrdinals) {
-        RexNode condition = null;
-        final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
-        RelDataType rowType = rel.getRowType();
-        int n;
-        if (fieldOrdinals != null) {
-            n = fieldOrdinals.length;
-        } else {
-            n = rowType.getFieldCount();
-        }
-        List<RelDataTypeField> fields = rowType.getFieldList();
-        for (int i = 0; i < n; ++i) {
-            int iField;
-            if (fieldOrdinals != null) {
-                iField = fieldOrdinals[i];
-            } else {
-                iField = i;
-            }
-            RelDataType type = fields.get(iField).getType();
-            if (!type.isNullable()) {
-                continue;
-            }
-            RexNode newCondition =
-                    rexBuilder.makeCall(
-                            SqlStdOperatorTable.IS_NOT_NULL, rexBuilder.makeInputRef(type, iField));
-            if (condition == null) {
-                condition = newCondition;
-            } else {
-                condition = rexBuilder.makeCall(SqlStdOperatorTable.AND, condition, newCondition);
-            }
-        }
-        if (condition == null) {
-            // no filtering required
-            return rel;
-        }
-
-        final RelFactories.FilterFactory factory = RelFactories.DEFAULT_FILTER_FACTORY;
-        return factory.createFilter(rel, condition, ImmutableSet.of());
-    }
-
-    /**
-     * Creates a projection which casts a rel's output to a desired row type.
-     *
-     * <p>No need to create new projection if {@code rel} is already a project, instead, create a
-     * projection with the input of {@code rel} and the new cast expressions.
-     *
-     * <p>The desired row type and the row type to be converted must have the same number of fields.
-     *
-     * @param rel producer of rows to be converted
-     * @param castRowType row type after cast
-     * @param rename if true, use field names from castRowType; if false, preserve field names from
-     *     rel
-     * @return conversion rel
-     */
-    public static RelNode createCastRel(
-            final RelNode rel, RelDataType castRowType, boolean rename) {
-        return createCastRel(rel, castRowType, rename, RelFactories.DEFAULT_PROJECT_FACTORY);
-    }
-
-    /**
-     * Creates a projection which casts a rel's output to a desired row type.
-     *
-     * <p>No need to create new projection if {@code rel} is already a project, instead, create a
-     * projection with the input of {@code rel} and the new cast expressions.
-     *
-     * <p>The desired row type and the row type to be converted must have the same number of fields.
-     *
-     * @param rel producer of rows to be converted
-     * @param castRowType row type after cast
-     * @param rename if true, use field names from castRowType; if false, preserve field names from
-     *     rel
-     * @param projectFactory Project Factory
-     * @return conversion rel
-     */
-    public static RelNode createCastRel(
-            final RelNode rel,
-            RelDataType castRowType,
-            boolean rename,
-            RelFactories.ProjectFactory projectFactory) {
-        requireNonNull(projectFactory, "projectFactory");
-        RelDataType rowType = rel.getRowType();
-        if (areRowTypesEqual(rowType, castRowType, rename)) {
-            // nothing to do
-            return rel;
-        }
-        if (rowType.getFieldCount() != castRowType.getFieldCount()) {
-            throw new IllegalArgumentException(
-                    "Field counts are not equal: "
-                            + "rowType ["
-                            + rowType
-                            + "] castRowType ["
-                            + castRowType
-                            + "]");
-        }
-        final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
-        List<RexNode> castExps;
-        RelNode input;
-        List<RelHint> hints = ImmutableList.of();
-        Set<CorrelationId> correlationVariables;
-        if (rel instanceof Project) {
-            // No need to create another project node if the rel
-            // is already a project.
-            final Project project = (Project) rel;
-            castExps =
-                    RexUtil.generateCastExpressions(
-                            rexBuilder, castRowType, ((Project) rel).getProjects());
-            input = rel.getInput(0);
-            hints = project.getHints();
-            correlationVariables = project.getVariablesSet();
-        } else {
-            castExps = RexUtil.generateCastExpressions(rexBuilder, castRowType, rowType);
-            input = rel;
-            correlationVariables = ImmutableSet.of();
-        }
-        if (rename) {
-            // Use names and types from castRowType.
-            return projectFactory.createProject(
-                    input, hints, castExps, castRowType.getFieldNames(), correlationVariables);
-        } else {
-            // Use names from rowType, types from castRowType.
-            return projectFactory.createProject(
-                    input, hints, castExps, rowType.getFieldNames(), correlationVariables);
-        }
-    }
-
-    /** Gets all fields in an aggregate. */
-    public static Set<Integer> getAllFields(Aggregate aggregate) {
-        return getAllFields2(aggregate.getGroupSet(), aggregate.getAggCallList());
-    }
-
-    /** Gets all fields in an aggregate. */
-    public static Set<Integer> getAllFields2(
-            ImmutableBitSet groupSet, List<AggregateCall> aggCallList) {
-        final Set<Integer> allFields = new TreeSet<>(groupSet.asList());
-        for (AggregateCall aggregateCall : aggCallList) {
-            allFields.addAll(aggregateCall.getArgList());
-            if (aggregateCall.filterArg >= 0) {
-                allFields.add(aggregateCall.filterArg);
-            }
-            if (aggregateCall.distinctKeys != null) {
-                allFields.addAll(aggregateCall.distinctKeys.asList());
-            }
-            allFields.addAll(RelCollations.ordinals(aggregateCall.collation));
-        }
-        return allFields;
-    }
-
-    /**
-     * Creates a LogicalAggregate that removes all duplicates from the result of an underlying
-     * relational expression.
-     *
-     * @param rel underlying rel
-     * @return rel implementing SingleValueAgg
-     */
-    public static RelNode createSingleValueAggRel(RelOptCluster cluster, RelNode rel) {
-        final int aggCallCnt = rel.getRowType().getFieldCount();
-        final List<AggregateCall> aggCalls = new ArrayList<>();
-
-        for (int i = 0; i < aggCallCnt; i++) {
-            aggCalls.add(
-                    AggregateCall.create(
-                            SqlStdOperatorTable.SINGLE_VALUE,
-                            false,
-                            false,
-                            false,
-                            ImmutableList.of(),
-                            ImmutableList.of(i),
-                            -1,
-                            null,
-                            RelCollations.EMPTY,
-                            0,
-                            rel,
-                            null,
-                            null));
-        }
-
-        return LogicalAggregate.create(
-                rel, ImmutableList.of(), ImmutableBitSet.of(), null, aggCalls);
-    }
-
-    // CHECKSTYLE: IGNORE 1
-    /**
-     * @deprecated Use {@link RelBuilder#distinct()}.
-     */
-    @Deprecated // to be removed before 2.0
-    public static RelNode createDistinctRel(RelNode rel) {
-        return LogicalAggregate.create(
-                rel,
-                ImmutableList.of(),
-                ImmutableBitSet.range(rel.getRowType().getFieldCount()),
-                null,
-                ImmutableList.of());
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static boolean analyzeSimpleEquiJoin(LogicalJoin join, int[] joinFieldOrdinals) {
-        RexNode joinExp = join.getCondition();
-        if (joinExp.getKind() != SqlKind.EQUALS) {
-            return false;
-        }
-        RexCall binaryExpression = (RexCall) joinExp;
-        RexNode leftComparand = binaryExpression.operands.get(0);
-        RexNode rightComparand = binaryExpression.operands.get(1);
-        if (!(leftComparand instanceof RexInputRef)) {
-            return false;
-        }
-        if (!(rightComparand instanceof RexInputRef)) {
-            return false;
-        }
-
-        final int leftFieldCount = join.getLeft().getRowType().getFieldCount();
-        RexInputRef leftFieldAccess = (RexInputRef) leftComparand;
-        if (!(leftFieldAccess.getIndex() < leftFieldCount)) {
-            // left field must access left side of join
-            return false;
-        }
-
-        RexInputRef rightFieldAccess = (RexInputRef) rightComparand;
-        if (!(rightFieldAccess.getIndex() >= leftFieldCount)) {
-            // right field must access right side of join
-            return false;
-        }
-
-        joinFieldOrdinals[0] = leftFieldAccess.getIndex();
-        joinFieldOrdinals[1] = rightFieldAccess.getIndex() - leftFieldCount;
-        return true;
-    }
-
-    /**
-     * Splits out the equi-join components of a join condition, and returns what's left. For
-     * example, given the condition
-     *
-     * <blockquote>
-     *
-     * <code>L.A = R.X AND L.B = L.C AND (L.D = 5 OR L.E =
-     * R.Y)</code>
-     *
-     * </blockquote>
-     *
-     * <p>returns
-     *
-     * <ul>
-     *   <li>leftKeys = {A}
-     *   <li>rightKeys = {X}
-     *   <li>rest = L.B = L.C AND (L.D = 5 OR L.E = R.Y)
-     * </ul>
-     *
-     * @param left left input to join
-     * @param right right input to join
-     * @param condition join condition
-     * @param leftKeys The ordinals of the fields from the left input which are equi-join keys
-     * @param rightKeys The ordinals of the fields from the right input which are equi-join keys
-     * @param filterNulls List of boolean values for each join key position indicating whether the
-     *     operator filters out nulls or not. Value is true if the operator is EQUALS and false if
-     *     the operator is IS NOT DISTINCT FROM (or an expanded version). If <code>filterNulls
-     *     </code> is null, only join conditions with EQUALS operators are considered equi-join
-     *     components. Rest (including IS NOT DISTINCT FROM) are returned in remaining join
-     *     condition.
-     * @return remaining join filters that are not equijoins; may return a {@link RexLiteral} true,
-     *     but never null
-     */
-    public static RexNode splitJoinCondition(
-            RelNode left,
-            RelNode right,
-            RexNode condition,
-            List<Integer> leftKeys,
-            List<Integer> rightKeys,
-            @Nullable List<Boolean> filterNulls) {
-        final List<RexNode> nonEquiList = new ArrayList<>();
-
-        splitJoinCondition(left, right, condition, leftKeys, rightKeys, filterNulls, nonEquiList);
-
-        return RexUtil.composeConjunction(left.getCluster().getRexBuilder(), nonEquiList);
-    }
-
-    /**
-     * As {@link #splitJoinCondition(RelNode, RelNode, RexNode, List, List, List)}, but writes
-     * non-equi conditions to a conjunctive list.
-     */
-    public static void splitJoinCondition(
-            RelNode left,
-            RelNode right,
-            RexNode condition,
-            List<Integer> leftKeys,
-            List<Integer> rightKeys,
-            @Nullable List<Boolean> filterNulls,
-            List<RexNode> nonEquiList) {
-        splitJoinCondition(
-                left.getCluster().getRexBuilder(),
-                left.getRowType().getFieldCount(),
-                condition,
-                leftKeys,
-                rightKeys,
-                filterNulls,
-                nonEquiList);
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static boolean isEqui(RelNode left, RelNode right, RexNode condition) {
-        final List<Integer> leftKeys = new ArrayList<>();
-        final List<Integer> rightKeys = new ArrayList<>();
-        final List<Boolean> filterNulls = new ArrayList<>();
-        final List<RexNode> nonEquiList = new ArrayList<>();
-        splitJoinCondition(
-                left.getCluster().getRexBuilder(),
-                left.getRowType().getFieldCount(),
-                condition,
-                leftKeys,
-                rightKeys,
-                filterNulls,
-                nonEquiList);
-        return nonEquiList.isEmpty();
-    }
-
-    /**
-     * Splits out the equi-join (and optionally, a single non-equi) components of a join condition,
-     * and returns what's left. Projection might be required by the caller to provide join keys that
-     * are not direct field references.
-     *
-     * @param sysFieldList list of system fields
-     * @param leftRel left join input
-     * @param rightRel right join input
-     * @param condition join condition
-     * @param leftJoinKeys The join keys from the left input which are equi-join keys
-     * @param rightJoinKeys The join keys from the right input which are equi-join keys
-     * @param filterNulls The join key positions for which null values will not match. null values
-     *     only match for the "is not distinct from" condition.
-     * @param rangeOp if null, only locate equi-joins; otherwise, locate a single non-equi join
-     *     predicate and return its operator in this list; join keys associated with the non-equi
-     *     join predicate are at the end of the key lists returned
-     * @return What's left, never null
-     */
-    public static RexNode splitJoinCondition(
-            List<RelDataTypeField> sysFieldList,
-            RelNode leftRel,
-            RelNode rightRel,
-            RexNode condition,
-            List<RexNode> leftJoinKeys,
-            List<RexNode> rightJoinKeys,
-            @Nullable List<Integer> filterNulls,
-            @Nullable List<SqlOperator> rangeOp) {
-        return splitJoinCondition(
-                sysFieldList,
-                ImmutableList.of(leftRel, rightRel),
-                condition,
-                ImmutableList.of(leftJoinKeys, rightJoinKeys),
-                filterNulls,
-                rangeOp);
-    }
-
-    /**
-     * Splits out the equi-join (and optionally, a single non-equi) components of a join condition,
-     * and returns what's left. Projection might be required by the caller to provide join keys that
-     * are not direct field references.
-     *
-     * @param sysFieldList list of system fields
-     * @param inputs join inputs
-     * @param condition join condition
-     * @param joinKeys The join keys from the inputs which are equi-join keys
-     * @param filterNulls The join key positions for which null values will not match. null values
-     *     only match for the "is not distinct from" condition.
-     * @param rangeOp if null, only locate equi-joins; otherwise, locate a single non-equi join
-     *     predicate and return its operator in this list; join keys associated with the non-equi
-     *     join predicate are at the end of the key lists returned
-     * @return What's left, never null
-     */
-    public static RexNode splitJoinCondition(
-            List<RelDataTypeField> sysFieldList,
-            List<RelNode> inputs,
-            RexNode condition,
-            List<List<RexNode>> joinKeys,
-            @Nullable List<Integer> filterNulls,
-            @Nullable List<SqlOperator> rangeOp) {
-        final List<RexNode> nonEquiList = new ArrayList<>();
-
-        splitJoinCondition(
-                sysFieldList, inputs, condition, joinKeys, filterNulls, rangeOp, nonEquiList);
-
-        // Convert the remainders into a list that are AND'ed together.
-        return RexUtil.composeConjunction(inputs.get(0).getCluster().getRexBuilder(), nonEquiList);
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static @Nullable RexNode splitCorrelatedFilterCondition(
-            LogicalFilter filter, List<RexInputRef> joinKeys, List<RexNode> correlatedJoinKeys) {
-        final List<RexNode> nonEquiList = new ArrayList<>();
-
-        splitCorrelatedFilterCondition(
-                filter, filter.getCondition(), joinKeys, correlatedJoinKeys, nonEquiList);
-
-        // Convert the remainders into a list that are AND'ed together.
-        return RexUtil.composeConjunction(filter.getCluster().getRexBuilder(), nonEquiList, true);
-    }
-
-    public static @Nullable RexNode splitCorrelatedFilterCondition(
-            LogicalFilter filter,
-            List<RexNode> joinKeys,
-            List<RexNode> correlatedJoinKeys,
-            boolean extractCorrelatedFieldAccess) {
-        return splitCorrelatedFilterCondition(
-                (Filter) filter, joinKeys, correlatedJoinKeys, extractCorrelatedFieldAccess);
-    }
-
-    public static @Nullable RexNode splitCorrelatedFilterCondition(
-            Filter filter,
-            List<RexNode> joinKeys,
-            List<RexNode> correlatedJoinKeys,
-            boolean extractCorrelatedFieldAccess) {
-        final List<RexNode> nonEquiList = new ArrayList<>();
-
-        splitCorrelatedFilterCondition(
-                filter,
-                filter.getCondition(),
-                joinKeys,
-                correlatedJoinKeys,
-                nonEquiList,
-                extractCorrelatedFieldAccess);
-
-        // Convert the remainders into a list that are AND'ed together.
-        return RexUtil.composeConjunction(filter.getCluster().getRexBuilder(), nonEquiList, true);
-    }
-
-    private static void splitJoinCondition(
-            List<RelDataTypeField> sysFieldList,
-            List<RelNode> inputs,
-            RexNode condition,
-            List<List<RexNode>> joinKeys,
-            @Nullable List<Integer> filterNulls,
-            @Nullable List<SqlOperator> rangeOp,
-            List<RexNode> nonEquiList) {
-        final int sysFieldCount = sysFieldList.size();
-        final RelOptCluster cluster = inputs.get(0).getCluster();
-        final RexBuilder rexBuilder = cluster.getRexBuilder();
-        final RelDataTypeFactory typeFactory = cluster.getTypeFactory();
-
-        final ImmutableBitSet[] inputsRange = new ImmutableBitSet[inputs.size()];
-        int totalFieldCount = 0;
-        for (int i = 0; i < inputs.size(); i++) {
-            final int firstField = totalFieldCount + sysFieldCount;
-            totalFieldCount = firstField + inputs.get(i).getRowType().getFieldCount();
-            inputsRange[i] = ImmutableBitSet.range(firstField, totalFieldCount);
-        }
-
-        // adjustment array
-        int[] adjustments = new int[totalFieldCount];
-        for (int i = 0; i < inputs.size(); i++) {
-            final int adjustment = inputsRange[i].nextSetBit(0);
-            for (int j = adjustment; j < inputsRange[i].length(); j++) {
-                adjustments[j] = -adjustment;
-            }
-        }
-
-        if (condition.getKind() == SqlKind.AND) {
-            for (RexNode operand : ((RexCall) condition).getOperands()) {
-                splitJoinCondition(
-                        sysFieldList, inputs, operand, joinKeys, filterNulls, rangeOp, nonEquiList);
-            }
-            return;
-        }
-
-        if (condition instanceof RexCall) {
-            RexNode leftKey = null;
-            RexNode rightKey = null;
-            int leftInput = 0;
-            int rightInput = 0;
-            List<RelDataTypeField> leftFields = null;
-            List<RelDataTypeField> rightFields = null;
-            boolean reverse = false;
-
-            final RexCall call =
-                    collapseExpandedIsNotDistinctFromExpr((RexCall) condition, rexBuilder);
-            SqlKind kind = call.getKind();
-
-            // Only consider range operators if we haven't already seen one
-            if ((kind == SqlKind.EQUALS)
-                    || (filterNulls != null && kind == SqlKind.IS_NOT_DISTINCT_FROM)
-                    || (rangeOp != null
-                            && rangeOp.isEmpty()
-                            && (kind == SqlKind.GREATER_THAN
-                                    || kind == SqlKind.GREATER_THAN_OR_EQUAL
-                                    || kind == SqlKind.LESS_THAN
-                                    || kind == SqlKind.LESS_THAN_OR_EQUAL))) {
-                final List<RexNode> operands = call.getOperands();
-                RexNode op0 = operands.get(0);
-                RexNode op1 = operands.get(1);
-
-                final ImmutableBitSet projRefs0 = InputFinder.bits(op0);
-                final ImmutableBitSet projRefs1 = InputFinder.bits(op1);
-
-                boolean foundBothInputs = false;
-                for (int i = 0; i < inputs.size() && !foundBothInputs; i++) {
-                    if (projRefs0.intersects(inputsRange[i])
-                            && projRefs0.union(inputsRange[i]).equals(inputsRange[i])) {
-                        if (leftKey == null) {
-                            leftKey = op0;
-                            leftInput = i;
-                            leftFields = inputs.get(leftInput).getRowType().getFieldList();
-                        } else {
-                            rightKey = op0;
-                            rightInput = i;
-                            rightFields = inputs.get(rightInput).getRowType().getFieldList();
-                            reverse = true;
-                            foundBothInputs = true;
-                        }
-                    } else if (projRefs1.intersects(inputsRange[i])
-                            && projRefs1.union(inputsRange[i]).equals(inputsRange[i])) {
-                        if (leftKey == null) {
-                            leftKey = op1;
-                            leftInput = i;
-                            leftFields = inputs.get(leftInput).getRowType().getFieldList();
-                        } else {
-                            rightKey = op1;
-                            rightInput = i;
-                            rightFields = inputs.get(rightInput).getRowType().getFieldList();
-                            foundBothInputs = true;
-                        }
-                    }
-                }
-
-                if ((leftKey != null) && (rightKey != null)) {
-                    // replace right Key input ref
-                    rightKey =
-                            rightKey.accept(
-                                    new RelOptUtil.RexInputConverter(
-                                            rexBuilder, rightFields, rightFields, adjustments));
-
-                    // left key only needs to be adjusted if there are system
-                    // fields, but do it for uniformity
-                    leftKey =
-                            leftKey.accept(
-                                    new RelOptUtil.RexInputConverter(
-                                            rexBuilder, leftFields, leftFields, adjustments));
-
-                    RelDataType leftKeyType = leftKey.getType();
-                    RelDataType rightKeyType = rightKey.getType();
-
-                    if (leftKeyType != rightKeyType) {
-                        // perform casting
-                        RelDataType targetKeyType =
-                                typeFactory.leastRestrictive(
-                                        ImmutableList.of(leftKeyType, rightKeyType));
-
-                        if (targetKeyType == null) {
-                            throw new AssertionError(
-                                    "Cannot find common type for join keys "
-                                            + leftKey
-                                            + " (type "
-                                            + leftKeyType
-                                            + ") and "
-                                            + rightKey
-                                            + " (type "
-                                            + rightKeyType
-                                            + ")");
-                        }
-
-                        if (leftKeyType != targetKeyType) {
-                            leftKey = rexBuilder.makeCast(targetKeyType, leftKey);
-                        }
-
-                        if (rightKeyType != targetKeyType) {
-                            rightKey = rexBuilder.makeCast(targetKeyType, rightKey);
-                        }
-                    }
-                }
-            }
-
-            if ((leftKey != null) && (rightKey != null)) {
-                // found suitable join keys
-                // add them to key list, ensuring that if there is a
-                // non-equi join predicate, it appears at the end of the
-                // key list; also mark the null filtering property
-                addJoinKey(
-                        joinKeys.get(leftInput), leftKey, (rangeOp != null) && !rangeOp.isEmpty());
-                addJoinKey(
-                        joinKeys.get(rightInput),
-                        rightKey,
-                        (rangeOp != null) && !rangeOp.isEmpty());
-                if (filterNulls != null && kind == SqlKind.EQUALS) {
-                    // nulls are considered not matching for equality comparison
-                    // add the position of the most recently inserted key
-                    filterNulls.add(joinKeys.get(leftInput).size() - 1);
-                }
-                if (rangeOp != null && kind != SqlKind.EQUALS && kind != SqlKind.IS_DISTINCT_FROM) {
-                    SqlOperator op = call.getOperator();
-                    if (reverse) {
-                        op = requireNonNull(op.reverse());
-                    }
-                    rangeOp.add(op);
-                }
-                return;
-            } // else fall through and add this condition as nonEqui condition
-        }
-
-        // The operator is not of RexCall type
-        // So we fail. Fall through.
-        // Add this condition to the list of non-equi-join conditions.
-        nonEquiList.add(condition);
-    }
-
-    /** Builds an equi-join condition from a set of left and right keys. */
-    public static RexNode createEquiJoinCondition(
-            final RelNode left,
-            final List<Integer> leftKeys,
-            final RelNode right,
-            final List<Integer> rightKeys,
-            final RexBuilder rexBuilder) {
-        final List<RelDataType> leftTypes = RelOptUtil.getFieldTypeList(left.getRowType());
-        final List<RelDataType> rightTypes = RelOptUtil.getFieldTypeList(right.getRowType());
-        return RexUtil.composeConjunction(
-                rexBuilder,
-                new AbstractList<RexNode>() {
-                    @Override
-                    public RexNode get(int index) {
-                        final int leftKey = leftKeys.get(index);
-                        final int rightKey = rightKeys.get(index);
-                        return rexBuilder.makeCall(
-                                SqlStdOperatorTable.EQUALS,
-                                rexBuilder.makeInputRef(leftTypes.get(leftKey), leftKey),
-                                rexBuilder.makeInputRef(
-                                        rightTypes.get(rightKey), leftTypes.size() + rightKey));
-                    }
-
-                    @Override
-                    public int size() {
-                        return leftKeys.size();
-                    }
-                });
-    }
-
-    /**
-     * Returns {@link SqlOperator} for given {@link SqlKind} or returns {@code operator} when {@link
-     * SqlKind} is not known.
-     *
-     * @param kind input kind
-     * @param operator default operator value
-     * @return SqlOperator for the given kind
-     * @see RexUtil#op(SqlKind)
-     */
-    public static SqlOperator op(SqlKind kind, SqlOperator operator) {
-        switch (kind) {
-            case EQUALS:
-                return SqlStdOperatorTable.EQUALS;
-            case NOT_EQUALS:
-                return SqlStdOperatorTable.NOT_EQUALS;
-            case GREATER_THAN:
-                return SqlStdOperatorTable.GREATER_THAN;
-            case GREATER_THAN_OR_EQUAL:
-                return SqlStdOperatorTable.GREATER_THAN_OR_EQUAL;
-            case LESS_THAN:
-                return SqlStdOperatorTable.LESS_THAN;
-            case LESS_THAN_OR_EQUAL:
-                return SqlStdOperatorTable.LESS_THAN_OR_EQUAL;
-            case IS_DISTINCT_FROM:
-                return SqlStdOperatorTable.IS_DISTINCT_FROM;
-            case IS_NOT_DISTINCT_FROM:
-                return SqlStdOperatorTable.IS_NOT_DISTINCT_FROM;
-            default:
-                return operator;
-        }
-    }
-
-    private static void addJoinKey(
-            List<RexNode> joinKeyList, RexNode key, boolean preserveLastElementInList) {
-        if (!joinKeyList.isEmpty() && preserveLastElementInList) {
-            joinKeyList.add(joinKeyList.size() - 1, key);
-        } else {
-            joinKeyList.add(key);
-        }
-    }
-
-    private static void splitCorrelatedFilterCondition(
-            LogicalFilter filter,
-            RexNode condition,
-            List<RexInputRef> joinKeys,
-            List<RexNode> correlatedJoinKeys,
-            List<RexNode> nonEquiList) {
-        if (condition instanceof RexCall) {
-            RexCall call = (RexCall) condition;
-            if (call.getOperator().getKind() == SqlKind.AND) {
-                for (RexNode operand : call.getOperands()) {
-                    splitCorrelatedFilterCondition(
-                            filter, operand, joinKeys, correlatedJoinKeys, nonEquiList);
-                }
-                return;
-            }
-
-            if (call.getOperator().getKind() == SqlKind.EQUALS) {
-                final List<RexNode> operands = call.getOperands();
-                RexNode op0 = operands.get(0);
-                RexNode op1 = operands.get(1);
-
-                if (!RexUtil.containsInputRef(op0) && op1 instanceof RexInputRef) {
-                    correlatedJoinKeys.add(op0);
-                    joinKeys.add((RexInputRef) op1);
-                    return;
-                } else if (op0 instanceof RexInputRef && !RexUtil.containsInputRef(op1)) {
-                    joinKeys.add((RexInputRef) op0);
-                    correlatedJoinKeys.add(op1);
-                    return;
-                }
-            }
-        }
-
-        // The operator is not of RexCall type
-        // So we fail. Fall through.
-        // Add this condition to the list of non-equi-join conditions.
-        nonEquiList.add(condition);
-    }
-
-    @SuppressWarnings("unused")
-    private static void splitCorrelatedFilterCondition(
-            LogicalFilter filter,
-            RexNode condition,
-            List<RexNode> joinKeys,
-            List<RexNode> correlatedJoinKeys,
-            List<RexNode> nonEquiList,
-            boolean extractCorrelatedFieldAccess) {
-        splitCorrelatedFilterCondition(
-                (Filter) filter,
-                condition,
-                joinKeys,
-                correlatedJoinKeys,
-                nonEquiList,
-                extractCorrelatedFieldAccess);
-    }
-
-    private static void splitCorrelatedFilterCondition(
-            Filter filter,
-            RexNode condition,
-            List<RexNode> joinKeys,
-            List<RexNode> correlatedJoinKeys,
-            List<RexNode> nonEquiList,
-            boolean extractCorrelatedFieldAccess) {
-        if (condition instanceof RexCall) {
-            RexCall call = (RexCall) condition;
-            if (call.getOperator().getKind() == SqlKind.AND) {
-                for (RexNode operand : call.getOperands()) {
-                    splitCorrelatedFilterCondition(
-                            filter,
-                            operand,
-                            joinKeys,
-                            correlatedJoinKeys,
-                            nonEquiList,
-                            extractCorrelatedFieldAccess);
-                }
-                return;
-            }
-
-            if (call.getOperator().getKind() == SqlKind.EQUALS) {
-                final List<RexNode> operands = call.getOperands();
-                RexNode op0 = operands.get(0);
-                RexNode op1 = operands.get(1);
-
-                if (extractCorrelatedFieldAccess) {
-                    if (!RexUtil.containsFieldAccess(op0) && op1 instanceof RexFieldAccess) {
-                        joinKeys.add(op0);
-                        correlatedJoinKeys.add(op1);
-                        return;
-                    } else if (op0 instanceof RexFieldAccess && !RexUtil.containsFieldAccess(op1)) {
-                        correlatedJoinKeys.add(op0);
-                        joinKeys.add(op1);
-                        return;
-                    }
-                } else {
-                    if (!RexUtil.containsInputRef(op0) && op1 instanceof RexInputRef) {
-                        correlatedJoinKeys.add(op0);
-                        joinKeys.add(op1);
-                        return;
-                    } else if (op0 instanceof RexInputRef && !RexUtil.containsInputRef(op1)) {
-                        joinKeys.add(op0);
-                        correlatedJoinKeys.add(op1);
-                        return;
-                    }
-                }
-            }
-        }
-
-        // The operator is not of RexCall type
-        // So we fail. Fall through.
-        // Add this condition to the list of non-equi-join conditions.
-        nonEquiList.add(condition);
-    }
-
-    private static void splitJoinCondition(
-            final RexBuilder rexBuilder,
-            final int leftFieldCount,
-            RexNode condition,
-            List<Integer> leftKeys,
-            List<Integer> rightKeys,
-            @Nullable List<Boolean> filterNulls,
-            List<RexNode> nonEquiList) {
-        if (condition instanceof RexCall) {
-            RexCall call = (RexCall) condition;
-            SqlKind kind = call.getKind();
-            if (kind == SqlKind.AND) {
-                for (RexNode operand : call.getOperands()) {
-                    splitJoinCondition(
-                            rexBuilder,
-                            leftFieldCount,
-                            operand,
-                            leftKeys,
-                            rightKeys,
-                            filterNulls,
-                            nonEquiList);
-                }
-                return;
-            }
-
-            if (filterNulls != null) {
-                call = collapseExpandedIsNotDistinctFromExpr(call, rexBuilder);
-                kind = call.getKind();
-            }
-
-            // "=" and "IS NOT DISTINCT FROM" are the same except for how they
-            // treat nulls.
-            if (kind == SqlKind.EQUALS
-                    || (filterNulls != null && kind == SqlKind.IS_NOT_DISTINCT_FROM)) {
-                final List<RexNode> operands = call.getOperands();
-                if ((operands.get(0) instanceof RexInputRef)
-                        && (operands.get(1) instanceof RexInputRef)) {
-                    RexInputRef op0 = (RexInputRef) operands.get(0);
-                    RexInputRef op1 = (RexInputRef) operands.get(1);
-
-                    RexInputRef leftField;
-                    RexInputRef rightField;
-                    if ((op0.getIndex() < leftFieldCount) && (op1.getIndex() >= leftFieldCount)) {
-                        // Arguments were of form 'op0 = op1'
-                        leftField = op0;
-                        rightField = op1;
-                    } else if ((op1.getIndex() < leftFieldCount)
-                            && (op0.getIndex() >= leftFieldCount)) {
-                        // Arguments were of form 'op1 = op0'
-                        leftField = op1;
-                        rightField = op0;
-                    } else {
-                        nonEquiList.add(condition);
-                        return;
-                    }
-
-                    leftKeys.add(leftField.getIndex());
-                    rightKeys.add(rightField.getIndex() - leftFieldCount);
-                    if (filterNulls != null) {
-                        filterNulls.add(kind == SqlKind.EQUALS);
-                    }
-                    return;
-                }
-                // Arguments were not field references, one from each side, so
-                // we fail. Fall through.
-            }
-        }
-
-        // Add this condition to the list of non-equi-join conditions.
-        if (!condition.isAlwaysTrue()) {
-            nonEquiList.add(condition);
-        }
-    }
-
-    /**
-     * Collapses an expanded version of {@code IS NOT DISTINCT FROM} expression.
-     *
-     * <p>Helper method for {@link #splitJoinCondition(RexBuilder, int, RexNode, List, List, List,
-     * List)} and {@link #splitJoinCondition(List, List, RexNode, List, List, List, List)}.
-     *
-     * <p>If the given expr <code>call</code> is an expanded version of {@code IS NOT DISTINCT FROM}
-     * function call, collapses it and return a {@code IS NOT DISTINCT FROM} function call.
-     *
-     * <p>For example: {@code t1.key IS NOT DISTINCT FROM t2.key} can rewritten in expanded form as
-     * {@code t1.key = t2.key OR (t1.key IS NULL AND t2.key IS NULL)}.
-     *
-     * @param call Function expression to try collapsing
-     * @param rexBuilder {@link RexBuilder} instance to create new {@link RexCall} instances.
-     * @return If the given function is an expanded IS NOT DISTINCT FROM function call, return a IS
-     *     NOT DISTINCT FROM function call. Otherwise return the input function call as it is.
-     */
-    public static RexCall collapseExpandedIsNotDistinctFromExpr(
-            final RexCall call, final RexBuilder rexBuilder) {
-        switch (call.getKind()) {
-            case OR:
-                return doCollapseExpandedIsNotDistinctFromOrExpr(call, rexBuilder);
-
-            case CASE:
-                return doCollapseExpandedIsNotDistinctFromCaseExpr(call, rexBuilder);
-
-            default:
-                return call;
-        }
-    }
-
-    private static RexCall doCollapseExpandedIsNotDistinctFromOrExpr(
-            final RexCall call, final RexBuilder rexBuilder) {
-        if (call.getKind() != SqlKind.OR || call.getOperands().size() != 2) {
-            return call;
-        }
-
-        final RexNode op0 = call.getOperands().get(0);
-        final RexNode op1 = call.getOperands().get(1);
-
-        if (!(op0 instanceof RexCall) || !(op1 instanceof RexCall)) {
-            return call;
-        }
-
-        RexCall opEqCall = (RexCall) op0;
-        RexCall opNullEqCall = (RexCall) op1;
-
-        // Swapping the operands if necessary
-        if (opEqCall.getKind() == SqlKind.AND
-                && (opNullEqCall.getKind() == SqlKind.EQUALS
-                        || opNullEqCall.getKind() == SqlKind.IS_TRUE)) {
-            RexCall temp = opEqCall;
-            opEqCall = opNullEqCall;
-            opNullEqCall = temp;
-        }
-
-        // Check if EQUALS is actually wrapped in IS TRUE expression
-        if (opEqCall.getKind() == SqlKind.IS_TRUE) {
-            RexNode tmp = opEqCall.getOperands().get(0);
-            if (!(tmp instanceof RexCall)) {
-                return call;
-            }
-            opEqCall = (RexCall) tmp;
-        }
-
-        if (opNullEqCall.getKind() != SqlKind.AND
-                || opNullEqCall.getOperands().size() != 2
-                || opEqCall.getKind() != SqlKind.EQUALS) {
-            return call;
-        }
-
-        final RexNode op10 = opNullEqCall.getOperands().get(0);
-        final RexNode op11 = opNullEqCall.getOperands().get(1);
-        if (op10.getKind() != SqlKind.IS_NULL || op11.getKind() != SqlKind.IS_NULL) {
-            return call;
-        }
-
-        return doCollapseExpandedIsNotDistinctFrom(
-                rexBuilder, call, (RexCall) op10, (RexCall) op11, opEqCall);
-    }
-
-    private static RexCall doCollapseExpandedIsNotDistinctFromCaseExpr(
-            final RexCall call, final RexBuilder rexBuilder) {
-        if (call.getKind() != SqlKind.CASE || call.getOperands().size() != 5) {
-            return call;
-        }
-
-        final RexNode op0 = call.getOperands().get(0);
-        final RexNode op1 = call.getOperands().get(1);
-        final RexNode op2 = call.getOperands().get(2);
-        final RexNode op3 = call.getOperands().get(3);
-        final RexNode op4 = call.getOperands().get(4);
-
-        if (!(op0 instanceof RexCall)
-                || !(op1 instanceof RexCall)
-                || !(op2 instanceof RexCall)
-                || !(op3 instanceof RexCall)
-                || !(op4 instanceof RexCall)) {
-            return call;
-        }
-
-        RexCall ifCall = (RexCall) op0;
-        RexCall thenCall = (RexCall) op1;
-        RexCall elseIfCall = (RexCall) op2;
-        RexCall elseIfThenCall = (RexCall) op3;
-        RexCall elseCall = (RexCall) op4;
-
-        if (ifCall.getKind() != SqlKind.IS_NULL
-                || thenCall.getKind() != SqlKind.IS_NULL
-                || elseIfCall.getKind() != SqlKind.IS_NULL
-                || elseIfThenCall.getKind() != SqlKind.IS_NULL
-                || elseCall.getKind() != SqlKind.EQUALS) {
-            return call;
-        }
-
-        if (!ifCall.equals(elseIfThenCall) || !thenCall.equals(elseIfCall)) {
-            return call;
-        }
-
-        return doCollapseExpandedIsNotDistinctFrom(rexBuilder, call, ifCall, elseIfCall, elseCall);
-    }
-
-    private static RexCall doCollapseExpandedIsNotDistinctFrom(
-            final RexBuilder rexBuilder,
-            final RexCall call,
-            RexCall ifNull0Call,
-            RexCall ifNull1Call,
-            RexCall equalsCall) {
-        final RexNode isNullInput0 = ifNull0Call.getOperands().get(0);
-        final RexNode isNullInput1 = ifNull1Call.getOperands().get(0);
-
-        final RexNode equalsInput0 =
-                RexUtil.removeNullabilityCast(
-                        rexBuilder.getTypeFactory(), equalsCall.getOperands().get(0));
-        final RexNode equalsInput1 =
-                RexUtil.removeNullabilityCast(
-                        rexBuilder.getTypeFactory(), equalsCall.getOperands().get(1));
-
-        if ((isNullInput0.equals(equalsInput0) && isNullInput1.equals(equalsInput1))
-                || (isNullInput1.equals(equalsInput0) && isNullInput0.equals(equalsInput1))) {
-            return (RexCall)
-                    rexBuilder.makeCall(
-                            SqlStdOperatorTable.IS_NOT_DISTINCT_FROM,
-                            ImmutableList.of(isNullInput0, isNullInput1));
-        }
-
-        return call;
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static void projectJoinInputs(
-            RelNode[] inputRels,
-            List<RexNode> leftJoinKeys,
-            List<RexNode> rightJoinKeys,
-            int systemColCount,
-            List<Integer> leftKeys,
-            List<Integer> rightKeys,
-            List<Integer> outputProj) {
-        RelNode leftRel = inputRels[0];
-        RelNode rightRel = inputRels[1];
-        final RelOptCluster cluster = leftRel.getCluster();
-        final RexBuilder rexBuilder = cluster.getRexBuilder();
-
-        int origLeftInputSize = leftRel.getRowType().getFieldCount();
-        int origRightInputSize = rightRel.getRowType().getFieldCount();
-
-        final List<RexNode> newLeftFields = new ArrayList<>();
-        final List<@Nullable String> newLeftFieldNames = new ArrayList<>();
-
-        final List<RexNode> newRightFields = new ArrayList<>();
-        final List<@Nullable String> newRightFieldNames = new ArrayList<>();
-        int leftKeyCount = leftJoinKeys.size();
-        int rightKeyCount = rightJoinKeys.size();
-        int i;
-
-        for (i = 0; i < systemColCount; i++) {
-            outputProj.add(i);
-        }
-
-        for (i = 0; i < origLeftInputSize; i++) {
-            final RelDataTypeField field = leftRel.getRowType().getFieldList().get(i);
-            newLeftFields.add(rexBuilder.makeInputRef(field.getType(), i));
-            newLeftFieldNames.add(field.getName());
-            outputProj.add(systemColCount + i);
-        }
-
-        int newLeftKeyCount = 0;
-        for (i = 0; i < leftKeyCount; i++) {
-            RexNode leftKey = leftJoinKeys.get(i);
-
-            if (leftKey instanceof RexInputRef) {
-                // already added to the projected left fields
-                // only need to remember the index in the join key list
-                leftKeys.add(((RexInputRef) leftKey).getIndex());
-            } else {
-                newLeftFields.add(leftKey);
-                newLeftFieldNames.add(null);
-                leftKeys.add(origLeftInputSize + newLeftKeyCount);
-                newLeftKeyCount++;
-            }
-        }
-
-        int leftFieldCount = origLeftInputSize + newLeftKeyCount;
-        for (i = 0; i < origRightInputSize; i++) {
-            final RelDataTypeField field = rightRel.getRowType().getFieldList().get(i);
-            newRightFields.add(rexBuilder.makeInputRef(field.getType(), i));
-            newRightFieldNames.add(field.getName());
-            outputProj.add(systemColCount + leftFieldCount + i);
-        }
-
-        int newRightKeyCount = 0;
-        for (i = 0; i < rightKeyCount; i++) {
-            RexNode rightKey = rightJoinKeys.get(i);
-
-            if (rightKey instanceof RexInputRef) {
-                // already added to the projected left fields
-                // only need to remember the index in the join key list
-                rightKeys.add(((RexInputRef) rightKey).getIndex());
-            } else {
-                newRightFields.add(rightKey);
-                newRightFieldNames.add(null);
-                rightKeys.add(origRightInputSize + newRightKeyCount);
-                newRightKeyCount++;
-            }
-        }
-
-        final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(cluster, null);
-
-        // added project if need to produce new keys than the original input
-        // fields
-        if (newLeftKeyCount > 0) {
-            leftRel =
-                    relBuilder
-                            .push(leftRel)
-                            .project(newLeftFields, newLeftFieldNames, true)
-                            .build();
-        }
-
-        if (newRightKeyCount > 0) {
-            rightRel =
-                    relBuilder.push(rightRel).project(newRightFields, newRightFieldNames).build();
-        }
-
-        inputRels[0] = leftRel;
-        inputRels[1] = rightRel;
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createProjectJoinRel(List<Integer> outputProj, RelNode joinRel) {
-        int newProjectOutputSize = outputProj.size();
-        List<RelDataTypeField> joinOutputFields = joinRel.getRowType().getFieldList();
-
-        // If no projection was passed in, or the number of desired projection
-        // columns is the same as the number of columns returned from the
-        // join, then no need to create a projection
-        if (newProjectOutputSize > 0 && newProjectOutputSize < joinOutputFields.size()) {
-            final PairList<RexNode, String> newProjects = PairList.of();
-            final RelBuilder relBuilder =
-                    RelFactories.LOGICAL_BUILDER.create(joinRel.getCluster(), null);
-            final RexBuilder rexBuilder = relBuilder.getRexBuilder();
-            for (int fieldIndex : outputProj) {
-                final RelDataTypeField field = joinOutputFields.get(fieldIndex);
-                newProjects.add(
-                        rexBuilder.makeInputRef(field.getType(), fieldIndex), field.getName());
-            }
-
-            // Create a project rel on the output of the join.
-            return relBuilder
-                    .push(joinRel)
-                    .project(newProjects.leftList(), newProjects.rightList(), true)
-                    .build();
-        }
-
-        return joinRel;
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static void registerAbstractRels(RelOptPlanner planner) {
-        registerAbstractRules(planner);
-    }
-
-    @Experimental
-    public static void registerAbstractRules(RelOptPlanner planner) {
-        RelOptRules.ABSTRACT_RULES.forEach(planner::addRule);
-    }
-
-    @Experimental
-    public static void registerAbstractRelationalRules(RelOptPlanner planner) {
-        RelOptRules.ABSTRACT_RELATIONAL_RULES.forEach(planner::addRule);
-        if (CalciteSystemProperty.COMMUTE.value()) {
-            planner.addRule(CoreRules.JOIN_ASSOCIATE);
-        }
-        // todo: rule which makes Project({OrdinalRef}) disappear
-    }
-
-    private static void registerEnumerableRules(RelOptPlanner planner) {
-        EnumerableRules.ENUMERABLE_RULES.forEach(planner::addRule);
-    }
-
-    private static void registerBaseRules(RelOptPlanner planner) {
-        RelOptRules.BASE_RULES.forEach(planner::addRule);
-    }
-
-    @SuppressWarnings("unused")
-    private static void registerReductionRules(RelOptPlanner planner) {
-        RelOptRules.CONSTANT_REDUCTION_RULES.forEach(planner::addRule);
-    }
-
-    private static void registerMaterializationRules(RelOptPlanner planner) {
-        RelOptRules.MATERIALIZATION_RULES.forEach(planner::addRule);
-    }
-
-    @SuppressWarnings("unused")
-    private static void registerCalcRules(RelOptPlanner planner) {
-        RelOptRules.CALC_RULES.forEach(planner::addRule);
-    }
-
-    @Experimental
-    public static void registerDefaultRules(
-            RelOptPlanner planner, boolean enableMaterializations, boolean enableBindable) {
-        if (CalciteSystemProperty.ENABLE_COLLATION_TRAIT.value()) {
-            registerAbstractRelationalRules(planner);
-        }
-        registerAbstractRules(planner);
-        registerBaseRules(planner);
-
-        if (enableMaterializations) {
-            registerMaterializationRules(planner);
-        }
-        if (enableBindable) {
-            for (RelOptRule rule : Bindables.RULES) {
-                planner.addRule(rule);
-            }
-        }
-        // Registers this rule for default ENUMERABLE convention
-        // because:
-        // 1. ScannableTable can bind data directly;
-        // 2. Only BindableTable supports project push down now.
-
-        // EnumerableInterpreterRule.INSTANCE would then transform
-        // the BindableTableScan to
-        // EnumerableInterpreter + BindableTableScan.
-
-        // Note: the cost of EnumerableInterpreter + BindableTableScan
-        // is always bigger that EnumerableTableScan because of the additional
-        // EnumerableInterpreter node, but if there are pushing projects or filter,
-        // we prefer BindableTableScan instead,
-        // see BindableTableScan#computeSelfCost.
-        planner.addRule(Bindables.BINDABLE_TABLE_SCAN_RULE);
-        planner.addRule(CoreRules.PROJECT_TABLE_SCAN);
-        planner.addRule(CoreRules.PROJECT_INTERPRETER_TABLE_SCAN);
-
-        if (CalciteSystemProperty.ENABLE_ENUMERABLE.value()) {
-            registerEnumerableRules(planner);
-            planner.addRule(EnumerableRules.TO_INTERPRETER);
-        }
-
-        if (enableBindable && CalciteSystemProperty.ENABLE_ENUMERABLE.value()) {
-            planner.addRule(EnumerableRules.TO_BINDABLE);
-        }
-
-        if (CalciteSystemProperty.ENABLE_STREAM.value()) {
-            for (RelOptRule rule : StreamRules.RULES) {
-                planner.addRule(rule);
-            }
-        }
-
-        planner.addRule(CoreRules.FILTER_REDUCE_EXPRESSIONS);
-    }
-
-    /**
-     * Dumps a plan as a string.
-     *
-     * @param header Header to print before the plan. Ignored if the format is XML
-     * @param rel Relational expression to explain
-     * @param format Output format
-     * @param detailLevel Detail level
-     * @return Plan
-     */
-    public static String dumpPlan(
-            String header, RelNode rel, SqlExplainFormat format, SqlExplainLevel detailLevel) {
-        StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw);
-        if (!header.isEmpty()) {
-            pw.println(header);
-        }
-        RelWriter planWriter;
-        switch (format) {
-            case XML:
-                planWriter = new RelXmlWriter(pw, detailLevel);
-                break;
-            case JSON:
-                planWriter = new RelJsonWriter();
-                rel.explain(planWriter);
-                return ((RelJsonWriter) planWriter).asString();
-            case DOT:
-                planWriter = new RelDotWriter(pw, detailLevel, false);
-                break;
-            default:
-                planWriter = new RelWriterImpl(pw, detailLevel, false);
-        }
-        rel.explain(planWriter);
-        pw.flush();
-        return sw.toString();
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static String dumpPlan(
-            String header, RelNode rel, boolean asXml, SqlExplainLevel detailLevel) {
-        return dumpPlan(
-                header, rel, asXml ? SqlExplainFormat.XML : SqlExplainFormat.TEXT, detailLevel);
-    }
-
-    /**
-     * Creates the row type descriptor for the result of a DML operation, which is a single column
-     * named ROWCOUNT of type BIGINT for INSERT; a single column named PLAN for EXPLAIN.
-     *
-     * @param kind Kind of node
-     * @param typeFactory factory to use for creating type descriptor
-     * @return created type
-     */
-    public static RelDataType createDmlRowType(SqlKind kind, RelDataTypeFactory typeFactory) {
-        switch (kind) {
-            case INSERT:
-            case DELETE:
-            case UPDATE:
-            case MERGE:
-                return typeFactory.createStructType(
-                        PairList.of(
-                                AvaticaConnection.ROWCOUNT_COLUMN_NAME,
-                                typeFactory.createSqlType(SqlTypeName.BIGINT)));
-            case EXPLAIN:
-                return typeFactory.createStructType(
-                        PairList.of(
-                                AvaticaConnection.PLAN_COLUMN_NAME,
-                                typeFactory.createSqlType(
-                                        SqlTypeName.VARCHAR, RelDataType.PRECISION_NOT_SPECIFIED)));
-            default:
-                throw Util.unexpected(kind);
-        }
-    }
-
-    /**
-     * Returns whether two types are equal using 'equals'.
-     *
-     * @param desc1 Description of first type
-     * @param type1 First type
-     * @param desc2 Description of second type
-     * @param type2 Second type
-     * @param litmus What to do if an error is detected (types are not equal)
-     * @return Whether the types are equal
-     */
-    public static boolean eq(
-            final String desc1,
-            RelDataType type1,
-            final String desc2,
-            RelDataType type2,
-            Litmus litmus) {
-        // if any one of the types is ANY return true
-        if (type1.getSqlTypeName() == SqlTypeName.ANY
-                || type2.getSqlTypeName() == SqlTypeName.ANY) {
-            return litmus.succeed();
-        }
-
-        if (!type1.equals(type2)) {
-            return litmus.fail(
-                    "type mismatch:\n{}:\n{}\n{}:\n{}",
-                    desc1,
-                    type1.getFullTypeString(),
-                    desc2,
-                    type2.getFullTypeString());
-        }
-        return litmus.succeed();
-    }
-
-    // ----- FLINK MODIFICATION BEGIN -----
-    // Backport from Calcite (CALCITE-6764)
-    public static boolean eqUpToNullability(
-            boolean ignoreNullability,
-            final String desc1,
-            RelDataType type1,
-            final String desc2,
-            RelDataType type2,
-            Litmus litmus) {
-        if (type1.getSqlTypeName() == SqlTypeName.ANY
-                || type2.getSqlTypeName() == SqlTypeName.ANY) {
-            return litmus.succeed();
-        }
-
-        boolean success;
-        if (ignoreNullability) {
-            success = SqlTypeUtil.equalSansNullability(type1, type2);
-        } else {
-            success = type1.equals(type2);
-        }
-
-        if (!success) {
-            return litmus.fail(
-                    "type mismatch:\n{}:\n{}\n{}:\n{}",
-                    desc1,
-                    type1.getFullTypeString(),
-                    desc2,
-                    type2.getFullTypeString());
-        }
-        return litmus.succeed();
-    }
-
-    // ----- FLINK MODIFICATION END -----
-
-    /**
-     * Returns whether two types are equal using {@link #areRowTypesEqual(RelDataType, RelDataType,
-     * boolean)}. Both types must not be null.
-     *
-     * @param desc1 Description of role of first type
-     * @param type1 First type
-     * @param desc2 Description of role of second type
-     * @param type2 Second type
-     * @param litmus Whether to assert if they are not equal
-     * @return Whether the types are equal
-     */
-    public static boolean equal(
-            final String desc1,
-            RelDataType type1,
-            final String desc2,
-            RelDataType type2,
-            Litmus litmus) {
-        if (!areRowTypesEqual(type1, type2, false)) {
-            return litmus.fail(getFullTypeDifferenceString(desc1, type1, desc2, type2));
-        }
-        return litmus.succeed();
-    }
-
-    /**
-     * Returns the detailed difference of two types.
-     *
-     * @param sourceDesc description of role of source type
-     * @param sourceType source type
-     * @param targetDesc description of role of target type
-     * @param targetType target type
-     * @return the detailed difference of two types
-     */
-    public static String getFullTypeDifferenceString(
-            final String sourceDesc,
-            RelDataType sourceType,
-            final String targetDesc,
-            RelDataType targetType) {
-        if (sourceType == targetType) {
-            return "";
-        }
-
-        final int sourceFieldCount = sourceType.getFieldCount();
-        final int targetFieldCount = targetType.getFieldCount();
-        if (sourceFieldCount != targetFieldCount) {
-            return "Type mismatch: the field sizes are not equal.\n"
-                    + sourceDesc
-                    + ": "
-                    + sourceType.getFullTypeString()
-                    + "\n"
-                    + targetDesc
-                    + ": "
-                    + targetType.getFullTypeString();
-        }
-
-        final StringBuilder stringBuilder = new StringBuilder();
-        final List<RelDataTypeField> f1 = sourceType.getFieldList();
-        final List<RelDataTypeField> f2 = targetType.getFieldList();
-        for (Pair<RelDataTypeField, RelDataTypeField> pair : Pair.zip(f1, f2)) {
-            final RelDataType t1 = pair.left.getType();
-            final RelDataType t2 = pair.right.getType();
-            // If one of the types is ANY comparison should succeed
-            if (sourceType.getSqlTypeName() == SqlTypeName.ANY
-                    || targetType.getSqlTypeName() == SqlTypeName.ANY) {
-                continue;
-            }
-            if (!t1.equals(t2)) {
-                stringBuilder.append(pair.left.getName());
-                stringBuilder.append(": ");
-                stringBuilder.append(t1.getFullTypeString());
-                stringBuilder.append(" -> ");
-                stringBuilder.append(t2.getFullTypeString());
-                stringBuilder.append("\n");
-            }
-        }
-        final String difference = stringBuilder.toString();
-        if (!difference.isEmpty()) {
-            return "Type mismatch:\n"
-                    + sourceDesc
-                    + ": "
-                    + sourceType.getFullTypeString()
-                    + "\n"
-                    + targetDesc
-                    + ": "
-                    + targetType.getFullTypeString()
-                    + "\n"
-                    + "Difference:\n"
-                    + difference;
-        } else {
-            return "";
-        }
-    }
-
-    /** Returns whether two relational expressions have the same row-type. */
-    public static boolean equalType(
-            String desc0, RelNode rel0, String desc1, RelNode rel1, Litmus litmus) {
-        // TODO: change 'equal' to 'eq', which is stronger.
-        return equal(desc0, rel0.getRowType(), desc1, rel1.getRowType(), litmus);
-    }
-
-    /**
-     * Returns a translation of the <code>IS DISTINCT FROM</code> (or <code>IS
-     * NOT DISTINCT FROM</code>) sql operator.
-     *
-     * @param neg if false, returns a translation of IS NOT DISTINCT FROM
-     */
-    public static RexNode isDistinctFrom(RexBuilder rexBuilder, RexNode x, RexNode y, boolean neg) {
-        RexNode ret = null;
-        if (x.getType().isStruct()) {
-            assert y.getType().isStruct();
-            List<RelDataTypeField> xFields = x.getType().getFieldList();
-            List<RelDataTypeField> yFields = y.getType().getFieldList();
-            assert xFields.size() == yFields.size();
-            for (Pair<RelDataTypeField, RelDataTypeField> pair : Pair.zip(xFields, yFields)) {
-                RelDataTypeField xField = pair.left;
-                RelDataTypeField yField = pair.right;
-                RexNode newX = rexBuilder.makeFieldAccess(x, xField.getIndex());
-                RexNode newY = rexBuilder.makeFieldAccess(y, yField.getIndex());
-                RexNode newCall = isDistinctFromInternal(rexBuilder, newX, newY, neg);
-                if (ret == null) {
-                    ret = newCall;
-                } else {
-                    ret = rexBuilder.makeCall(SqlStdOperatorTable.AND, ret, newCall);
-                }
-            }
-        } else {
-            ret = isDistinctFromInternal(rexBuilder, x, y, neg);
-        }
-
-        // The result of IS DISTINCT FROM is NOT NULL because it can
-        // only return TRUE or FALSE.
-        requireNonNull(ret, "ret");
-        checkArgument(!ret.getType().isNullable());
-
-        return ret;
-    }
-
-    private static RexNode isDistinctFromInternal(
-            RexBuilder rexBuilder, RexNode x, RexNode y, boolean neg) {
-
-        if (neg) {
-            // x is not distinct from y
-            // x=y IS TRUE or ((x is null) and (y is null)),
-            return rexBuilder.makeCall(
-                    SqlStdOperatorTable.OR,
-                    rexBuilder.makeCall(
-                            SqlStdOperatorTable.AND,
-                            rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, x),
-                            rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, y)),
-                    rexBuilder.makeCall(
-                            SqlStdOperatorTable.IS_TRUE,
-                            rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, x, y)));
-        } else {
-            // x is distinct from y
-            // x=y IS NOT TRUE and ((x is not null) or (y is not null)),
-            return rexBuilder.makeCall(
-                    SqlStdOperatorTable.AND,
-                    rexBuilder.makeCall(
-                            SqlStdOperatorTable.OR,
-                            rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, x),
-                            rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, y)),
-                    rexBuilder.makeCall(
-                            SqlStdOperatorTable.IS_NOT_TRUE,
-                            rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, x, y)));
-        }
-    }
-
-    /** Converts a relational expression to a string, showing just basic attributes. */
-    public static String toString(final RelNode rel) {
-        return toString(rel, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
-    }
-
-    /**
-     * Converts a relational expression to a string; returns null if and only if {@code rel} is
-     * null.
-     */
-    public static @PolyNull String toString(
-            final @PolyNull RelNode rel, SqlExplainLevel detailLevel) {
-        if (rel == null) {
-            return null;
-        }
-        final StringWriter sw = new StringWriter();
-        final RelWriter planWriter = new RelWriterImpl(new PrintWriter(sw), detailLevel, false);
-        rel.explain(planWriter);
-        return sw.toString();
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode renameIfNecessary(RelNode rel, RelDataType desiredRowType) {
-        final RelDataType rowType = rel.getRowType();
-        if (rowType == desiredRowType) {
-            // Nothing to do.
-            return rel;
-        }
-        assert !rowType.equals(desiredRowType);
-
-        if (!areRowTypesEqual(rowType, desiredRowType, false)) {
-            // The row types are different ignoring names. Nothing we can do.
-            return rel;
-        }
-        rel = createRename(rel, desiredRowType.getFieldNames());
-        return rel;
-    }
-
-    public static String dumpType(RelDataType type) {
-        final StringWriter sw = new StringWriter();
-        final PrintWriter pw = new PrintWriter(sw);
-        final TypeDumper typeDumper = new TypeDumper(pw);
-        if (type.isStruct()) {
-            typeDumper.acceptFields(type.getFieldList());
-        } else {
-            typeDumper.accept(type);
-        }
-        pw.flush();
-        return sw.toString();
-    }
-
-    /**
-     * Returns the set of columns with unique names, with prior columns taking precedence over
-     * columns that appear later in the list.
-     */
-    public static List<RelDataTypeField> deduplicateColumns(
-            List<RelDataTypeField> baseColumns, List<RelDataTypeField> extendedColumns) {
-        final Set<String> dedupedFieldNames = new HashSet<>();
-        final ImmutableList.Builder<RelDataTypeField> dedupedFields = ImmutableList.builder();
-        for (RelDataTypeField f : Iterables.concat(baseColumns, extendedColumns)) {
-            if (dedupedFieldNames.add(f.getName())) {
-                dedupedFields.add(f);
-            }
-        }
-        return dedupedFields.build();
-    }
-
-    /**
-     * Decomposes a predicate into a list of expressions that are AND'ed together.
-     *
-     * @param rexPredicate predicate to be analyzed
-     * @param rexList list of decomposed RexNodes
-     */
-    public static void decomposeConjunction(@Nullable RexNode rexPredicate, List<RexNode> rexList) {
-        if (rexPredicate == null || rexPredicate.isAlwaysTrue()) {
-            return;
-        }
-        if (rexPredicate.isA(SqlKind.AND)) {
-            for (RexNode operand : ((RexCall) rexPredicate).getOperands()) {
-                decomposeConjunction(operand, rexList);
-            }
-        } else {
-            rexList.add(rexPredicate);
-        }
-    }
-
-    /**
-     * Decomposes a predicate into a list of expressions that are AND'ed together, and a list of
-     * expressions that are preceded by NOT.
-     *
-     * <p>For example, {@code a AND NOT b AND NOT (c and d) AND TRUE AND NOT FALSE} returns {@code
-     * rexList = [a], notList = [b, c AND d]}.
-     *
-     * <p>TRUE and NOT FALSE expressions are ignored. FALSE and NOT TRUE expressions are placed on
-     * {@code rexList} and {@code notList} as other expressions.
-     *
-     * <p>For example, {@code a AND TRUE AND NOT TRUE} returns {@code rexList = [a], notList =
-     * [TRUE]}.
-     *
-     * @param rexPredicate predicate to be analyzed
-     * @param rexList list of decomposed RexNodes (except those with NOT)
-     * @param notList list of decomposed RexNodes that were prefixed NOT
-     */
-    public static void decomposeConjunction(
-            @Nullable RexNode rexPredicate, List<RexNode> rexList, List<RexNode> notList) {
-        if (rexPredicate == null || rexPredicate.isAlwaysTrue()) {
-            return;
-        }
-        switch (rexPredicate.getKind()) {
-            case AND:
-                for (RexNode operand : ((RexCall) rexPredicate).getOperands()) {
-                    decomposeConjunction(operand, rexList, notList);
-                }
-                break;
-            case NOT:
-                final RexNode e = ((RexCall) rexPredicate).getOperands().get(0);
-                if (e.isAlwaysFalse()) {
-                    return;
-                }
-                switch (e.getKind()) {
-                    case OR:
-                        final List<RexNode> ors = new ArrayList<>();
-                        decomposeDisjunction(e, ors);
-                        for (RexNode or : ors) {
-                            switch (or.getKind()) {
-                                case NOT:
-                                    rexList.add(((RexCall) or).operands.get(0));
-                                    break;
-                                default:
-                                    notList.add(or);
-                            }
-                        }
-                        break;
-                    default:
-                        notList.add(e);
-                }
-                break;
-            case LITERAL:
-                if (!RexLiteral.isNullLiteral(rexPredicate)
-                        && RexLiteral.booleanValue(rexPredicate)) {
-                    return; // ignore TRUE
-                }
-            // fall through
-            default:
-                rexList.add(rexPredicate);
-                break;
-        }
-    }
-
-    /**
-     * Decomposes a predicate into a list of expressions that are OR'ed together.
-     *
-     * @param rexPredicate predicate to be analyzed
-     * @param rexList list of decomposed RexNodes
-     */
-    public static void decomposeDisjunction(@Nullable RexNode rexPredicate, List<RexNode> rexList) {
-        if (rexPredicate == null || rexPredicate.isAlwaysFalse()) {
-            return;
-        }
-        if (rexPredicate.isA(SqlKind.OR)) {
-            for (RexNode operand : ((RexCall) rexPredicate).getOperands()) {
-                decomposeDisjunction(operand, rexList);
-            }
-        } else {
-            rexList.add(rexPredicate);
-        }
-    }
-
-    /**
-     * Returns a condition decomposed by AND.
-     *
-     * <p>For example, {@code conjunctions(TRUE)} returns the empty list; {@code
-     * conjunctions(FALSE)} returns list {@code {FALSE}}.
-     */
-    public static List<RexNode> conjunctions(@Nullable RexNode rexPredicate) {
-        final List<RexNode> list = new ArrayList<>();
-        decomposeConjunction(rexPredicate, list);
-        return list;
-    }
-
-    /**
-     * Returns a condition decomposed by OR.
-     *
-     * <p>For example, {@code disjunctions(FALSE)} returns the empty list.
-     */
-    public static List<RexNode> disjunctions(RexNode rexPredicate) {
-        final List<RexNode> list = new ArrayList<>();
-        decomposeDisjunction(rexPredicate, list);
-        return list;
-    }
-
-    /**
-     * Ands two sets of join filters together, either of which can be null.
-     *
-     * @param rexBuilder rexBuilder to create AND expression
-     * @param left filter on the left that the right will be AND'd to
-     * @param right filter on the right
-     * @return AND'd filter
-     * @see org.apache.calcite.rex.RexUtil#composeConjunction
-     */
-    public static RexNode andJoinFilters(
-            RexBuilder rexBuilder, @Nullable RexNode left, @Nullable RexNode right) {
-        // don't bother AND'ing in expressions that always evaluate to
-        // true
-        if ((left != null) && !left.isAlwaysTrue()) {
-            if ((right != null) && !right.isAlwaysTrue()) {
-                left = rexBuilder.makeCall(SqlStdOperatorTable.AND, left, right);
-            }
-        } else {
-            left = right;
-        }
-
-        // Joins must have some filter
-        if (left == null) {
-            left = rexBuilder.makeLiteral(true);
-        }
-        return left;
-    }
-
-    /**
-     * Decomposes the WHERE clause of a view into predicates that constraint a column to a
-     * particular value.
-     *
-     * <p>This method is key to the validation of a modifiable view. Columns that are constrained to
-     * a single value can be omitted from the SELECT clause of a modifiable view.
-     *
-     * @param projectMap Mapping from column ordinal to the expression that populate that column, to
-     *     be populated by this method
-     * @param filters List of remaining filters, to be populated by this method
-     * @param constraint Constraint to be analyzed
-     */
-    public static void inferViewPredicates(
-            Map<Integer, RexNode> projectMap, List<RexNode> filters, RexNode constraint) {
-        for (RexNode node : conjunctions(constraint)) {
-            switch (node.getKind()) {
-                case EQUALS:
-                    final List<RexNode> operands = ((RexCall) node).getOperands();
-                    RexNode o0 = operands.get(0);
-                    RexNode o1 = operands.get(1);
-                    if (o0 instanceof RexLiteral) {
-                        o0 = operands.get(1);
-                        o1 = operands.get(0);
-                    }
-                    if (o0.getKind() == SqlKind.CAST) {
-                        o0 = ((RexCall) o0).getOperands().get(0);
-                    }
-                    if (o0 instanceof RexInputRef && o1 instanceof RexLiteral) {
-                        final int index = ((RexInputRef) o0).getIndex();
-                        if (projectMap.get(index) == null) {
-                            projectMap.put(index, o1);
-                            continue;
-                        }
-                    }
-                    break;
-                default:
-                    break;
-            }
-            filters.add(node);
-        }
-    }
-
-    /**
-     * Returns a mapping of the column ordinal in the underlying table to a column constraint of the
-     * modifiable view.
-     *
-     * @param modifiableViewTable The modifiable view which has a constraint
-     * @param targetRowType The target type
-     */
-    public static Map<Integer, RexNode> getColumnConstraints(
-            ModifiableView modifiableViewTable,
-            RelDataType targetRowType,
-            RelDataTypeFactory typeFactory) {
-        final RexBuilder rexBuilder = new RexBuilder(typeFactory);
-        final RexNode constraint = modifiableViewTable.getConstraint(rexBuilder, targetRowType);
-        final Map<Integer, RexNode> projectMap = new HashMap<>();
-        final List<RexNode> filters = new ArrayList<>();
-        RelOptUtil.inferViewPredicates(projectMap, filters, constraint);
-        assert filters.isEmpty();
-        return projectMap;
-    }
-
-    /**
-     * Ensures that a source value does not violate the constraint of the target column.
-     *
-     * @param sourceValue The insert value being validated
-     * @param targetConstraint The constraint applied to sourceValue for validation
-     * @param errorSupplier The function to apply when validation fails
-     */
-    public static void validateValueAgainstConstraint(
-            SqlNode sourceValue,
-            RexNode targetConstraint,
-            Supplier<CalciteContextException> errorSupplier) {
-        if (!(sourceValue instanceof SqlLiteral)) {
-            // We cannot guarantee that the value satisfies the constraint.
-            throw errorSupplier.get();
-        }
-        final SqlLiteral insertValue = (SqlLiteral) sourceValue;
-        final RexLiteral columnConstraint = (RexLiteral) targetConstraint;
-
-        final RexSqlStandardConvertletTable convertletTable = new RexSqlStandardConvertletTable();
-        final RexToSqlNodeConverter sqlNodeToRexConverter =
-                new RexToSqlNodeConverterImpl(convertletTable);
-        final SqlLiteral constraintValue =
-                (SqlLiteral) sqlNodeToRexConverter.convertLiteral(columnConstraint);
-
-        if (!insertValue.equals(constraintValue)) {
-            // The value does not satisfy the constraint.
-            throw errorSupplier.get();
-        }
-    }
-
-    /**
-     * Adjusts key values in a list by some fixed amount.
-     *
-     * @param keys list of key values
-     * @param adjustment the amount to adjust the key values by
-     * @return modified list
-     */
-    public static List<Integer> adjustKeys(List<Integer> keys, int adjustment) {
-        if (adjustment == 0) {
-            return keys;
-        }
-        final List<Integer> newKeys = new ArrayList<>();
-        for (int key : keys) {
-            newKeys.add(key + adjustment);
-        }
-        return newKeys;
-    }
-
-    /**
-     * Simplifies outer joins if filter above would reject nulls.
-     *
-     * @param joinRel Join
-     * @param aboveFilters Filters from above
-     * @param joinType Join type, can not be inner join
-     */
-    public static JoinRelType simplifyJoin(
-            RelNode joinRel, ImmutableList<RexNode> aboveFilters, JoinRelType joinType) {
-        // No need to simplify if the join only outputs left side.
-        if (!joinType.projectsRight()) {
-            return joinType;
-        }
-        final int nTotalFields = joinRel.getRowType().getFieldCount();
-        final int nSysFields = 0;
-        final int nFieldsLeft = joinRel.getInputs().get(0).getRowType().getFieldCount();
-        final int nFieldsRight = joinRel.getInputs().get(1).getRowType().getFieldCount();
-        assert nTotalFields == nSysFields + nFieldsLeft + nFieldsRight;
-
-        // set the reference bitmaps for the left and right children
-        ImmutableBitSet leftBitmap = ImmutableBitSet.range(nSysFields, nSysFields + nFieldsLeft);
-        ImmutableBitSet rightBitmap = ImmutableBitSet.range(nSysFields + nFieldsLeft, nTotalFields);
-
-        for (RexNode filter : aboveFilters) {
-            if (joinType.generatesNullsOnLeft() && Strong.isNotTrue(filter, leftBitmap)) {
-                joinType = joinType.cancelNullsOnLeft();
-            }
-            if (joinType.generatesNullsOnRight() && Strong.isNotTrue(filter, rightBitmap)) {
-                joinType = joinType.cancelNullsOnRight();
-            }
-            if (!joinType.isOuterJoin()) {
-                break;
-            }
-        }
-        return joinType;
-    }
-
-    /**
-     * Classifies filters according to where they should be processed. They either stay where they
-     * are, are pushed to the join (if they originated from above the join), or are pushed to one of
-     * the children. Filters that are pushed are added to list passed in as input parameters.
-     *
-     * @param joinRel join node
-     * @param filters filters to be classified
-     * @param pushInto whether filters can be pushed into the join
-     * @param pushLeft true if filters can be pushed to the left
-     * @param pushRight true if filters can be pushed to the right
-     * @param joinFilters list of filters to push to the join
-     * @param leftFilters list of filters to push to the left child
-     * @param rightFilters list of filters to push to the right child
-     * @return whether at least one filter was pushed
-     */
-    public static boolean classifyFilters(
-            RelNode joinRel,
-            List<RexNode> filters,
-            boolean pushInto,
-            boolean pushLeft,
-            boolean pushRight,
-            List<RexNode> joinFilters,
-            List<RexNode> leftFilters,
-            List<RexNode> rightFilters) {
-        RexBuilder rexBuilder = joinRel.getCluster().getRexBuilder();
-        List<RelDataTypeField> joinFields = joinRel.getRowType().getFieldList();
-        final int nSysFields = 0; // joinRel.getSystemFieldList().size();
-        final List<RelDataTypeField> leftFields =
-                joinRel.getInputs().get(0).getRowType().getFieldList();
-        final int nFieldsLeft = leftFields.size();
-        final List<RelDataTypeField> rightFields =
-                joinRel.getInputs().get(1).getRowType().getFieldList();
-        final int nFieldsRight = rightFields.size();
-        final int nTotalFields = nFieldsLeft + nFieldsRight;
-
-        // set the reference bitmaps for the left and right children
-        ImmutableBitSet leftBitmap = ImmutableBitSet.range(nSysFields, nSysFields + nFieldsLeft);
-        ImmutableBitSet rightBitmap = ImmutableBitSet.range(nSysFields + nFieldsLeft, nTotalFields);
-
-        final List<RexNode> filtersToRemove = new ArrayList<>();
-        for (RexNode filter : filters) {
-            final InputFinder inputFinder = InputFinder.analyze(filter);
-            final ImmutableBitSet inputBits = inputFinder.build();
-
-            // REVIEW - are there any expressions that need special handling
-            // and therefore cannot be pushed?
-
-            if (pushLeft && leftBitmap.contains(inputBits)) {
-                // ignore filters that always evaluate to true
-                if (!filter.isAlwaysTrue()) {
-                    // adjust the field references in the filter to reflect
-                    // that fields in the left now shift over by the number
-                    // of system fields
-                    final RexNode shiftedFilter =
-                            shiftFilter(
-                                    nSysFields,
-                                    nSysFields + nFieldsLeft,
-                                    -nSysFields,
-                                    rexBuilder,
-                                    joinFields,
-                                    nTotalFields,
-                                    leftFields,
-                                    filter);
-
-                    leftFilters.add(shiftedFilter);
-                }
-                filtersToRemove.add(filter);
-            } else if (pushRight && rightBitmap.contains(inputBits)) {
-                if (!filter.isAlwaysTrue()) {
-                    // adjust the field references in the filter to reflect
-                    // that fields in the right now shift over to the left
-                    final RexNode shiftedFilter =
-                            shiftFilter(
-                                    nSysFields + nFieldsLeft,
-                                    nTotalFields,
-                                    -(nSysFields + nFieldsLeft),
-                                    rexBuilder,
-                                    joinFields,
-                                    nTotalFields,
-                                    rightFields,
-                                    filter);
-                    rightFilters.add(shiftedFilter);
-                }
-                filtersToRemove.add(filter);
-
-            } else {
-                // If the filter can't be pushed to either child, we may push them into the join
-                if (pushInto) {
-                    if (!joinFilters.contains(filter)) {
-                        joinFilters.add(filter);
-                    }
-                    filtersToRemove.add(filter);
-                }
-            }
-        }
-
-        // Remove filters after the loop, to prevent concurrent modification.
-        if (!filtersToRemove.isEmpty()) {
-            filters.removeAll(filtersToRemove);
-        }
-
-        // Did anything change?
-        return !filtersToRemove.isEmpty();
-    }
-
-    /**
-     * Classifies filters according to where they should be processed. They either stay where they
-     * are, are pushed to the join (if they originated from above the join), or are pushed to one of
-     * the children. Filters that are pushed are added to list passed in as input parameters.
-     *
-     * @param joinRel join node
-     * @param filters filters to be classified
-     * @param joinType join type
-     * @param pushInto whether filters can be pushed into the ON clause
-     * @param pushLeft true if filters can be pushed to the left
-     * @param pushRight true if filters can be pushed to the right
-     * @param joinFilters list of filters to push to the join
-     * @param leftFilters list of filters to push to the left child
-     * @param rightFilters list of filters to push to the right child
-     * @return whether at least one filter was pushed
-     * @deprecated Use {@link RelOptUtil#classifyFilters(RelNode, List, boolean, boolean, boolean,
-     *     List, List, List)}
-     */
-    @Deprecated // to be removed before 2.0
-    public static boolean classifyFilters(
-            RelNode joinRel,
-            List<RexNode> filters,
-            JoinRelType joinType,
-            boolean pushInto,
-            boolean pushLeft,
-            boolean pushRight,
-            List<RexNode> joinFilters,
-            List<RexNode> leftFilters,
-            List<RexNode> rightFilters) {
-        RexBuilder rexBuilder = joinRel.getCluster().getRexBuilder();
-        List<RelDataTypeField> joinFields = joinRel.getRowType().getFieldList();
-        final int nTotalFields = joinFields.size();
-        final int nSysFields = 0; // joinRel.getSystemFieldList().size();
-        final List<RelDataTypeField> leftFields =
-                joinRel.getInputs().get(0).getRowType().getFieldList();
-        final int nFieldsLeft = leftFields.size();
-        final List<RelDataTypeField> rightFields =
-                joinRel.getInputs().get(1).getRowType().getFieldList();
-        final int nFieldsRight = rightFields.size();
-
-        // SemiJoin, CorrelateSemiJoin, CorrelateAntiJoin: right fields are not returned
-        assert nTotalFields
-                == (!joinType.projectsRight()
-                        ? nSysFields + nFieldsLeft
-                        : nSysFields + nFieldsLeft + nFieldsRight);
-
-        // set the reference bitmaps for the left and right children
-        ImmutableBitSet leftBitmap = ImmutableBitSet.range(nSysFields, nSysFields + nFieldsLeft);
-        ImmutableBitSet rightBitmap = ImmutableBitSet.range(nSysFields + nFieldsLeft, nTotalFields);
-
-        final List<RexNode> filtersToRemove = new ArrayList<>();
-        for (RexNode filter : filters) {
-            final InputFinder inputFinder = InputFinder.analyze(filter);
-            final ImmutableBitSet inputBits = inputFinder.build();
-
-            // REVIEW - are there any expressions that need special handling
-            // and therefore cannot be pushed?
-
-            // filters can be pushed to the left child if the left child
-            // does not generate NULLs and the only columns referenced in
-            // the filter originate from the left child
-            if (pushLeft && leftBitmap.contains(inputBits)) {
-                // ignore filters that always evaluate to true
-                if (!filter.isAlwaysTrue()) {
-                    // adjust the field references in the filter to reflect
-                    // that fields in the left now shift over by the number
-                    // of system fields
-                    final RexNode shiftedFilter =
-                            shiftFilter(
-                                    nSysFields,
-                                    nSysFields + nFieldsLeft,
-                                    -nSysFields,
-                                    rexBuilder,
-                                    joinFields,
-                                    nTotalFields,
-                                    leftFields,
-                                    filter);
-
-                    leftFilters.add(shiftedFilter);
-                }
-                filtersToRemove.add(filter);
-
-                // filters can be pushed to the right child if the right child
-                // does not generate NULLs and the only columns referenced in
-                // the filter originate from the right child
-            } else if (pushRight && rightBitmap.contains(inputBits)) {
-                if (!filter.isAlwaysTrue()) {
-                    // adjust the field references in the filter to reflect
-                    // that fields in the right now shift over to the left;
-                    // since we never push filters to a NULL generating
-                    // child, the types of the source should match the dest
-                    // so we don't need to explicitly pass the destination
-                    // fields to RexInputConverter
-                    final RexNode shiftedFilter =
-                            shiftFilter(
-                                    nSysFields + nFieldsLeft,
-                                    nTotalFields,
-                                    -(nSysFields + nFieldsLeft),
-                                    rexBuilder,
-                                    joinFields,
-                                    nTotalFields,
-                                    rightFields,
-                                    filter);
-                    rightFilters.add(shiftedFilter);
-                }
-                filtersToRemove.add(filter);
-
-            } else {
-                // If the filter can't be pushed to either child and the join
-                // is an inner join, push them to the join if they originated
-                // from above the join
-                if (!joinType.isOuterJoin() && pushInto) {
-                    if (!joinFilters.contains(filter)) {
-                        joinFilters.add(filter);
-                    }
-                    filtersToRemove.add(filter);
-                }
-            }
-        }
-
-        // Remove filters after the loop, to prevent concurrent modification.
-        if (!filtersToRemove.isEmpty()) {
-            filters.removeAll(filtersToRemove);
-        }
-
-        // Did anything change?
-        return !filtersToRemove.isEmpty();
-    }
-
-    private static RexNode shiftFilter(
-            int start,
-            int end,
-            int offset,
-            RexBuilder rexBuilder,
-            List<RelDataTypeField> joinFields,
-            int nTotalFields,
-            List<RelDataTypeField> rightFields,
-            RexNode filter) {
-        int[] adjustments = new int[nTotalFields];
-        for (int i = start; i < end; i++) {
-            adjustments[i] = offset;
-        }
-        return filter.accept(
-                new RexInputConverter(rexBuilder, joinFields, rightFields, adjustments));
-    }
-
-    /**
-     * Splits a filter into two lists, depending on whether or not the filter only references its
-     * child input.
-     *
-     * @param childBitmap Fields in the child
-     * @param predicate filters that will be split
-     * @param pushable returns the list of filters that can be pushed to the child input
-     * @param notPushable returns the list of filters that cannot be pushed to the child input
-     */
-    public static void splitFilters(
-            ImmutableBitSet childBitmap,
-            @Nullable RexNode predicate,
-            List<RexNode> pushable,
-            List<RexNode> notPushable) {
-        // for each filter, if the filter only references the child inputs,
-        // then it can be pushed
-        for (RexNode filter : conjunctions(predicate)) {
-            ImmutableBitSet filterRefs = InputFinder.bits(filter);
-            if (childBitmap.contains(filterRefs)) {
-                pushable.add(filter);
-            } else {
-                notPushable.add(filter);
-            }
-        }
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static boolean checkProjAndChildInputs(Project project, boolean checkNames) {
-        int n = project.getProjects().size();
-        RelDataType inputType = project.getInput().getRowType();
-        if (inputType.getFieldList().size() != n) {
-            return false;
-        }
-        List<RelDataTypeField> projFields = project.getRowType().getFieldList();
-        List<RelDataTypeField> inputFields = inputType.getFieldList();
-        boolean namesDifferent = false;
-        for (int i = 0; i < n; ++i) {
-            RexNode exp = project.getProjects().get(i);
-            if (!(exp instanceof RexInputRef)) {
-                return false;
-            }
-            RexInputRef fieldAccess = (RexInputRef) exp;
-            if (i != fieldAccess.getIndex()) {
-                // can't support reorder yet
-                return false;
-            }
-            if (checkNames) {
-                String inputFieldName = inputFields.get(i).getName();
-                String projFieldName = projFields.get(i).getName();
-                if (!projFieldName.equals(inputFieldName)) {
-                    namesDifferent = true;
-                }
-            }
-        }
-
-        // inputs are the same; return value depends on the checkNames
-        // parameter
-        return !checkNames || namesDifferent;
-    }
-
-    /**
-     * Creates projection expressions reflecting the swapping of a join's input.
-     *
-     * @param newJoin the RelNode corresponding to the join with its inputs swapped
-     * @param origJoin original LogicalJoin
-     * @param origOrder if true, create the projection expressions to reflect the original
-     *     (pre-swapped) join projection; otherwise, create the projection to reflect the order of
-     *     the swapped projection
-     * @return array of expression representing the swapped join inputs
-     */
-    public static List<RexNode> createSwappedJoinExprs(
-            RelNode newJoin, Join origJoin, boolean origOrder) {
-        final List<RelDataTypeField> newJoinFields = newJoin.getRowType().getFieldList();
-        final RexBuilder rexBuilder = newJoin.getCluster().getRexBuilder();
-        final List<RexNode> exps = new ArrayList<>();
-        final int nFields =
-                origOrder
-                        ? origJoin.getRight().getRowType().getFieldCount()
-                        : origJoin.getLeft().getRowType().getFieldCount();
-        for (int i = 0; i < newJoinFields.size(); i++) {
-            final int source = (i + nFields) % newJoinFields.size();
-            RelDataTypeField field = origOrder ? newJoinFields.get(source) : newJoinFields.get(i);
-            exps.add(rexBuilder.makeInputRef(field.getType(), source));
-        }
-        return exps;
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RexNode pushFilterPastProject(RexNode filter, final Project projRel) {
-        return pushPastProject(filter, projRel);
-    }
-
-    /**
-     * Converts an expression that is based on the output fields of a {@link Project} to an
-     * equivalent expression on the Project's input fields.
-     *
-     * @param node The expression to be converted
-     * @param project Project underneath the expression
-     * @return converted expression
-     */
-    public static RexNode pushPastProject(RexNode node, Project project) {
-        return node.accept(pushShuttle(project));
-    }
-
-    /**
-     * Converts a list of expressions that are based on the output fields of a {@link Project} to
-     * equivalent expressions on the Project's input fields.
-     *
-     * @param nodes The expressions to be converted
-     * @param project Project underneath the expression
-     * @return converted expressions
-     */
-    public static List<RexNode> pushPastProject(List<? extends RexNode> nodes, Project project) {
-        return pushShuttle(project).visitList(nodes);
-    }
-
-    public static @Nullable RexNode pushPastProjectUnlessBloat(
-            RexNode node, Project project, int bloat) {
-        List<RexNode> newConditions =
-                pushPastProjectUnlessBloat(Collections.singletonList(node), project, bloat);
-        if (newConditions == null || newConditions.size() != 1) {
-            return null;
-        }
-        return newConditions.get(0);
-    }
-
-    /**
-     * As {@link #pushPastProject}, but returns null if the resulting expressions are significantly
-     * more complex.
-     *
-     * @param bloat Maximum allowable increase in complexity
-     */
-    public static @Nullable List<RexNode> pushPastProjectUnlessBloat(
-            List<? extends RexNode> nodes, Project project, int bloat) {
-        if (bloat < 0) {
-            // If bloat is negative never merge.
-            return null;
-        }
-        if (RexOver.containsOver(nodes, null) && project.containsOver()) {
-            // Is it valid relational algebra to apply windowed function to a windowed
-            // function? Possibly. But it's invalid SQL, so don't go there.
-            return null;
-        }
-        final List<RexNode> list = pushPastProject(nodes, project);
-        final int bottomCount = RexUtil.nodeCount(project.getProjects());
-        final int topCount = RexUtil.nodeCount(nodes);
-        final int mergedCount = RexUtil.nodeCount(list);
-        if (mergedCount > bottomCount + topCount + bloat) {
-            // The merged expression is more complex than the input expressions.
-            // Do not merge.
-            return null;
-        }
-        return list;
-    }
-
-    private static RexShuttle pushShuttle(final Project project) {
-        return new RexShuttle() {
-            @Override
-            public RexNode visitInputRef(RexInputRef ref) {
-                return project.getProjects().get(ref.getIndex());
-            }
-        };
-    }
-
-    /**
-     * Converts an expression that is based on the output fields of a {@link Calc} to an equivalent
-     * expression on the Calc's input fields.
-     *
-     * @param node The expression to be converted
-     * @param calc Calc underneath the expression
-     * @return converted expression
-     */
-    public static RexNode pushPastCalc(RexNode node, Calc calc) {
-        return node.accept(pushShuttle(calc));
-    }
-
-    private static RexShuttle pushShuttle(final Calc calc) {
-        final List<RexNode> projects =
-                Util.transform(
-                        calc.getProgram().getProjectList(), calc.getProgram()::expandLocalRef);
-        return new RexShuttle() {
-            @Override
-            public RexNode visitInputRef(RexInputRef ref) {
-                return projects.get(ref.getIndex());
-            }
-        };
-    }
-
-    /**
-     * Creates a new {@link org.apache.calcite.rel.rules.MultiJoin} to reflect projection references
-     * from a {@link Project} that is on top of the {@link org.apache.calcite.rel.rules.MultiJoin}.
-     *
-     * @param multiJoin the original MultiJoin
-     * @param project the Project on top of the MultiJoin
-     * @return the new MultiJoin
-     */
-    public static MultiJoin projectMultiJoin(MultiJoin multiJoin, Project project) {
-        // Locate all input references in the projection expressions as well
-        // the post-join filter.  Since the filter effectively sits in
-        // between the LogicalProject and the MultiJoin, the projection needs
-        // to include those filter references.
-        ImmutableBitSet inputRefs =
-                InputFinder.bits(project.getProjects(), multiJoin.getPostJoinFilter());
-
-        // create new copies of the bitmaps
-        List<RelNode> multiJoinInputs = multiJoin.getInputs();
-        List<BitSet> newProjFields = new ArrayList<>();
-        for (RelNode multiJoinInput : multiJoinInputs) {
-            newProjFields.add(new BitSet(multiJoinInput.getRowType().getFieldCount()));
-        }
-
-        // set the bits found in the expressions
-        int currInput = -1;
-        int startField = 0;
-        int nFields = 0;
-        for (int bit : inputRefs) {
-            while (bit >= (startField + nFields)) {
-                startField += nFields;
-                currInput++;
-                assert currInput < multiJoinInputs.size();
-                nFields = multiJoinInputs.get(currInput).getRowType().getFieldCount();
-            }
-            newProjFields.get(currInput).set(bit - startField);
-        }
-
-        // create a new MultiJoin containing the new field bitmaps
-        // for each input
-        return new MultiJoin(
-                multiJoin.getCluster(),
-                multiJoin.getInputs(),
-                multiJoin.getJoinFilter(),
-                multiJoin.getRowType(),
-                multiJoin.isFullOuterJoin(),
-                multiJoin.getOuterJoinConditions(),
-                multiJoin.getJoinTypes(),
-                Util.transform(newProjFields, ImmutableBitSet::fromBitSet),
-                multiJoin.getJoinFieldRefCountsMap(),
-                multiJoin.getPostJoinFilter());
-    }
-
-    public static <T extends RelNode> T addTrait(T rel, RelTrait trait) {
-        //noinspection unchecked
-        return (T) rel.copy(rel.getTraitSet().replace(trait), rel.getInputs());
-    }
-
-    /** Returns a shallow copy of a relational expression with a particular input replaced. */
-    public static RelNode replaceInput(RelNode parent, int ordinal, RelNode newInput) {
-        final List<RelNode> inputs = new ArrayList<>(parent.getInputs());
-        if (inputs.get(ordinal) == newInput) {
-            return parent;
-        }
-        inputs.set(ordinal, newInput);
-        return parent.copy(parent.getTraitSet(), inputs);
-    }
-
-    /**
-     * Creates a {@link org.apache.calcite.rel.logical.LogicalProject} that projects particular
-     * fields of its input, according to a mapping.
-     */
-    public static RelNode createProject(RelNode child, Mappings.TargetMapping mapping) {
-        return createProject(child, Mappings.asListNonNull(mapping.inverse()));
-    }
-
-    public static RelNode createProject(
-            RelNode child,
-            Mappings.TargetMapping mapping,
-            RelFactories.ProjectFactory projectFactory) {
-        return createProject(projectFactory, child, Mappings.asListNonNull(mapping.inverse()));
-    }
-
-    /**
-     * Returns the relational table node for {@code tableName} if it occurs within a relational
-     * expression {@code root} otherwise an empty option is returned.
-     */
-    public static @Nullable RelOptTable findTable(RelNode root, final String tableName) {
-        try {
-            RelShuttle visitor =
-                    new RelHomogeneousShuttle() {
-                        @Override
-                        public RelNode visit(TableScan scan) {
-                            final RelOptTable scanTable = scan.getTable();
-                            final List<String> qualifiedName = scanTable.getQualifiedName();
-                            if (qualifiedName.get(qualifiedName.size() - 1).equals(tableName)) {
-                                throw new Util.FoundOne(scanTable);
-                            }
-                            return super.visit(scan);
-                        }
-                    };
-            root.accept(visitor);
-            return null;
-        } catch (Util.FoundOne e) {
-            Util.swallow(e, null);
-            return (RelOptTable) e.getNode();
-        }
-    }
-
-    /**
-     * Returns whether relational expression {@code target} occurs within a relational expression
-     * {@code ancestor}.
-     */
-    public static boolean contains(RelNode ancestor, final RelNode target) {
-        if (ancestor == target) {
-            // Short-cut common case.
-            return true;
-        }
-        try {
-            new RelVisitor() {
-                @Override
-                public void visit(RelNode node, int ordinal, @Nullable RelNode parent) {
-                    if (node == target) {
-                        throw Util.FoundOne.NULL;
-                    }
-                    super.visit(node, ordinal, parent);
-                }
-                // CHECKSTYLE: IGNORE 1
-            }.go(ancestor);
-            return false;
-        } catch (Util.FoundOne e) {
-            return true;
-        }
-    }
-
-    /**
-     * Within a relational expression {@code query}, replaces occurrences of {@code find} with
-     * {@code replace}.
-     */
-    public static RelNode replace(RelNode query, RelNode find, RelNode replace) {
-        if (find == replace) {
-            // Short-cut common case.
-            return query;
-        }
-        assert equalType("find", find, "replace", replace, Litmus.THROW);
-        if (query == find) {
-            // Short-cut another common case.
-            return replace;
-        }
-        return replaceRecurse(query, find, replace);
-    }
-
-    /** Helper for {@link #replace}. */
-    private static RelNode replaceRecurse(RelNode query, RelNode find, RelNode replace) {
-        if (query == find) {
-            return replace;
-        }
-        final List<RelNode> inputs = query.getInputs();
-        if (!inputs.isEmpty()) {
-            final List<RelNode> newInputs = new ArrayList<>();
-            for (RelNode input : inputs) {
-                newInputs.add(replaceRecurse(input, find, replace));
-            }
-            if (!newInputs.equals(inputs)) {
-                return query.copy(query.getTraitSet(), newInputs);
-            }
-        }
-        return query;
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelOptTable.ToRelContext getContext(RelOptCluster cluster) {
-        return ViewExpanders.simpleContext(cluster);
-    }
-
-    /** Returns the number of {@link org.apache.calcite.rel.core.Join} nodes in a tree. */
-    public static int countJoins(RelNode rootRel) {
-        /** Visitor that counts join nodes. */
-        class JoinCounter extends RelVisitor {
-            int joinCount;
-
-            @Override
-            public void visit(
-                    RelNode node,
-                    int ordinal,
-                    @org.checkerframework.checker.nullness.qual.Nullable RelNode parent) {
-                if (node instanceof Join) {
-                    ++joinCount;
-                }
-                super.visit(node, ordinal, parent);
-            }
-
-            int run(RelNode node) {
-                go(node);
-                return joinCount;
-            }
-        }
-
-        return new JoinCounter().run(rootRel);
-    }
-
-    /** Permutes a record type according to a mapping. */
-    public static RelDataType permute(
-            RelDataTypeFactory typeFactory, RelDataType rowType, Mapping mapping) {
-        return typeFactory.createStructType(Mappings.apply3(mapping, rowType.getFieldList()));
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createProject(
-            RelNode child, List<? extends RexNode> exprList, List<String> fieldNameList) {
-        final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(child.getCluster(), null);
-        return relBuilder.push(child).project(exprList, fieldNameList, true).build();
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createProject(
-            RelNode child,
-            List<Pair<RexNode, ? extends @Nullable String>> projectList,
-            boolean optimize) {
-        final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(child.getCluster(), null);
-        return relBuilder
-                .push(child)
-                .projectNamed(Pair.left(projectList), Pair.right(projectList), !optimize)
-                .build();
-    }
-
-    /**
-     * Creates a relational expression that projects the given fields of the input.
-     *
-     * <p>Optimizes if the fields are the identity projection.
-     *
-     * @param child Input relational expression
-     * @param posList Source of each projected field
-     * @return Relational expression that projects given fields
-     */
-    public static RelNode createProject(final RelNode child, final List<Integer> posList) {
-        return createProject(RelFactories.DEFAULT_PROJECT_FACTORY, child, posList);
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createProject(
-            RelNode child,
-            List<? extends RexNode> exprs,
-            List<? extends @Nullable String> fieldNames,
-            boolean optimize) {
-        final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(child.getCluster(), null);
-        return relBuilder.push(child).projectNamed(exprs, fieldNames, !optimize).build();
-    }
-
-    // CHECKSTYLE: IGNORE 1
-    /**
-     * @deprecated Use {@link RelBuilder#projectNamed(Iterable, Iterable, boolean)}
-     */
-    @Deprecated // to be removed before 2.0
-    public static RelNode createProject(
-            RelNode child,
-            List<? extends RexNode> exprs,
-            List<? extends @Nullable String> fieldNames,
-            boolean optimize,
-            RelBuilder relBuilder) {
-        return relBuilder.push(child).projectNamed(exprs, fieldNames, !optimize).build();
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode createRename(RelNode rel, List<? extends @Nullable String> fieldNames) {
-        final List<RelDataTypeField> fields = rel.getRowType().getFieldList();
-        assert fieldNames.size() == fields.size();
-        final List<RexNode> refs =
-                new AbstractList<RexNode>() {
-                    @Override
-                    public int size() {
-                        return fields.size();
-                    }
-
-                    @Override
-                    public RexNode get(int index) {
-                        return RexInputRef.of(index, fields);
-                    }
-                };
-        final RelBuilder relBuilder = RelFactories.LOGICAL_BUILDER.create(rel.getCluster(), null);
-        return relBuilder.push(rel).projectNamed(refs, fieldNames, false).build();
-    }
-
-    /**
-     * Creates a relational expression which permutes the output fields of a relational expression
-     * according to a permutation.
-     *
-     * <p>Optimizations:
-     *
-     * <ul>
-     *   <li>If the relational expression is a {@link org.apache.calcite.rel.logical.LogicalCalc} or
-     *       {@link org.apache.calcite.rel.logical.LogicalProject} that is already acting as a
-     *       permutation, combines the new permutation with the old;
-     *   <li>If the permutation is the identity, returns the original relational expression.
-     * </ul>
-     *
-     * <p>If a permutation is combined with its inverse, these optimizations would combine to remove
-     * them both.
-     *
-     * @param rel Relational expression
-     * @param permutation Permutation to apply to fields
-     * @param fieldNames Field names; if null, or if a particular entry is null, the name of the
-     *     permuted field is used
-     * @return relational expression which permutes its input fields
-     */
-    public static RelNode permute(
-            RelNode rel, Permutation permutation, @Nullable List<String> fieldNames) {
-        if (permutation.isIdentity()) {
-            return rel;
-        }
-        if (rel instanceof LogicalCalc) {
-            LogicalCalc calc = (LogicalCalc) rel;
-            Permutation permutation1 = calc.getProgram().getPermutation();
-            if (permutation1 != null) {
-                Permutation permutation2 = permutation.product(permutation1);
-                return permute(rel, permutation2, null);
-            }
-        }
-        if (rel instanceof LogicalProject) {
-            Permutation permutation1 = ((LogicalProject) rel).getPermutation();
-            if (permutation1 != null) {
-                Permutation permutation2 = permutation.product(permutation1);
-                return permute(rel, permutation2, null);
-            }
-        }
-        final List<RelDataType> outputTypeList = new ArrayList<>();
-        final List<String> outputNameList = new ArrayList<>();
-        final List<RexNode> exprList = new ArrayList<>();
-        final List<RexLocalRef> projectRefList = new ArrayList<>();
-        final List<RelDataTypeField> fields = rel.getRowType().getFieldList();
-        final RelOptCluster cluster = rel.getCluster();
-        for (int i = 0; i < permutation.getTargetCount(); i++) {
-            int target = permutation.getTarget(i);
-            final RelDataTypeField targetField = fields.get(target);
-            outputTypeList.add(targetField.getType());
-            outputNameList.add(
-                    ((fieldNames == null)
-                                    || (fieldNames.size() <= i)
-                                    || (fieldNames.get(i) == null))
-                            ? targetField.getName()
-                            : fieldNames.get(i));
-            exprList.add(cluster.getRexBuilder().makeInputRef(fields.get(i).getType(), i));
-            final int source = permutation.getSource(i);
-            projectRefList.add(new RexLocalRef(source, fields.get(source).getType()));
-        }
-        final RelDataTypeFactory typeFactory = cluster.getTypeFactory();
-        final RexProgram program =
-                new RexProgram(
-                        rel.getRowType(),
-                        exprList,
-                        projectRefList,
-                        null,
-                        typeFactory.createStructType(outputTypeList, outputNameList));
-        return LogicalCalc.create(rel, program);
-    }
-
-    /**
-     * Creates a relational expression that projects the given fields of the input.
-     *
-     * <p>Optimizes if the fields are the identity projection.
-     *
-     * @param factory ProjectFactory
-     * @param child Input relational expression
-     * @param posList Source of each projected field
-     * @return Relational expression that projects given fields
-     */
-    public static RelNode createProject(
-            final RelFactories.ProjectFactory factory,
-            final RelNode child,
-            final List<Integer> posList) {
-        RelDataType rowType = child.getRowType();
-        final List<String> fieldNames = rowType.getFieldNames();
-        final RelBuilder relBuilder = RelBuilder.proto(factory).create(child.getCluster(), null);
-        final List<RexNode> exprs =
-                new AbstractList<RexNode>() {
-                    @Override
-                    public int size() {
-                        return posList.size();
-                    }
-
-                    @Override
-                    public RexNode get(int index) {
-                        final int pos = posList.get(index);
-                        return relBuilder.getRexBuilder().makeInputRef(child, pos);
-                    }
-                };
-        final List<String> names = Util.select(fieldNames, posList);
-        return relBuilder.push(child).projectNamed(exprs, names, false).build();
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode projectMapping(
-            RelNode rel,
-            Mapping mapping,
-            @Nullable List<String> fieldNames,
-            RelFactories.ProjectFactory projectFactory) {
-        assert mapping.getMappingType().isSingleSource();
-        assert mapping.getMappingType().isMandatorySource();
-        if (mapping.isIdentity()) {
-            return rel;
-        }
-        final List<String> outputNameList = new ArrayList<>();
-        final List<RexNode> exprList = new ArrayList<>();
-        final List<RelDataTypeField> fields = rel.getRowType().getFieldList();
-        final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
-        for (int i = 0; i < mapping.getTargetCount(); i++) {
-            final int source = mapping.getSource(i);
-            final RelDataTypeField sourceField = fields.get(source);
-            outputNameList.add(
-                    ((fieldNames == null)
-                                    || (fieldNames.size() <= i)
-                                    || (fieldNames.get(i) == null))
-                            ? sourceField.getName()
-                            : fieldNames.get(i));
-            exprList.add(rexBuilder.makeInputRef(rel, source));
-        }
-        return projectFactory.createProject(
-                rel, ImmutableList.of(), exprList, outputNameList, ImmutableSet.of());
-    }
-
-    /** Predicate for if a {@link Calc} does not contain windowed aggregates. */
-    public static boolean notContainsWindowedAgg(Calc calc) {
-        return !calc.containsOver();
-    }
-
-    /** Predicate for if a {@link Filter} does not contain windowed aggregates. */
-    public static boolean notContainsWindowedAgg(Filter filter) {
-        return !filter.containsOver();
-    }
-
-    /** Predicate for if a {@link Project} does not contain windowed aggregates. */
-    public static boolean notContainsWindowedAgg(Project project) {
-        return !project.containsOver();
-    }
-
-    /** Policies for handling two- and three-valued boolean logic. */
-    public enum Logic {
-        /** Three-valued boolean logic. */
-        TRUE_FALSE_UNKNOWN,
-
-        /** Nulls are not possible. */
-        TRUE_FALSE,
-
-        /**
-         * Two-valued logic where UNKNOWN is treated as FALSE.
-         *
-         * <p>"x IS TRUE" produces the same result, and "WHERE x", "JOIN ... ON x" and "HAVING x"
-         * have the same effect.
-         */
-        UNKNOWN_AS_FALSE,
-
-        /**
-         * Two-valued logic where UNKNOWN is treated as TRUE.
-         *
-         * <p>"x IS FALSE" produces the same result, as does "WHERE NOT x", etc.
-         *
-         * <p>In particular, this is the mode used by "WHERE k NOT IN q". If "k IN q" produces TRUE
-         * or UNKNOWN, "NOT k IN q" produces FALSE or UNKNOWN and the row is eliminated; if "k IN q"
-         * it returns FALSE, the row is retained by the WHERE clause.
-         */
-        UNKNOWN_AS_TRUE,
-
-        /**
-         * A semi-join will have been applied, so that only rows for which the value is TRUE will
-         * have been returned.
-         */
-        TRUE,
-
-        /**
-         * An anti-semi-join will have been applied, so that only rows for which the value is FALSE
-         * will have been returned.
-         *
-         * <p>Currently only used within {@link LogicVisitor}, to ensure that 'NOT (NOT EXISTS (q))'
-         * behaves the same as 'EXISTS (q)')
-         */
-        FALSE;
-
-        public Logic negate() {
-            switch (this) {
-                case UNKNOWN_AS_FALSE:
-                case TRUE:
-                    return UNKNOWN_AS_TRUE;
-                case UNKNOWN_AS_TRUE:
-                    return UNKNOWN_AS_FALSE;
-                default:
-                    return this;
-            }
-        }
-
-        /**
-         * Variant of {@link #negate()} to be used within {@link LogicVisitor}, where FALSE values
-         * may exist.
-         */
-        public Logic negate2() {
-            switch (this) {
-                case FALSE:
-                    return TRUE;
-                case TRUE:
-                    return FALSE;
-                case UNKNOWN_AS_FALSE:
-                    return UNKNOWN_AS_TRUE;
-                case UNKNOWN_AS_TRUE:
-                    return UNKNOWN_AS_FALSE;
-                default:
-                    return this;
-            }
-        }
-    }
-
-    /**
-     * Pushes down expressions in "equal" join condition.
-     *
-     * <p>For example, given "emp JOIN dept ON emp.deptno + 1 = dept.deptno", adds a project above
-     * "emp" that computes the expression "emp.deptno + 1". The resulting join condition is a simple
-     * combination of AND, equals, and input fields, plus the remaining non-equal conditions.
-     *
-     * @param originalJoin Join whose condition is to be pushed down
-     * @param relBuilder Factory to create project operator
-     */
-    public static RelNode pushDownJoinConditions(Join originalJoin, RelBuilder relBuilder) {
-        RexNode joinCond = originalJoin.getCondition();
-        final JoinRelType joinType = originalJoin.getJoinType();
-
-        final List<RexNode> extraLeftExprs = new ArrayList<>();
-        final List<RexNode> extraRightExprs = new ArrayList<>();
-        final int leftCount = originalJoin.getLeft().getRowType().getFieldCount();
-        final int rightCount = originalJoin.getRight().getRowType().getFieldCount();
-
-        // You cannot push a 'get' because field names might change.
-        //
-        // Pushing sub-queries is OK in principle (if they don't reference both
-        // sides of the join via correlating variables) but we'd rather not do it
-        // yet.
-        if (!containsGet(joinCond) && RexUtil.SubQueryFinder.find(joinCond) == null) {
-            joinCond =
-                    pushDownEqualJoinConditions(
-                            joinCond,
-                            leftCount,
-                            rightCount,
-                            extraLeftExprs,
-                            extraRightExprs,
-                            relBuilder.getRexBuilder());
-        }
-
-        final PairList<RexNode, @Nullable String> pairs = PairList.of();
-        relBuilder.push(originalJoin.getLeft());
-        if (!extraLeftExprs.isEmpty()) {
-            final List<RelDataTypeField> fields = relBuilder.peek().getRowType().getFieldList();
-            for (int i = 0, n = leftCount + extraLeftExprs.size(); i < n; i++) {
-                if (i < leftCount) {
-                    RelDataTypeField field = fields.get(i);
-                    pairs.add(new RexInputRef(i, field.getType()), field.getName());
-                } else {
-                    pairs.add(extraLeftExprs.get(i - leftCount), null);
-                }
-            }
-            relBuilder.project(pairs.leftList(), pairs.rightList());
-            pairs.clear();
-        }
-
-        relBuilder.push(originalJoin.getRight());
-        if (!extraRightExprs.isEmpty()) {
-            final List<RelDataTypeField> fields = relBuilder.peek().getRowType().getFieldList();
-            final int newLeftCount = leftCount + extraLeftExprs.size();
-            for (int i = 0, n = rightCount + extraRightExprs.size(); i < n; i++) {
-                if (i < rightCount) {
-                    RelDataTypeField field = fields.get(i);
-                    pairs.add(new RexInputRef(i, field.getType()), field.getName());
-                } else {
-                    pairs.add(
-                            RexUtil.shift(extraRightExprs.get(i - rightCount), -newLeftCount),
-                            null);
-                }
-            }
-            relBuilder.project(pairs.leftList(), pairs.rightList());
-            pairs.clear();
-        }
-
-        final RelNode right = relBuilder.build();
-        final RelNode left = relBuilder.build();
-        if (joinType == JoinRelType.ASOF || joinType == JoinRelType.LEFT_ASOF) {
-            LogicalAsofJoin ljoin = (LogicalAsofJoin) originalJoin;
-            RexNode match =
-                    RexUtil.shift(ljoin.getMatchCondition(), leftCount, extraLeftExprs.size());
-            RelNode copy = ljoin.copy(originalJoin.getTraitSet(), joinCond, match, left, right);
-            relBuilder.push(copy);
-        } else {
-            relBuilder.push(
-                    originalJoin.copy(
-                            originalJoin.getTraitSet(),
-                            joinCond,
-                            left,
-                            right,
-                            joinType,
-                            originalJoin.isSemiJoinDone()));
-        }
-        if (!extraLeftExprs.isEmpty() || !extraRightExprs.isEmpty()) {
-            final int totalFields =
-                    joinType.projectsRight()
-                            ? leftCount
-                                    + extraLeftExprs.size()
-                                    + rightCount
-                                    + extraRightExprs.size()
-                            : leftCount + extraLeftExprs.size();
-            final int[] mappingRanges =
-                    joinType.projectsRight()
-                            ? new int[] {
-                                0,
-                                0,
-                                leftCount,
-                                leftCount,
-                                leftCount + extraLeftExprs.size(),
-                                rightCount
-                            }
-                            : new int[] {0, 0, leftCount};
-            Mappings.TargetMapping mapping =
-                    Mappings.createShiftMapping(totalFields, mappingRanges);
-            relBuilder.project(relBuilder.fields(mapping.inverse()));
-        }
-        return relBuilder.build();
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode pushDownJoinConditions(Join originalJoin) {
-        return pushDownJoinConditions(originalJoin, RelFactories.LOGICAL_BUILDER);
-    }
-
-    @Deprecated // to be removed before 2.0
-    public static RelNode pushDownJoinConditions(
-            Join originalJoin, RelFactories.ProjectFactory projectFactory) {
-        return pushDownJoinConditions(originalJoin, RelBuilder.proto(projectFactory));
-    }
-
-    private static RelNode pushDownJoinConditions(
-            Join originalJoin, RelBuilderFactory relBuilderFactory) {
-        return pushDownJoinConditions(
-                originalJoin, relBuilderFactory.create(originalJoin.getCluster(), null));
-    }
-
-    private static boolean containsGet(RexNode node) {
-        try {
-            node.accept(
-                    new RexVisitorImpl<Void>(true) {
-                        @Override
-                        public Void visitCall(RexCall call) {
-                            if (call.getOperator() == RexBuilder.GET_OPERATOR) {
-                                throw Util.FoundOne.NULL;
-                            }
-                            return super.visitCall(call);
-                        }
-                    });
-            return false;
-        } catch (Util.FoundOne e) {
-            return true;
-        }
-    }
-
-    /**
-     * Pushes down parts of a join condition.
-     *
-     * <p>For example, given "emp JOIN dept ON emp.deptno + 1 = dept.deptno", adds a project above
-     * "emp" that computes the expression "emp.deptno + 1". The resulting join condition is a simple
-     * combination of AND, equals, and input fields.
-     */
-    private static RexNode pushDownEqualJoinConditions(
-            RexNode condition,
-            int leftCount,
-            int rightCount,
-            List<RexNode> extraLeftExprs,
-            List<RexNode> extraRightExprs,
-            RexBuilder builder) {
-        // Normalize the condition first
-        RexNode node =
-                (condition instanceof RexCall)
-                        ? collapseExpandedIsNotDistinctFromExpr((RexCall) condition, builder)
-                        : condition;
-
-        switch (node.getKind()) {
-            case EQUALS:
-            case IS_NOT_DISTINCT_FROM:
-                final RexCall call0 = (RexCall) node;
-                final RexNode leftRex = call0.getOperands().get(0);
-                final RexNode rightRex = call0.getOperands().get(1);
-                final ImmutableBitSet leftBits = RelOptUtil.InputFinder.bits(leftRex);
-                final ImmutableBitSet rightBits = RelOptUtil.InputFinder.bits(rightRex);
-                final int pivot = leftCount + extraLeftExprs.size();
-                Side lside = Side.of(leftBits, pivot);
-                Side rside = Side.of(rightBits, pivot);
-                if (!lside.opposite(rside)) {
-                    return call0;
-                }
-            // fall through
-            case AND:
-                final RexCall call = (RexCall) node;
-                final List<RexNode> list = new ArrayList<>();
-                List<RexNode> operands = Lists.newArrayList(call.getOperands());
-                for (int i = 0; i < operands.size(); i++) {
-                    RexNode operand = operands.get(i);
-                    if (operand instanceof RexCall) {
-                        operand = collapseExpandedIsNotDistinctFromExpr((RexCall) operand, builder);
-                    }
-                    if (node.getKind() == SqlKind.AND
-                            && operand.getKind() != SqlKind.EQUALS
-                            && operand.getKind() != SqlKind.IS_NOT_DISTINCT_FROM) {
-                        // one of the join condition is neither EQ nor INDF
-                        list.add(operand);
-                    } else {
-                        final int left2 = leftCount + extraLeftExprs.size();
-                        final RexNode e =
-                                pushDownEqualJoinConditions(
-                                        operand,
-                                        leftCount,
-                                        rightCount,
-                                        extraLeftExprs,
-                                        extraRightExprs,
-                                        builder);
-                        if (!e.equals(operand)) {
-                            final List<RexNode> remainingOperands = Util.skip(operands, i + 1);
-                            final int left3 = leftCount + extraLeftExprs.size();
-                            fix(remainingOperands, left2, left3);
-                            fix(list, left2, left3);
-                        }
-                        list.add(e);
-                    }
-                }
-                if (!list.equals(call.getOperands())) {
-                    return call.clone(call.getType(), list);
-                }
-                return call;
-            case OR:
-            case INPUT_REF:
-            case LITERAL:
-            case NOT:
-                return node;
-            default:
-                final ImmutableBitSet bits = RelOptUtil.InputFinder.bits(node);
-                final int mid = leftCount + extraLeftExprs.size();
-                switch (Side.of(bits, mid)) {
-                    case LEFT:
-                        fix(extraRightExprs, mid, mid + 1);
-                        extraLeftExprs.add(node);
-                        return new RexInputRef(mid, node.getType());
-                    case RIGHT:
-                        final int index2 = mid + rightCount + extraRightExprs.size();
-                        extraRightExprs.add(node);
-                        return new RexInputRef(index2, node.getType());
-                    case BOTH:
-                    case EMPTY:
-                    default:
-                        return node;
-                }
-        }
-    }
-
-    private static void fix(List<RexNode> operands, int before, int after) {
-        if (before == after) {
-            return;
-        }
-        operands.replaceAll(e -> RexUtil.shift(e, before, after - before));
-    }
-
-    /**
-     * Determines whether any of the fields in a given relational expression may contain null
-     * values, taking into account constraints on the field types and also deduced predicates.
-     *
-     * <p>The method is cautious: It may sometimes return {@code true} when the actual answer is
-     * {@code false}. In particular, it does this when there is no executor, or the executor is not
-     * a sub-class of {@link RexExecutorImpl}.
-     */
-    private static boolean containsNullableFields(RelNode r) {
-        final RexBuilder rexBuilder = r.getCluster().getRexBuilder();
-        final RelDataType rowType = r.getRowType();
-        final List<RexNode> list = new ArrayList<>();
-        final RelMetadataQuery mq = r.getCluster().getMetadataQuery();
-        for (RelDataTypeField field : rowType.getFieldList()) {
-            if (field.getType().isNullable()) {
-                list.add(
-                        rexBuilder.makeCall(
-                                SqlStdOperatorTable.IS_NOT_NULL,
-                                rexBuilder.makeInputRef(field.getType(), field.getIndex())));
-            }
-        }
-        if (list.isEmpty()) {
-            // All columns are declared NOT NULL.
-            return false;
-        }
-        final RelOptPredicateList predicates = mq.getPulledUpPredicates(r);
-        if (RelOptPredicateList.isEmpty(predicates)) {
-            // We have no predicates, so cannot deduce that any of the fields
-            // declared NULL are really NOT NULL.
-            return true;
-        }
-        final RexExecutor executor = r.getCluster().getPlanner().getExecutor();
-        if (!(executor instanceof RexExecutorImpl)) {
-            // Cannot proceed without an executor.
-            return true;
-        }
-        final RexImplicationChecker checker =
-                new RexImplicationChecker(rexBuilder, executor, rowType);
-        final RexNode first = RexUtil.composeConjunction(rexBuilder, predicates.pulledUpPredicates);
-        final RexNode second = RexUtil.composeConjunction(rexBuilder, list);
-        // Suppose we have EMP(empno INT NOT NULL, mgr INT),
-        // and predicates [empno > 0, mgr > 0].
-        // We make first: "empno > 0 AND mgr > 0"
-        // and second: "mgr IS NOT NULL"
-        // and ask whether first implies second.
-        // It does, so we have no nullable columns.
-        return !checker.implies(first, second);
-    }
-
-    // ~ Inner Classes ----------------------------------------------------------
-
-    /**
-     * A {@code RelShuttle} which propagates all the hints of relational expression to their
-     * children nodes.
-     *
-     * <p>Given a plan:
-     *
-     * <blockquote>
-     *
-     * <pre>
-     *            Filter (Hint1)
-     *                |
-     *               Join
-     *              /    \
-     *            Scan  Project (Hint2)
-     *                     |
-     *                    Scan2
-     * </pre>
-     *
-     * </blockquote>
-     *
-     * <p>Every hint has a {@code inheritPath} (integers list) which records its propagate path,
-     * number `0` represents the hint is propagated from the first(left) child, number `1`
-     * represents the hint is propagated from the second(right) child, so the plan would have hints
-     * path as follows (assumes each hint can be propagated to all child nodes):
-     *
-     * <ul>
-     *   <li>Filter would have hints {Hint1[]}
-     *   <li>Join would have hints {Hint1[0]}
-     *   <li>Scan would have hints {Hint1[0, 0]}
-     *   <li>Project would have hints {Hint1[0,1], Hint2[]}
-     *   <li>Scan2 would have hints {[Hint1[0, 1, 0], Hint2[0]}
-     * </ul>
-     */
-    private static class RelHintPropagateShuttle extends RelHomogeneousShuttle {
-        /** Stack recording the hints and its current inheritPath. */
-        private final Deque<Pair<List<RelHint>, Deque<Integer>>> inheritPaths = new ArrayDeque<>();
-
-        /**
-         * The hint strategies to decide if a hint should be attached to a relational expression.
-         */
-        private final HintStrategyTable hintStrategies;
-
-        RelHintPropagateShuttle(HintStrategyTable hintStrategies) {
-            this.hintStrategies = hintStrategies;
-        }
-
-        /** Visits a particular child of a parent. */
-        @Override
-        protected RelNode visitChild(RelNode parent, int i, RelNode child) {
-            inheritPaths.forEach(inheritPath -> inheritPath.right.push(i));
-            try {
-                RelNode child2 = child.accept(this);
-                if (child2 != child) {
-                    final List<RelNode> newInputs = new ArrayList<>(parent.getInputs());
-                    newInputs.set(i, child2);
-                    return parent.copy(parent.getTraitSet(), newInputs);
-                }
-                return parent;
-            } finally {
-                inheritPaths.forEach(inheritPath -> inheritPath.right.pop());
-            }
-        }
-
-        @Override
-        public RelNode visit(RelNode other) {
-            if (other instanceof Hintable) {
-                return visitHintable(other);
-            } else {
-                return visitChildren(other);
-            }
-        }
-
-        /**
-         * Handle the {@link Hintable}s.
-         *
-         * <p>There are two cases to handle hints:
-         *
-         * <ul>
-         *   <li>For TableScan: table scan is always a leaf node, attach the hints of the
-         *       propagation path directly;
-         *   <li>For other {@link Hintable}s: if the node has hints itself, that means, these hints
-         *       are query hints that need to propagate to its children, so we do these things:
-         *       <ol>
-         *         <li>push the hints with empty inheritPath to the stack
-         *         <li>visit the children nodes and propagate the hints
-         *         <li>pop the hints pushed in step1
-         *         <li>attach the hints of the propagation path
-         *       </ol>
-         *       if the node does not have hints, attach the hints of the propagation path directly.
-         * </ul>
-         *
-         * @param node {@link Hintable} to handle
-         * @return New copy of the {@code hintable} with propagated hints attached
-         */
-        private RelNode visitHintable(RelNode node) {
-            final List<RelHint> topHints = ((Hintable) node).getHints();
-            final boolean hasHints = !topHints.isEmpty();
-            final boolean hasQueryHints = hasHints && !(node instanceof TableScan);
-            if (hasQueryHints) {
-                inheritPaths.push(Pair.of(topHints, new ArrayDeque<>()));
-            }
-            final RelNode node1 = visitChildren(node);
-            if (hasQueryHints) {
-                inheritPaths.pop();
-            }
-            return attachHints(node1);
-        }
-
-        private RelNode attachHints(RelNode original) {
-            assert original instanceof Hintable;
-            if (!inheritPaths.isEmpty()) {
-                final List<RelHint> hints =
-                        inheritPaths.stream()
-                                .sorted(Comparator.comparingInt(o -> o.right.size()))
-                                .map(path -> copyWithInheritPath(path.left, path.right))
-                                .reduce(
-                                        new ArrayList<>(),
-                                        (acc, hints1) -> {
-                                            acc.addAll(hints1);
-                                            return acc;
-                                        });
-                final List<RelHint> filteredHints = hintStrategies.apply(hints, original);
-                if (!filteredHints.isEmpty()) {
-                    return ((Hintable) original).attachHints(filteredHints);
-                }
-            }
-            return original;
-        }
-
-        private static List<RelHint> copyWithInheritPath(
-                List<RelHint> hints, Deque<Integer> inheritPath) {
-            // Copy the Dequeue in reverse order.
-            final List<Integer> path = new ArrayList<>();
-            final Iterator<Integer> iterator = inheritPath.descendingIterator();
-            while (iterator.hasNext()) {
-                path.add(iterator.next());
-            }
-            return hints.stream().map(hint -> hint.copy(path)).collect(Collectors.toList());
-        }
-    }
-
-    /**
-     * A {@code RelShuttle} which propagates the given hints to the sub-tree from the root node. It
-     * stops the search of current path if the node already has hints or the whole propagation if
-     * there is already a matched node.
-     *
-     * <p>Given a plan:
-     *
-     * <blockquote>
-     *
-     * <pre>
-     *            Filter
-     *                |
-     *               Join
-     *              /    \
-     *            Scan  Project (Hint2)
-     *                     |
-     *                    Scan2
-     * </pre>
-     *
-     * </blockquote>
-     *
-     * <p>The [Filter, Join, Scan] are the candidates(in sequence) to propagate, the whole
-     * propagation ends if we append the given hints to a node successfully.
-     */
-    private static class SubTreeHintPropagateShuttle extends RelHomogeneousShuttle {
-        /** Stack recording the appended inheritPath. */
-        private final List<Integer> appendPath = new ArrayList<>();
-
-        /**
-         * The hint strategies to decide if a hint should be attached to a relational expression.
-         */
-        private final HintStrategyTable hintStrategies;
-
-        /** Hints to propagate. */
-        private final List<RelHint> hints;
-
-        SubTreeHintPropagateShuttle(HintStrategyTable hintStrategies, List<RelHint> hints) {
-            this.hintStrategies = hintStrategies;
-            this.hints = hints;
-        }
-
-        /** Visits a particular child of a parent. */
-        @Override
-        protected RelNode visitChild(RelNode parent, int i, RelNode child) {
-            appendPath.add(i);
-            try {
-                RelNode child2 = child.accept(this);
-                if (child2 != child) {
-                    final List<RelNode> newInputs = new ArrayList<>(parent.getInputs());
-                    newInputs.set(i, child2);
-                    return parent.copy(parent.getTraitSet(), newInputs);
-                }
-                return parent;
-            } finally {
-                // Remove the last element.
-                appendPath.remove(appendPath.size() - 1);
-            }
-        }
-
-        @Override
-        public RelNode visit(RelNode other) {
-            if (this.appendPath.size() > 3) {
-                // Returns early if the visiting depth is bigger than 3
-                return other;
-            }
-            if (other instanceof Hintable) {
-                return visitHintable(other);
-            } else {
-                return visitChildren(other);
-            }
-        }
-
-        /**
-         * Handle the {@link Hintable}s.
-         *
-         * <p>Try to propagate the given hints to the node, the propagation finishes if:
-         *
-         * <ul>
-         *   <li>This hintable already has hints, that means, the rel is definitely not created by a
-         *       planner rule(or copied by the planner rule)
-         *   <li>This hintable appended the hints successfully
-         * </ul>
-         *
-         * @param node {@link Hintable} to handle
-         * @return New copy of the {@code hintable} with propagated hints attached
-         */
-        private RelNode visitHintable(RelNode node) {
-            final List<RelHint> topHints = ((Hintable) node).getHints();
-            final boolean hasHints = !topHints.isEmpty();
-            if (hasHints) {
-                // This node is definitely not created by the planner, returns early.
-                return node;
-            }
-            final RelNode node1 = attachHints(node);
-            if (node1 != node) {
-                return node1;
-            }
-            return visitChildren(node);
-        }
-
-        private RelNode attachHints(RelNode original) {
-            assert original instanceof Hintable;
-            final List<RelHint> hints =
-                    this.hints.stream()
-                            .map(hint -> copyWithAppendPath(hint, appendPath))
-                            .collect(Collectors.toList());
-            final List<RelHint> filteredHints = hintStrategies.apply(hints, original);
-            if (!filteredHints.isEmpty()) {
-                return ((Hintable) original).attachHints(filteredHints);
-            }
-            return original;
-        }
-
-        private static RelHint copyWithAppendPath(RelHint hint, List<Integer> appendPaths) {
-            if (appendPaths.isEmpty()) {
-                return hint;
-            } else {
-                List<Integer> newPath = new ArrayList<>(hint.inheritPath);
-                newPath.addAll(appendPaths);
-                return hint.copy(newPath);
-            }
-        }
-    }
-
-    /**
-     * A {@code RelShuttle} which resets all the hints of a relational expression to what they are
-     * originally like.
-     *
-     * <p>This would trigger a reverse transformation of what {@link RelHintPropagateShuttle} does.
-     *
-     * <p>Transformation rules:
-     *
-     * <ul>
-     *   <li>Project: remove the hints that have non-empty inherit path (which means the hint was
-     *       not originally declared from it);
-     *   <li>Aggregate: remove the hints that have non-empty inherit path;
-     *   <li>Join: remove all the hints;
-     *   <li>TableScan: remove the hints that have non-empty inherit path.
-     * </ul>
-     */
-    private static class ResetHintsShuttle extends RelHomogeneousShuttle {
-        @Override
-        public RelNode visit(RelNode node) {
-            node = visitChildren(node);
-            if (node instanceof Hintable) {
-                node = resetHints((Hintable) node);
-            }
-            return node;
-        }
-
-        private static RelNode resetHints(Hintable hintable) {
-            if (hintable.getHints().isEmpty()) {
-                return (RelNode) hintable;
-            }
-            final List<RelHint> resetHints =
-                    hintable.getHints().stream()
-                            .filter(hint -> hint.inheritPath.isEmpty())
-                            .collect(Collectors.toList());
-            return hintable.withHints(resetHints);
-        }
-    }
-
-    /** Visitor that finds all variables used but not stopped in an expression. */
-    private static class VariableSetVisitor extends RelVisitor {
-        final Set<CorrelationId> variables = new HashSet<>();
-
-        // implement RelVisitor
-        @Override
-        public void visit(
-                RelNode p,
-                int ordinal,
-                @org.checkerframework.checker.nullness.qual.Nullable RelNode parent) {
-            super.visit(p, ordinal, parent);
-            p.collectVariablesUsed(variables);
-
-            // Important! Remove stopped variables AFTER we visit children
-            // (which what super.visit() does)
-            variables.removeAll(p.getVariablesSet());
-        }
-    }
-
-    /** Visitor that finds all variables used in an expression. */
-    public static class VariableUsedVisitor extends RexShuttle {
-        public final Set<CorrelationId> variables = new LinkedHashSet<>();
-        public final Multimap<CorrelationId, Integer> variableFields = LinkedHashMultimap.create();
-        @NotOnlyInitialized private final @Nullable RelShuttle relShuttle;
-
-        public VariableUsedVisitor(@UnknownInitialization @Nullable RelShuttle relShuttle) {
-            this.relShuttle = relShuttle;
-        }
-
-        @Override
-        public RexNode visitCorrelVariable(RexCorrelVariable p) {
-            variables.add(p.id);
-            variableFields.put(p.id, -1);
-            return p;
-        }
-
-        @Override
-        public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
-            if (fieldAccess.getReferenceExpr() instanceof RexCorrelVariable) {
-                final RexCorrelVariable v = (RexCorrelVariable) fieldAccess.getReferenceExpr();
-                variableFields.put(v.id, fieldAccess.getField().getIndex());
-            }
-            return super.visitFieldAccess(fieldAccess);
-        }
-
-        @Override
-        public RexNode visitSubQuery(RexSubQuery subQuery) {
-            if (relShuttle != null) {
-                subQuery.rel.accept(relShuttle); // look inside sub-queries
-            }
-            return super.visitSubQuery(subQuery);
-        }
-    }
-
-    /** Shuttle that finds the set of inputs that are used. */
-    public static class InputReferencedVisitor extends RexShuttle {
-        public final NavigableSet<Integer> inputPosReferenced = new TreeSet<>();
-
-        @Override
-        public RexNode visitInputRef(RexInputRef inputRef) {
-            inputPosReferenced.add(inputRef.getIndex());
-            return inputRef;
-        }
-    }
-
-    /** Converts types to descriptive strings. */
-    public static class TypeDumper {
-        private String indent;
-        private final PrintWriter pw;
-
-        TypeDumper(PrintWriter pw) {
-            this.pw = pw;
-            this.indent = "";
-        }
-
-        void accept(RelDataType type) {
-            if (type.isStruct()) {
-                final List<RelDataTypeField> fields = type.getFieldList();
-
-                // RECORD (
-                //   I INTEGER NOT NULL,
-                //   J VARCHAR(240))
-                pw.println("RECORD (");
-                String prevIndent = indent;
-                String extraIndent = "  ";
-                this.indent = indent + extraIndent;
-                acceptFields(fields);
-                this.indent = prevIndent;
-                pw.print(")");
-                if (!type.isNullable()) {
-                    pw.print(NON_NULLABLE_SUFFIX);
-                }
-            } else if (type instanceof MultisetSqlType) {
-                // E.g. "INTEGER NOT NULL MULTISET NOT NULL"
-                RelDataType componentType =
-                        requireNonNull(
-                                type.getComponentType(),
-                                () -> "type.getComponentType() for " + type);
-                accept(componentType);
-                pw.print(" MULTISET");
-                if (!type.isNullable()) {
-                    pw.print(NON_NULLABLE_SUFFIX);
-                }
-            } else {
-                // E.g. "INTEGER" E.g. "VARCHAR(240) CHARACTER SET "ISO-8859-1"
-                // COLLATE "ISO-8859-1$en_US$primary" NOT NULL"
-                pw.print(type.getFullTypeString());
-            }
-        }
-
-        private void acceptFields(final List<RelDataTypeField> fields) {
-            for (int i = 0; i < fields.size(); i++) {
-                RelDataTypeField field = fields.get(i);
-                if (i > 0) {
-                    pw.println(",");
-                }
-                pw.print(indent);
-                pw.print(field.getName());
-                pw.print(" ");
-                accept(field.getType());
-            }
-        }
-    }
-
-    /** Visitor which builds a bitmap of the inputs used by an expression. */
-    public static class InputFinder extends RexVisitorImpl<Void> {
-        private final ImmutableBitSet.Builder bitBuilder;
-        private final @Nullable Set<RelDataTypeField> extraFields;
-
-        private InputFinder(
-                @Nullable Set<RelDataTypeField> extraFields, ImmutableBitSet.Builder bitBuilder) {
-            super(true);
-            this.bitBuilder = bitBuilder;
-            this.extraFields = extraFields;
-        }
-
-        public InputFinder() {
-            this(null);
-        }
-
-        public InputFinder(@Nullable Set<RelDataTypeField> extraFields) {
-            this(extraFields, ImmutableBitSet.builder());
-        }
-
-        public InputFinder(
-                @Nullable Set<RelDataTypeField> extraFields, ImmutableBitSet initialBits) {
-            this(extraFields, initialBits.rebuild());
-        }
-
-        /** Returns an input finder that has analyzed a given expression. */
-        public static InputFinder analyze(RexNode node) {
-            final InputFinder inputFinder = new InputFinder();
-            node.accept(inputFinder);
-            return inputFinder;
-        }
-
-        /** Returns a bit set describing the inputs used by an expression. */
-        public static ImmutableBitSet bits(RexNode node) {
-            return analyze(node).build();
-        }
-
-        /**
-         * Returns a bit set describing the inputs used by a collection of project expressions and
-         * an optional condition.
-         */
-        public static ImmutableBitSet bits(List<RexNode> exprs, @Nullable RexNode expr) {
-            final InputFinder inputFinder = new InputFinder();
-            RexUtil.apply(inputFinder, exprs, expr);
-            return inputFinder.build();
-        }
-
-        /**
-         * Returns the bit set.
-         *
-         * <p>After calling this method, you cannot do any more visits or call this method again.
-         */
-        public ImmutableBitSet build() {
-            return bitBuilder.build();
-        }
-
-        @Override
-        public Void visitInputRef(RexInputRef inputRef) {
-            bitBuilder.set(inputRef.getIndex());
-            return null;
-        }
-
-        @Override
-        public Void visitCall(RexCall call) {
-            if (call.getOperator() == RexBuilder.GET_OPERATOR) {
-                RexLiteral literal = (RexLiteral) call.getOperands().get(1);
-                if (extraFields != null) {
-                    requireNonNull(literal, () -> "first operand in " + call);
-                    String value2 = (String) literal.getValue2();
-                    requireNonNull(value2, () -> "value of the first operand in " + call);
-                    extraFields.add(new RelDataTypeFieldImpl(value2, -1, call.getType()));
-                }
-            }
-            return super.visitCall(call);
-        }
-    }
-
-    /**
-     * Walks an expression tree, converting the index of RexInputRefs based on some adjustment
-     * factor.
-     */
-    public static class RexInputConverter extends RexShuttle {
-        protected final RexBuilder rexBuilder;
-        private final @Nullable List<RelDataTypeField> srcFields;
-        protected final @Nullable List<RelDataTypeField> destFields;
-        private final @Nullable List<RelDataTypeField> leftDestFields;
-        private final @Nullable List<RelDataTypeField> rightDestFields;
-        private final int nLeftDestFields;
-        private final int[] adjustments;
-
-        /**
-         * Creates a RexInputConverter.
-         *
-         * @param rexBuilder builder for creating new RexInputRefs
-         * @param srcFields fields where the RexInputRefs originated from; if null, a new
-         *     RexInputRef is always created, referencing the input from destFields corresponding to
-         *     its current index value
-         * @param destFields fields that the new RexInputRefs will be referencing; if null, use the
-         *     type information from the source field when creating the new RexInputRef
-         * @param leftDestFields in the case where the destination is a join, these are the fields
-         *     from the left join input
-         * @param rightDestFields in the case where the destination is a join, these are the fields
-         *     from the right join input
-         * @param adjustments the amount to adjust each field by
-         */
-        private RexInputConverter(
-                RexBuilder rexBuilder,
-                @Nullable List<RelDataTypeField> srcFields,
-                @Nullable List<RelDataTypeField> destFields,
-                @Nullable List<RelDataTypeField> leftDestFields,
-                @Nullable List<RelDataTypeField> rightDestFields,
-                int[] adjustments) {
-            this.rexBuilder = rexBuilder;
-            this.srcFields = srcFields;
-            this.destFields = destFields;
-            this.adjustments = adjustments;
-            this.leftDestFields = leftDestFields;
-            this.rightDestFields = rightDestFields;
-            if (leftDestFields == null) {
-                nLeftDestFields = 0;
-            } else {
-                assert destFields == null;
-                nLeftDestFields = leftDestFields.size();
-            }
-        }
-
-        public RexInputConverter(
-                RexBuilder rexBuilder,
-                @Nullable List<RelDataTypeField> srcFields,
-                @Nullable List<RelDataTypeField> leftDestFields,
-                @Nullable List<RelDataTypeField> rightDestFields,
-                int[] adjustments) {
-            this(rexBuilder, srcFields, null, leftDestFields, rightDestFields, adjustments);
-        }
-
-        public RexInputConverter(
-                RexBuilder rexBuilder,
-                @Nullable List<RelDataTypeField> srcFields,
-                @Nullable List<RelDataTypeField> destFields,
-                int[] adjustments) {
-            this(rexBuilder, srcFields, destFields, null, null, adjustments);
-        }
-
-        public RexInputConverter(
-                RexBuilder rexBuilder,
-                @Nullable List<RelDataTypeField> srcFields,
-                int[] adjustments) {
-            this(rexBuilder, srcFields, null, null, null, adjustments);
-        }
-
-        @Override
-        public RexNode visitInputRef(RexInputRef var) {
-            int srcIndex = var.getIndex();
-            int destIndex = srcIndex + adjustments[srcIndex];
-
-            RelDataType type;
-            if (destFields != null) {
-                type = destFields.get(destIndex).getType();
-            } else if (leftDestFields != null) {
-                if (destIndex < nLeftDestFields) {
-                    type = leftDestFields.get(destIndex).getType();
-                } else {
-                    type =
-                            requireNonNull(rightDestFields, "rightDestFields")
-                                    .get(destIndex - nLeftDestFields)
-                                    .getType();
-                }
-            } else {
-                type = requireNonNull(srcFields, "srcFields").get(srcIndex).getType();
-            }
-            if ((adjustments[srcIndex] != 0)
-                    || (srcFields == null)
-                    || (type != srcFields.get(srcIndex).getType())) {
-                return rexBuilder.makeInputRef(type, destIndex);
-            } else {
-                return var;
-            }
-        }
-    }
-
-    /** What kind of sub-query. */
-    public enum SubQueryType {
-        EXISTS,
-        IN,
-        SCALAR
-    }
-
-    /** Categorizes whether a bit set contains bits left and right of a line. */
-    enum Side {
-        LEFT,
-        RIGHT,
-        BOTH,
-        EMPTY;
-
-        static Side of(ImmutableBitSet bitSet, int middle) {
-            final int firstBit = bitSet.nextSetBit(0);
-            if (firstBit < 0) {
-                return EMPTY;
-            }
-            if (firstBit >= middle) {
-                return RIGHT;
-            }
-            if (bitSet.nextSetBit(middle) < 0) {
-                return LEFT;
-            }
-            return BOTH;
-        }
-
-        public boolean opposite(Side side) {
-            return (this == LEFT && side == RIGHT) || (this == RIGHT && side == LEFT);
-        }
-    }
-
-    /**
-     * Shuttle that finds correlation variables inside a given relational expression, including
-     * those that are inside {@link RexSubQuery sub-queries}.
-     */
-    private static class CorrelationCollector extends RelHomogeneousShuttle {
-        @SuppressWarnings("assignment.type.incompatible")
-        private final VariableUsedVisitor vuv = new VariableUsedVisitor(this);
-
-        @Override
-        public RelNode visit(RelNode other) {
-            other.collectVariablesUsed(vuv.variables);
-            other.accept(vuv);
-            RelNode result = super.visit(other);
-            // Important! Remove stopped variables AFTER we visit
-            // children. (which what super.visit() does)
-            vuv.variables.removeAll(other.getVariablesSet());
-            return result;
-        }
-    }
-
-    /** Result of calling {@link org.apache.calcite.plan.RelOptUtil#createExistsPlan}. */
-    public static class Exists {
-        public final RelNode r;
-        public final boolean indicator;
-        public final boolean outerJoin;
-
-        private Exists(RelNode r, boolean indicator, boolean outerJoin) {
-            this.r = r;
-            this.indicator = indicator;
-            this.outerJoin = outerJoin;
-        }
-    }
-}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
index 7363bb119f8..0114ad194fd 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
@@ -27,6 +27,7 @@ import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.metadata.RelMdUtil;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rex.LogicVisitor;
 import org.apache.calcite.rex.RexInputRef;
@@ -161,6 +162,12 @@ public class SubQueryRemoveRule extends RelRule<SubQueryRemoveRule.Config>
      */
     private static RexNode rewriteSome(
             RexSubQuery e, Set<CorrelationId> variablesSet, RelBuilder builder, int subQueryIndex) {
+        // If the sub-query is guaranteed empty, just return
+        // FALSE.
+        final RelMetadataQuery mq = e.rel.getCluster().getMetadataQuery();
+        if (RelMdUtil.isRelDefinitelyEmpty(mq, e.rel)) {
+            return builder.getRexBuilder().makeLiteral(Boolean.FALSE, e.getType(), true);
+        }
         // Most general case, where the left and right keys might have nulls, and
         // caller requires 3-valued logic return.
         //
@@ -397,22 +404,32 @@ public class SubQueryRemoveRule extends RelRule<SubQueryRemoveRule.Config>
                     //     then false // sub-query is empty for corresponding corr value
                     //   when q.c = 0 then false // sub-query is empty
                     //   when e.deptno is null then unknown
-                    //   when q.c <> q.d && q.d <= 1
+                    //   when q.c <> q.d && q.dd <= 1
                     //     then e.deptno != m || unknown
-                    //   when q.d = 1
+                    //   when q.dd = 1
                     //     then e.deptno != m // sub-query has the distinct result
                     //   else true
                     //   end as v
                     // from emp as e
                     // left outer join (
-                    //   select name, count(distinct *) as c, count(distinct deptno) as d,
+                    //   select name, count(*) as c, count(deptno) as d, count(distinct deptno) as
+                    // dd,
                     //       max(deptno) as m, "alwaysTrue" as indicator
                     //   from emp group by name) as q on e.name = q.name
+
+                    // Additional details on the `q.c <> q.d && q.dd <= 1` clause:
+                    // the q.c <> q.d comparison identifies if there are any null values,
+                    // since count(*) counts null values and count(deptno) does not.
+                    // if there's no null value, c should be equal to d.
+                    // the q.dd <= 1 part means: true if there is at most one non-null value
+                    // so this clause means:
+                    // "if there are any null values and there is at most one non-null value".
                     builder.push(e.rel)
                             .aggregate(
                                     builder.groupKey(),
-                                    builder.count(true, "c"),
-                                    builder.count(true, "d", builder.field(0)),
+                                    builder.count(false, "c"),
+                                    builder.count(false, "d", builder.field(0)),
+                                    builder.count(true, "dd", builder.field(0)),
                                     builder.max(builder.field(0)).as("m"));
 
                     parentQueryFields.addAll(builder.fields());
@@ -432,12 +449,12 @@ public class SubQueryRemoveRule extends RelRule<SubQueryRemoveRule.Config>
                                             builder.notEquals(
                                                     builder.field("d"), builder.field("c")),
                                             builder.lessThanOrEqual(
-                                                    builder.field("d"), builder.literal(1))),
+                                                    builder.field("dd"), builder.literal(1))),
                                     builder.or(
                                             builder.notEquals(
                                                     e.operands.get(0), builder.field(qAlias, "m")),
                                             literalUnknown),
-                                    builder.equals(builder.field("d"), builder.literal(1)),
+                                    builder.equals(builder.field("dd"), builder.literal(1)),
                                     builder.notEquals(
                                             e.operands.get(0), builder.field(qAlias, "m")),
                                     literalTrue);
@@ -474,15 +491,13 @@ public class SubQueryRemoveRule extends RelRule<SubQueryRemoveRule.Config>
             Set<CorrelationId> variablesSet,
             RelOptUtil.Logic logic,
             RelBuilder builder) {
-        // If the sub-query is guaranteed to produce at least one row, just return
+        // If the sub-query is guaranteed never empty, just return
         // TRUE.
         final RelMetadataQuery mq = e.rel.getCluster().getMetadataQuery();
-        final Double minRowCount = mq.getMinRowCount(e.rel);
-        if (minRowCount != null && minRowCount >= 1D) {
+        if (RelMdUtil.isRelDefinitelyNotEmpty(mq, e.rel)) {
             return builder.literal(true);
         }
-        final Double maxRowCount = mq.getMaxRowCount(e.rel);
-        if (maxRowCount != null && maxRowCount < 1D) {
+        if (RelMdUtil.isRelDefinitelyEmpty(mq, e.rel)) {
             return builder.literal(false);
         }
         builder.push(e.rel);
@@ -573,6 +588,12 @@ public class SubQueryRemoveRule extends RelRule<SubQueryRemoveRule.Config>
             RelBuilder builder,
             int offset,
             int subQueryIndex) {
+        // If the sub-query is guaranteed empty, just return
+        // FALSE.
+        final RelMetadataQuery mq = e.rel.getCluster().getMetadataQuery();
+        if (RelMdUtil.isRelDefinitelyEmpty(mq, e.rel)) {
+            return builder.getRexBuilder().makeLiteral(Boolean.FALSE, e.getType(), true);
+        }
         // Most general case, where the left and right keys might have nulls, and
         // caller requires 3-valued logic return.
         //
@@ -700,16 +721,14 @@ public class SubQueryRemoveRule extends RelRule<SubQueryRemoveRule.Config>
                     if (variablesSet.isEmpty()) {
                         builder.aggregate(
                                 builder.groupKey(builder.field("cs")), builder.count(false, "c"));
-
-                        // sorts input with desc order since we are interested
-                        // only in the case when one of the values is true.
-                        // When true value is absent then we are interested
-                        // only in false value.
-                        builder.sortLimit(
-                                0, 1, ImmutableList.of(builder.desc(builder.field("cs"))));
                     } else {
                         builder.distinct();
                     }
+                    // sorts input with desc order since we are interested
+                    // only in the case when one of the values is true.
+                    // When true value is absent then we are interested
+                    // only in false value.
+                    builder.sortLimit(0, 1, ImmutableList.of(builder.desc(builder.field("cs"))));
             }
             // clears expressionOperands and fields lists since
             // all expressions were used in the filter
@@ -908,14 +927,58 @@ public class SubQueryRemoveRule extends RelRule<SubQueryRemoveRule.Config>
         final RexSubQuery e = requireNonNull(RexUtil.SubQueryFinder.find(join.getCondition()));
         final RelOptUtil.Logic logic =
                 LogicVisitor.find(RelOptUtil.Logic.TRUE, ImmutableList.of(join.getCondition()), e);
-        builder.push(join.getLeft());
-        builder.push(join.getRight());
-        final int fieldCount = join.getRowType().getFieldCount();
+
+        ImmutableBitSet inputSet = RelOptUtil.InputFinder.bits(e.getOperands(), null);
+        int nFieldsLeft = join.getLeft().getRowType().getFieldCount();
+        int nFieldsRight = join.getRight().getRowType().getFieldCount();
+
+        boolean inputIntersectsLeftSide =
+                inputSet.intersects(ImmutableBitSet.range(0, nFieldsLeft));
+        boolean inputIntersectsRightSide =
+                inputSet.intersects(ImmutableBitSet.range(nFieldsLeft, nFieldsLeft + nFieldsRight));
+        if (inputIntersectsLeftSide && inputIntersectsRightSide) {
+            // The current existential rewrite needs to make join with one side of the origin join
+            // and
+            // generate a new condition to replace the on clause. But for RexNode whose operands are
+            // on either side of the join, we can't push them into join. So this rewriting is not
+            // supported.
+            return;
+        }
+
         final Set<CorrelationId> variablesSet = RelOptUtil.getVariablesUsed(e.rel);
-        final RexNode target = rule.apply(e, variablesSet, logic, builder, 2, fieldCount, 0);
-        final RexShuttle shuttle = new ReplaceSubQueryShuttle(e, target);
-        builder.join(join.getJoinType(), shuttle.apply(join.getCondition()));
-        builder.project(fields(builder, join.getRowType().getFieldCount()));
+        if (inputIntersectsLeftSide) {
+            builder.push(join.getLeft());
+
+            final RexNode target = rule.apply(e, variablesSet, logic, builder, 1, nFieldsLeft, 0);
+            final RexShuttle shuttle = new ReplaceSubQueryShuttle(e, target);
+
+            final RexNode newCond =
+                    shuttle.apply(
+                            RexUtil.shift(
+                                    join.getCondition(),
+                                    nFieldsLeft,
+                                    builder.fields().size() - nFieldsLeft));
+            builder.push(join.getRight());
+            builder.join(join.getJoinType(), newCond);
+
+            final int nFields = builder.fields().size();
+            ImmutableList<RexNode> fields =
+                    builder.fields(
+                            ImmutableBitSet.range(0, nFieldsLeft)
+                                    .union(ImmutableBitSet.range(nFields - nFieldsRight, nFields)));
+            builder.project(fields);
+        } else {
+            builder.push(join.getLeft());
+            builder.push(join.getRight());
+
+            final int nFields = join.getRowType().getFieldCount();
+            final RexNode target = rule.apply(e, variablesSet, logic, builder, 2, nFields, 0);
+            final RexShuttle shuttle = new ReplaceSubQueryShuttle(e, target);
+
+            builder.join(join.getJoinType(), shuttle.apply(join.getCondition()));
+            builder.project(fields(builder, nFields));
+        }
+
         call.transformTo(builder.build());
     }
 
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
index 9b395adec1f..0fbbb0be86d 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
@@ -58,8 +58,8 @@ import static org.apache.calcite.util.ReflectUtil.isStatic;
  * <p>FLINK modifications are at lines
  *
  * <ol>
- *   <li>Should be removed after fixing CALCITE-5199: Lines 242-244
- *   <li>Added in FLINK-39695 (backport of CALCITE-6764): Lines 407 ~ 438
+ *   <li>Should be removed after fixing CALCITE-5199: Lines 241-243
+ *   <li>Preserve FLOAT precision as in Calcite 1.38.0: Lines 592 ~ 594
  * </ol>
  */
 public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
@@ -403,10 +403,7 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
         return canonize(newType);
     }
 
-    // ----- FLINK MODIFICATION BEGIN -----
-    // Backport from Calcite (CALCITE-6764): creates a type with specified nullability
-    // without deep-copying record field types. For record types, makes the struct
-    // itself nullable/not-nullable while keeping field types unchanged.
+    @Override
     public RelDataType enforceTypeWithNullability(final RelDataType type, final boolean nullable) {
         requireNonNull(type, "type");
         RelDataType newType;
@@ -434,8 +431,6 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
         return canonize(newType);
     }
 
-    // ----- FLINK MODIFICATION END -----
-
     /**
      * Registers a type, or returns the existing type if it is already registered.
      *
@@ -672,6 +667,13 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
         protected void generateTypeString(StringBuilder sb, boolean withDetail) {
             sb.append("JavaType(");
             sb.append(clazz);
+            if (clazz == String.class
+                    && charset != null
+                    && !SqlCollation.IMPLICIT.getCharset().equals(charset)) {
+                sb.append(" CHARACTER SET \"");
+                sb.append(charset.name());
+                sb.append("\"");
+            }
             sb.append(")");
         }
 
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexBuilder.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexBuilder.java
index bd08e57eda3..525870e2e57 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -73,6 +73,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.UUID;
 import java.util.function.IntPredicate;
 import java.util.stream.Collectors;
 
@@ -87,9 +88,7 @@ import static org.apache.calcite.linq4j.Nullness.castNonNull;
  *
  * <p>Some common literal values (NULL, TRUE, FALSE, 0, 1, '') are cached.
  *
- * <p>FLINK modifications (backport of CALCITE-6764): Lines 237, 243, 247, 251 ~ 255
- *
- * <p>FLINK modifications (backport of FLINK-39945): Lines 1270-1284
+ * <p>FLINK modifications (because of FLINK-39945): Lines 858-860, 1277-1294
  */
 public class RexBuilder {
     /**
@@ -189,6 +188,10 @@ public class RexBuilder {
      */
     public RexNode makeFieldAccess(RexNode expr, String fieldName, boolean caseSensitive) {
         final RelDataType type = expr.getType();
+        if (type.getSqlTypeName() == SqlTypeName.VARIANT) {
+            // VARIANT.field is rewritten as an VARIANT[field]
+            return this.makeCall(SqlStdOperatorTable.ITEM, expr, this.makeLiteral(fieldName));
+        }
         final RelDataTypeField field = type.getField(fieldName, caseSensitive, false);
         if (field == null) {
             throw new AssertionError("Type '" + type + "' has no field '" + fieldName + "'");
@@ -234,7 +237,7 @@ public class RexBuilder {
         }
 
         if (expr.getType().isNullable()) {
-            fieldType = typeFactory.createTypeWithNullability(fieldType, true);
+            fieldType = typeFactory.enforceTypeWithNullability(fieldType, true);
         }
         return new RexFieldAccess(expr, field, fieldType);
     }
@@ -810,7 +813,9 @@ public class RexBuilder {
             return true;
         }
         final SqlTypeName sqlType = toType.getSqlTypeName();
-        if (sqlType == SqlTypeName.MEASURE) {
+        if (sqlType == SqlTypeName.MEASURE
+                || sqlType == SqlTypeName.VARIANT
+                || sqlType == SqlTypeName.UUID) {
             return false;
         }
         if (!RexLiteral.valueMatchesType(value, sqlType, false)) {
@@ -850,7 +855,9 @@ public class RexBuilder {
         if (toType.getSqlTypeName() == SqlTypeName.DECIMAL
                 && fromTypeName.getFamily() == SqlTypeFamily.NUMERIC) {
             final BigDecimal decimalValue = (BigDecimal) value;
+            // FLINK MODIFICATION BEGIN
             return SqlTypeUtil.isValidDecimalValue(decimalValue, toType);
+            // FLINK MODIFICATION END
         }
 
         if (SqlTypeName.INT_TYPES.contains(sqlType)) {
@@ -1269,19 +1276,22 @@ public class RexBuilder {
             /*
             FLINK MODIFICATION BEGIN
             case DECIMAL:
-                if (o != null && type.getScale() != RelDataType.SCALE_NOT_SPECIFIED) {
-                    assert o instanceof BigDecimal;
-                    o =
-                            ((BigDecimal) o)
-                                    .setScale(
-                                            type.getScale(),
-                                            typeFactory.getTypeSystem().roundingMode());
+                if (o == null) {
+                    break;
+                }
+                assert o instanceof BigDecimal;
+                if (type instanceof IntervalSqlType) {
+                    SqlIntervalQualifier qualifier = ((IntervalSqlType) type).getIntervalQualifier();
+                    o = ((BigDecimal) o).multiply(qualifier.getUnit().multiplier);
+                    typeName = type.getSqlTypeName();
+                } else if (type.getScale() != RelDataType.SCALE_NOT_SPECIFIED) {
+                    o = ((BigDecimal) o).setScale(type.getScale(), typeFactory.getTypeSystem().roundingMode());
                     if (type.getScale() < 0) {
                         o = new BigDecimal(((BigDecimal) o).toPlainString());
                     }
                 }
                 break;
-                FLINK MODIFICATION ENF
+                FLINK MODIFICATION END
                  */
             default:
                 break;
@@ -1299,6 +1309,10 @@ public class RexBuilder {
         return b ? booleanTrue : booleanFalse;
     }
 
+    public RexLiteral makeUuidLiteral(@Nullable UUID uuid) {
+        return new RexLiteral(uuid, typeFactory.createSqlType(SqlTypeName.UUID), SqlTypeName.UUID);
+    }
+
     /** Creates a numeric literal. */
     @SuppressWarnings("deprecation") // [CALCITE-6598]
     public RexLiteral makeExactLiteral(BigDecimal bd) {
@@ -1639,11 +1653,16 @@ public class RexBuilder {
      * <p>If all of the expressions are literals, creates a call {@link Sarg} literal, "SEARCH(arg,
      * SARG([point0..point0], [point1..point1], ...)"; otherwise creates a disjunction, "arg =
      * point0 OR arg = point1 OR ...".
+     *
+     * <p>If there is only a single expression, creates a plain equality {@code arg = point0}.
      */
     public RexNode makeIn(RexNode arg, List<? extends RexNode> ranges) {
         if (areAssignable(arg, ranges)) {
             final Sarg sarg = toSarg(Comparable.class, ranges, RexUnknownAs.UNKNOWN);
             if (sarg != null) {
+                if (sarg.isPoints() && sarg.pointCount == 1) {
+                    return makeCall(SqlStdOperatorTable.EQUALS, arg, ranges.get(0));
+                }
                 final List<RelDataType> types =
                         ranges.stream().map(RexNode::getType).collect(Collectors.toList());
                 RelDataType sargType =
@@ -1884,7 +1903,7 @@ public class RexBuilder {
             case CHAR:
                 final NlsString nlsString = (NlsString) value;
                 if (trim) {
-                    return makeCharLiteral(nlsString.rtrim());
+                    return makeCharLiteral(nlsString.rtrim(type.getPrecision()));
                 } else {
                     return makeCharLiteral(padRight(nlsString, type.getPrecision()));
                 }
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexChecker.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexChecker.java
deleted file mode 100644
index 572f1192f65..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexChecker.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.rex;
-
-import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.util.Litmus;
-
-import java.util.List;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * Visitor which checks the validity of a {@link RexNode} expression.
- *
- * <p>FLINK modifications (backport of CALCITE-6764): Lines 121 ~ 133, 164 ~ 177
- *
- * <p>There are two modes of operation:
- *
- * <ul>
- *   <li>{@link Litmus#THROW} causes the checker to throw an {@link AssertionError} as soon as an
- *       invalid node is detected. Note: This mode requires that assertions are enabled.
- *       <blockquote>
- *       <pre>
- * // Example usage of Litmus.THROW
- * RexNode node;
- * RelDataType rowType;
- * RexChecker checker = new RexChecker(rowType, Litmus.THROW);
- * node.accept(checker); // Will throw an AssertionError if node is invalid
- * </pre>
- *       </blockquote>
- *   <li>{@link Litmus#IGNORE} tests validity without throwing an error.
- *       <blockquote>
- *       <pre>
- * // Example usage of Litmus.IGNORE
- * RexNode node;
- * RelDataType rowType;
- * RexChecker checker = new RexChecker(rowType, Litmus.IGNORE);
- * node.accept(checker);
- * if (!checker.isValid()) {
- *     // Handle invalid node
- * }
- * </pre>
- *       </blockquote>
- * </ul>
- *
- * @see RexNode
- */
-public class RexChecker extends RexVisitorImpl<Boolean> {
-    // ~ Instance fields --------------------------------------------------------
-
-    protected final RelNode.Context context;
-    protected final Litmus litmus;
-    protected final List<RelDataType> inputTypeList;
-    protected int failCount;
-
-    // ~ Constructors -----------------------------------------------------------
-
-    /**
-     * Creates a RexChecker with a given input row type.
-     *
-     * <p>If <code>fail</code> is true, the checker will throw an {@link AssertionError} if an
-     * invalid node is found and assertions are enabled.
-     *
-     * <p>Otherwise, each method returns whether its part of the tree is valid.
-     *
-     * @param inputRowType Input row type
-     * @param context Context of the enclosing {@link RelNode}, or null
-     * @param litmus What to do if an invalid node is detected
-     */
-    public RexChecker(final RelDataType inputRowType, RelNode.Context context, Litmus litmus) {
-        this(RelOptUtil.getFieldTypeList(inputRowType), context, litmus);
-    }
-
-    /**
-     * Creates a RexChecker with a given set of input fields.
-     *
-     * <p>If <code>fail</code> is true, the checker will throw an {@link AssertionError} if an
-     * invalid node is found and assertions are enabled.
-     *
-     * <p>Otherwise, each method returns whether its part of the tree is valid.
-     *
-     * @param inputTypeList Input row type
-     * @param context Context of the enclosing {@link RelNode}, or null
-     * @param litmus What to do if an error is detected
-     */
-    public RexChecker(List<RelDataType> inputTypeList, RelNode.Context context, Litmus litmus) {
-        super(true);
-        this.inputTypeList = inputTypeList;
-        this.context = context;
-        this.litmus = litmus;
-    }
-
-    // ~ Methods ----------------------------------------------------------------
-
-    /**
-     * Returns the number of failures encountered.
-     *
-     * @return Number of failures
-     */
-    public int getFailureCount() {
-        return failCount;
-    }
-
-    @Override
-    public Boolean visitInputRef(RexInputRef ref) {
-        final int index = ref.getIndex();
-        if ((index < 0) || (index >= inputTypeList.size())) {
-            ++failCount;
-            return litmus.fail(
-                    "RexInputRef index {} out of range 0..{}", index, inputTypeList.size() - 1);
-        }
-        // Type of field and type of result can differ in nullability.  See [CALCITE-6764]
-        if (!ref.getType().isStruct()
-                && !RelOptUtil.eqUpToNullability(
-                        ref.getType().isNullable(),
-                        "ref",
-                        ref.getType(),
-                        "input",
-                        inputTypeList.get(index),
-                        litmus)) {
-            ++failCount;
-            return litmus.fail(null);
-        }
-        return litmus.succeed();
-    }
-
-    @Override
-    public Boolean visitLocalRef(RexLocalRef ref) {
-        ++failCount;
-        return litmus.fail("RexLocalRef illegal outside program");
-    }
-
-    @Override
-    public Boolean visitCall(RexCall call) {
-        for (RexNode operand : call.getOperands()) {
-            Boolean valid = operand.accept(this);
-            if (valid != null && !valid) {
-                return litmus.fail(null);
-            }
-        }
-        return litmus.succeed();
-    }
-
-    @Override
-    public Boolean visitFieldAccess(RexFieldAccess fieldAccess) {
-        super.visitFieldAccess(fieldAccess);
-        final RelDataType refType = fieldAccess.getReferenceExpr().getType();
-        assert refType.isStruct();
-        final RelDataTypeField field = fieldAccess.getField();
-        final int index = field.getIndex();
-        if ((index < 0) || (index >= refType.getFieldList().size())) {
-            ++failCount;
-            return litmus.fail(null);
-        }
-        // Type of field may not match type of field access - they may differ in nullability
-        final RelDataTypeField typeField = refType.getFieldList().get(index);
-        if (!RelOptUtil.eqUpToNullability(
-                refType.isNullable(),
-                "type1",
-                typeField.getType(),
-                "type2",
-                fieldAccess.getType(),
-                litmus)) {
-            ++failCount;
-            return litmus.fail(null);
-        }
-        return litmus.succeed();
-    }
-
-    @Override
-    public Boolean visitCorrelVariable(RexCorrelVariable v) {
-        if (context != null && !context.correlationIds().contains(v.id)) {
-            ++failCount;
-            return litmus.fail(
-                    "correlation id {} not found in correlation list {}",
-                    v,
-                    context.correlationIds());
-        }
-        return litmus.succeed();
-    }
-
-    /** Returns whether an expression is valid. */
-    public final boolean isValid(RexNode expr) {
-        return requireNonNull(expr.accept(this), () -> "expr.accept(RexChecker) for expr=" + expr);
-    }
-}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexFieldAccess.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexFieldAccess.java
deleted file mode 100644
index 955cc334db6..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexFieldAccess.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.rex;
-
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.sql.SqlKind;
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Access to a field of a row-expression.
- *
- * <p>FLINK modifications (backport of CALCITE-6764): Lines 59, 63 ~ 65, 67 ~ 73, 95
- *
- * <p>You might expect to use a <code>RexFieldAccess</code> to access columns of relational tables,
- * for example, the expression <code>emp.empno</code> in the query
- *
- * <blockquote>
- *
- * <pre>SELECT emp.empno FROM emp</pre>
- *
- * </blockquote>
- *
- * <p>but there is a specialized expression {@link RexInputRef} for this purpose. So in practice,
- * <code>RexFieldAccess</code> is usually used to access fields of correlating variables, for
- * example the expression <code>emp.deptno</code> in
- *
- * <blockquote>
- *
- * <pre>SELECT ename
- * FROM dept
- * WHERE EXISTS (
- *     SELECT NULL
- *     FROM emp
- *     WHERE emp.deptno = dept.deptno
- *     AND gender = 'F')</pre>
- *
- * </blockquote>
- */
-public class RexFieldAccess extends RexNode {
-    // ~ Instance fields --------------------------------------------------------
-
-    private final RexNode expr;
-    private final RelDataTypeField field;
-    // Not always the same as the field.getType().
-    private final RelDataType type;
-
-    // ~ Constructors -----------------------------------------------------------
-
-    RexFieldAccess(RexNode expr, RelDataTypeField field) {
-        this(expr, field, field.getType());
-    }
-
-    RexFieldAccess(RexNode expr, RelDataTypeField field, RelDataType type) {
-        checkValid(expr, field);
-        this.expr = expr;
-        this.field = field;
-        this.digest = expr + "." + field.getName();
-        this.type = type;
-    }
-
-    // ~ Methods ----------------------------------------------------------------
-
-    private static void checkValid(RexNode expr, RelDataTypeField field) {
-        RelDataType exprType = expr.getType();
-        int fieldIdx = field.getIndex();
-        checkArgument(
-                fieldIdx >= 0
-                        && fieldIdx < exprType.getFieldList().size()
-                        && exprType.getFieldList().get(fieldIdx).equals(field),
-                "Field %s does not exist for expression %s",
-                field,
-                expr);
-    }
-
-    public RelDataTypeField getField() {
-        return field;
-    }
-
-    @Override
-    public RelDataType getType() {
-        return type;
-    }
-
-    @Override
-    public SqlKind getKind() {
-        return SqlKind.FIELD_ACCESS;
-    }
-
-    @Override
-    public <R> R accept(RexVisitor<R> visitor) {
-        return visitor.visitFieldAccess(this);
-    }
-
-    @Override
-    public <R, P> R accept(RexBiVisitor<R, P> visitor, P arg) {
-        return visitor.visitFieldAccess(this, arg);
-    }
-
-    /** Returns the expression whose field is being accessed. */
-    public RexNode getReferenceExpr() {
-        return expr;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        RexFieldAccess that = (RexFieldAccess) o;
-
-        return field.equals(that.field) && expr.equals(that.expr);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = expr.hashCode();
-        result = 31 * result + field.hashCode();
-        return result;
-    }
-}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexProgram.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexProgram.java
deleted file mode 100644
index 04dea5bbca1..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexProgram.java
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.rex;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Ordering;
-import org.apache.calcite.linq4j.Ord;
-import org.apache.calcite.plan.RelOptPredicateList;
-import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollation;
-import org.apache.calcite.rel.RelCollations;
-import org.apache.calcite.rel.RelFieldCollation;
-import org.apache.calcite.rel.RelInput;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.RelWriter;
-import org.apache.calcite.rel.externalize.RelJsonWriter;
-import org.apache.calcite.rel.externalize.RelWriterImpl;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.sql.SqlExplainLevel;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.calcite.sql.type.SqlTypeUtil;
-import org.apache.calcite.util.Litmus;
-import org.apache.calcite.util.Pair;
-import org.apache.calcite.util.Permutation;
-import org.apache.calcite.util.mapping.MappingType;
-import org.apache.calcite.util.mapping.Mappings;
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static java.util.Objects.requireNonNull;
-import static org.apache.calcite.linq4j.Nullness.castNonNull;
-
-/**
- * A collection of expressions which read inputs, compute output expressions, and optionally use a
- * condition to filter rows.
- *
- * <p>Programs are immutable. It may help to use a {@link RexProgramBuilder}, which has the same
- * relationship to {@link RexProgram} as {@link StringBuilder} has to {@link String}.
- *
- * <p>A program can contain aggregate functions. If it does, the arguments to each aggregate
- * function must be an {@link RexInputRef}.
- *
- * <p>FLINK modifications (backport of CALCITE-6764): Lines 999 ~ 1002
- *
- * @see RexProgramBuilder
- */
-public class RexProgram {
-    // ~ Instance fields --------------------------------------------------------
-
-    /**
-     * First stage of expression evaluation. The expressions in this array can refer to inputs
-     * (using input ordinal #0) or previous expressions in the array (using input ordinal #1).
-     */
-    private final List<RexNode> exprs;
-
-    /** With {@link #condition}, the second stage of expression evaluation. */
-    private final List<RexLocalRef> projects;
-
-    /** The optional condition. If null, the calculator does not filter rows. */
-    private final @Nullable RexLocalRef condition;
-
-    private final RelDataType inputRowType;
-
-    private final RelDataType outputRowType;
-
-    /** Reference counts for each expression, computed on demand. */
-    private int[] refCounts;
-
-    // ~ Constructors -----------------------------------------------------------
-
-    /**
-     * Creates a program.
-     *
-     * <p>The expressions must be valid: they must not contain common expressions, forward
-     * references, or non-trivial aggregates.
-     *
-     * @param inputRowType Input row type
-     * @param exprs Common expressions
-     * @param projects Projection expressions
-     * @param condition Condition expression. If null, calculator does not filter rows
-     * @param outputRowType Description of the row produced by the program
-     */
-    public RexProgram(
-            RelDataType inputRowType,
-            List<? extends RexNode> exprs,
-            List<RexLocalRef> projects,
-            @Nullable RexLocalRef condition,
-            RelDataType outputRowType) {
-        this.inputRowType = inputRowType;
-        this.exprs = ImmutableList.copyOf(exprs);
-        this.projects = ImmutableList.copyOf(projects);
-        this.condition = condition;
-        this.outputRowType = outputRowType;
-        assert isValid(Litmus.THROW, null);
-    }
-
-    // ~ Methods ----------------------------------------------------------------
-
-    // REVIEW jvs 16-Oct-2006:  The description below is confusing.  I
-    // think it means "none of the entries are null, there may be none,
-    // and there is no further reduction into smaller common sub-expressions
-    // possible"?
-
-    /**
-     * Returns the common sub-expressions of this program.
-     *
-     * <p>The list is never null but may be empty; each the expression in the list is not null; and
-     * no further reduction into smaller common sub-expressions is possible.
-     */
-    public List<RexNode> getExprList() {
-        return exprs;
-    }
-
-    /**
-     * Returns an array of references to the expressions which this program is to project. Never
-     * null, may be empty.
-     */
-    public List<RexLocalRef> getProjectList() {
-        return projects;
-    }
-
-    /** Returns a list of project expressions and their field names. */
-    public List<Pair<RexLocalRef, String>> getNamedProjects() {
-        return new AbstractList<Pair<RexLocalRef, String>>() {
-            @Override
-            public int size() {
-                return projects.size();
-            }
-
-            @Override
-            public Pair<RexLocalRef, String> get(int index) {
-                return Pair.of(
-                        projects.get(index), outputRowType.getFieldList().get(index).getName());
-            }
-        };
-    }
-
-    /**
-     * Returns the field reference of this program's filter condition, or null if there is no
-     * condition.
-     */
-    public @Nullable RexLocalRef getCondition() {
-        return condition;
-    }
-
-    /**
-     * Creates a program which calculates projections and filters rows based upon a condition. Does
-     * not attempt to eliminate common sub-expressions.
-     *
-     * @param projectExprs Project expressions
-     * @param conditionExpr Condition on which to filter rows, or null if rows are not to be
-     *     filtered
-     * @param outputRowType Output row type
-     * @param rexBuilder Builder of rex expressions
-     * @return A program
-     */
-    public static RexProgram create(
-            RelDataType inputRowType,
-            List<? extends RexNode> projectExprs,
-            @Nullable RexNode conditionExpr,
-            RelDataType outputRowType,
-            RexBuilder rexBuilder) {
-        return create(
-                inputRowType,
-                projectExprs,
-                conditionExpr,
-                outputRowType.getFieldNames(),
-                rexBuilder);
-    }
-
-    /**
-     * Creates a program which calculates projections and filters rows based upon a condition. Does
-     * not attempt to eliminate common sub-expressions.
-     *
-     * @param projectExprs Project expressions
-     * @param conditionExpr Condition on which to filter rows, or null if rows are not to be
-     *     filtered
-     * @param fieldNames Names of projected fields
-     * @param rexBuilder Builder of rex expressions
-     * @return A program
-     */
-    public static RexProgram create(
-            RelDataType inputRowType,
-            List<? extends RexNode> projectExprs,
-            @Nullable RexNode conditionExpr,
-            @Nullable List<? extends @Nullable String> fieldNames,
-            RexBuilder rexBuilder) {
-        if (fieldNames == null) {
-            fieldNames = Collections.nCopies(projectExprs.size(), null);
-        } else {
-            assert fieldNames.size() == projectExprs.size()
-                    : "fieldNames=" + fieldNames + ", exprs=" + projectExprs;
-        }
-        final RexProgramBuilder programBuilder = new RexProgramBuilder(inputRowType, rexBuilder);
-        for (int i = 0; i < projectExprs.size(); i++) {
-            programBuilder.addProject(projectExprs.get(i), fieldNames.get(i));
-        }
-        if (conditionExpr != null) {
-            programBuilder.addCondition(conditionExpr);
-        }
-        return programBuilder.getProgram();
-    }
-
-    /**
-     * Create a program from serialized output. In this case, the input is mainly from the output
-     * json string of {@link RelJsonWriter}
-     */
-    public static RexProgram create(RelInput input) {
-        final List<RexNode> exprs = requireNonNull(input.getExpressionList("exprs"), "exprs");
-        final List<RexNode> projectRexNodes =
-                requireNonNull(input.getExpressionList("projects"), "projects");
-        final List<RexLocalRef> projects = new ArrayList<>(projectRexNodes.size());
-        for (RexNode rexNode : projectRexNodes) {
-            projects.add((RexLocalRef) rexNode);
-        }
-        final RelDataType inputType = input.getRowType("inputRowType");
-        final RelDataType outputType = input.getRowType("outputRowType");
-        final RexLocalRef condition = (RexLocalRef) input.getExpression("condition");
-        return new RexProgram(inputType, exprs, projects, condition, outputType);
-    }
-
-    // description of this calc, chiefly intended for debugging
-    @Override
-    public String toString() {
-        // Intended to produce similar output to explainCalc,
-        // but without requiring a RelNode or RelOptPlanWriter.
-        final RelWriterImpl pw = new RelWriterImpl(new PrintWriter(new StringWriter()));
-        collectExplainTerms("", pw);
-        return pw.simple();
-    }
-
-    /**
-     * Writes an explanation of the expressions in this program to a plan writer.
-     *
-     * @param pw Plan writer
-     */
-    public RelWriter explainCalc(RelWriter pw) {
-        if (pw instanceof RelJsonWriter) {
-            return pw.item("exprs", exprs)
-                    .item("projects", projects)
-                    .item("condition", condition)
-                    .item("inputRowType", inputRowType)
-                    .item("outputRowType", outputRowType);
-        } else {
-            return collectExplainTerms("", pw, pw.getDetailLevel());
-        }
-    }
-
-    public RelWriter collectExplainTerms(String prefix, RelWriter pw) {
-        return collectExplainTerms(prefix, pw, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
-    }
-
-    /**
-     * Collects the expressions in this program into a list of terms and values.
-     *
-     * @param prefix Prefix for term names, usually the empty string, but useful if a relational
-     *     expression contains more than one program
-     * @param pw Plan writer
-     */
-    public RelWriter collectExplainTerms(String prefix, RelWriter pw, SqlExplainLevel level) {
-        final List<RelDataTypeField> inFields = inputRowType.getFieldList();
-        final List<RelDataTypeField> outFields = outputRowType.getFieldList();
-        assert outFields.size() == projects.size()
-                : "outFields.length=" + outFields.size() + ", projects.length=" + projects.size();
-        pw.item(
-                prefix + "expr#0" + ((inFields.size() > 1) ? (".." + (inFields.size() - 1)) : ""),
-                "{inputs}");
-        for (int i = inFields.size(); i < exprs.size(); i++) {
-            pw.item(prefix + "expr#" + i, exprs.get(i));
-        }
-
-        // If a lot of the fields are simply projections of the underlying
-        // expression, try to be a bit less verbose.
-        int trivialCount = countTrivial(projects);
-
-        switch (trivialCount) {
-            case 0:
-                break;
-            case 1:
-                trivialCount = 0;
-                break;
-            default:
-                pw.item(prefix + "proj#0.." + (trivialCount - 1), "{exprs}");
-                break;
-        }
-
-        final boolean withFieldNames = level != SqlExplainLevel.DIGEST_ATTRIBUTES;
-        // Print the non-trivial fields with their names as they appear in the
-        // output row type.
-        for (int i = trivialCount; i < projects.size(); i++) {
-            final String fieldName =
-                    withFieldNames ? prefix + outFields.get(i).getName() : prefix + i;
-            pw.item(fieldName, projects.get(i));
-        }
-        if (condition != null) {
-            pw.item(prefix + "$condition", condition);
-        }
-        return pw;
-    }
-
-    /**
-     * Returns the number of expressions at the front of an array which are simply projections of
-     * the same field.
-     *
-     * @param refs References
-     */
-    private static int countTrivial(List<RexLocalRef> refs) {
-        for (int i = 0; i < refs.size(); i++) {
-            RexLocalRef ref = refs.get(i);
-            if (ref.getIndex() != i) {
-                return i;
-            }
-        }
-        return refs.size();
-    }
-
-    /** Returns the number of expressions in this program. */
-    public int getExprCount() {
-        return exprs.size() + projects.size() + ((condition == null) ? 0 : 1);
-    }
-
-    /** Creates the identity program. */
-    public static RexProgram createIdentity(RelDataType rowType) {
-        return createIdentity(rowType, rowType);
-    }
-
-    /**
-     * Creates a program that projects its input fields but with possibly different names for the
-     * output fields.
-     */
-    public static RexProgram createIdentity(RelDataType rowType, RelDataType outputRowType) {
-        if (rowType != outputRowType
-                && !Pair.right(rowType.getFieldList())
-                        .equals(Pair.right(outputRowType.getFieldList()))) {
-            throw new IllegalArgumentException(
-                    "field type mismatch: " + rowType + " vs. " + outputRowType);
-        }
-        final List<RelDataTypeField> fields = rowType.getFieldList();
-        final List<RexLocalRef> projectRefs = new ArrayList<>();
-        final List<RexInputRef> refs = new ArrayList<>();
-        for (int i = 0; i < fields.size(); i++) {
-            final RexInputRef ref = RexInputRef.of(i, fields);
-            refs.add(ref);
-            projectRefs.add(new RexLocalRef(i, ref.getType()));
-        }
-        return new RexProgram(rowType, refs, projectRefs, null, outputRowType);
-    }
-
-    /**
-     * Returns the type of the input row to the program.
-     *
-     * @return input row type
-     */
-    public RelDataType getInputRowType() {
-        return inputRowType;
-    }
-
-    /**
-     * Returns whether this program contains windowed aggregate functions.
-     *
-     * @return whether this program contains windowed aggregate functions
-     */
-    public boolean containsAggs() {
-        return RexOver.containsOver(this);
-    }
-
-    /**
-     * Returns the type of the output row from this program.
-     *
-     * @return output row type
-     */
-    public RelDataType getOutputRowType() {
-        return outputRowType;
-    }
-
-    /**
-     * Checks that this program is valid.
-     *
-     * <p>If <code>fail</code> is true, executes <code>assert false</code>, so will throw an {@link
-     * AssertionError} if assertions are enabled. If <code>
-     * fail</code> is false, merely returns whether the program is valid.
-     *
-     * @param litmus What to do if an error is detected
-     * @param context Context of enclosing {@link RelNode}, for validity checking, or null if not
-     *     known
-     * @return Whether the program is valid
-     */
-    public boolean isValid(Litmus litmus, RelNode.Context context) {
-        if (inputRowType == null) {
-            return litmus.fail(null);
-        }
-        if (exprs == null) {
-            return litmus.fail(null);
-        }
-        if (projects == null) {
-            return litmus.fail(null);
-        }
-        if (outputRowType == null) {
-            return litmus.fail(null);
-        }
-
-        // If the input row type is a struct (contains fields) then the leading
-        // expressions must be references to those fields. But we don't require
-        // this if the input row type is, say, a java class.
-        if (inputRowType.isStruct()) {
-            if (!RexUtil.containIdentity(exprs, inputRowType, litmus)) {
-                return litmus.fail(null);
-            }
-
-            // None of the other fields should be inputRefs.
-            for (int i = inputRowType.getFieldCount(); i < exprs.size(); i++) {
-                RexNode expr = exprs.get(i);
-                if (expr instanceof RexInputRef) {
-                    return litmus.fail(null);
-                }
-            }
-        }
-        // todo: enable
-        // CHECKSTYLE: IGNORE 1
-        if (false && RexUtil.containNoCommonExprs(exprs, litmus)) {
-            return litmus.fail(null);
-        }
-        if (!RexUtil.containNoForwardRefs(exprs, inputRowType, litmus)) {
-            return litmus.fail(null);
-        }
-        if (!RexUtil.containNoNonTrivialAggs(exprs, litmus)) {
-            return litmus.fail(null);
-        }
-        final Checker checker = new Checker(inputRowType, RexUtil.types(exprs), null, litmus);
-        if (condition != null) {
-            if (!SqlTypeUtil.inBooleanFamily(condition.getType())) {
-                return litmus.fail("condition must be boolean");
-            }
-            condition.accept(checker);
-            if (checker.failCount > 0) {
-                return litmus.fail(null);
-            }
-        }
-        for (RexLocalRef project : projects) {
-            project.accept(checker);
-            if (checker.failCount > 0) {
-                return litmus.fail(null);
-            }
-        }
-        for (RexNode expr : exprs) {
-            expr.accept(checker);
-            if (checker.failCount > 0) {
-                return litmus.fail(null);
-            }
-        }
-        return litmus.succeed();
-    }
-
-    /**
-     * Returns whether an expression always evaluates to null.
-     *
-     * <p>Like {@link RexUtil#isNull(RexNode)}, null literals are null, and casts of null literals
-     * are null. But this method also regards references to null expressions as null.
-     *
-     * @param expr Expression
-     * @return Whether expression always evaluates to null
-     */
-    public boolean isNull(RexNode expr) {
-        switch (expr.getKind()) {
-            case LITERAL:
-                return ((RexLiteral) expr).getValue2() == null;
-            case LOCAL_REF:
-                RexLocalRef inputRef = (RexLocalRef) expr;
-                return isNull(exprs.get(inputRef.index));
-            case CAST:
-                return isNull(((RexCall) expr).operands.get(0));
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Fully expands a RexLocalRef back into a pure RexNode tree containing no RexLocalRefs
-     * (reversing the effect of common subexpression elimination). For example, <code>
-     * program.expandLocalRef(program.getCondition())</code> will return the expansion of a
-     * program's condition.
-     *
-     * @param ref a RexLocalRef from this program
-     * @return expanded form
-     */
-    public RexNode expandLocalRef(RexLocalRef ref) {
-        return ref.accept(new ExpansionShuttle(exprs));
-    }
-
-    /** Expands a list of expressions that may contain {@link RexLocalRef}s. */
-    public List<RexNode> expandList(List<? extends RexNode> nodes) {
-        return new ExpansionShuttle(exprs).visitList(nodes);
-    }
-
-    /**
-     * Splits this program into a list of project expressions and a list of filter expressions.
-     *
-     * <p>Neither list is null. The filters are evaluated first.
-     */
-    public Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> split() {
-        final List<RexNode> filters = new ArrayList<>();
-        if (condition != null) {
-            RelOptUtil.decomposeConjunction(expandLocalRef(condition), filters);
-        }
-        final ImmutableList.Builder<RexNode> projects = ImmutableList.builder();
-        for (RexLocalRef project : this.projects) {
-            projects.add(expandLocalRef(project));
-        }
-        return Pair.of(projects.build(), ImmutableList.copyOf(filters));
-    }
-
-    /**
-     * Given a list of collations which hold for the input to this program, returns a list of
-     * collations which hold for its output. The result is mutable and sorted.
-     */
-    public List<RelCollation> getCollations(List<RelCollation> inputCollations) {
-        final List<RelCollation> outputCollations = new ArrayList<>();
-        deduceCollations(outputCollations, inputRowType.getFieldCount(), projects, inputCollations);
-        return outputCollations;
-    }
-
-    /**
-     * Given a list of expressions and a description of which are ordered, populates a list of
-     * collations, sorted in natural order.
-     */
-    public static void deduceCollations(
-            List<RelCollation> outputCollations,
-            final int sourceCount,
-            List<RexLocalRef> refs,
-            List<RelCollation> inputCollations) {
-        int[] targets = new int[sourceCount];
-        Arrays.fill(targets, -1);
-        for (int i = 0; i < refs.size(); i++) {
-            final RexLocalRef ref = refs.get(i);
-            final int source = ref.getIndex();
-            if ((source < sourceCount) && (targets[source] == -1)) {
-                targets[source] = i;
-            }
-        }
-        loop:
-        for (RelCollation collation : inputCollations) {
-            final List<RelFieldCollation> fieldCollations = new ArrayList<>(0);
-            for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
-                final int source = fieldCollation.getFieldIndex();
-                final int target = targets[source];
-                if (target < 0) {
-                    continue loop;
-                }
-                fieldCollations.add(fieldCollation.withFieldIndex(target));
-            }
-
-            // Success -- all of the source fields of this key are mapped
-            // to the output.
-            outputCollations.add(RelCollations.of(fieldCollations));
-        }
-        outputCollations.sort(Ordering.natural());
-    }
-
-    /**
-     * Returns whether the fields on the leading edge of the project list are the input fields.
-     *
-     * @param fail Whether to throw an assert failure if does not project identity
-     */
-    public boolean projectsIdentity(final boolean fail) {
-        final int fieldCount = inputRowType.getFieldCount();
-        if (projects.size() < fieldCount) {
-            assert !fail
-                    : "program '"
-                            + toString()
-                            + "' does not project identity for input row type '"
-                            + inputRowType
-                            + "'";
-            return false;
-        }
-        for (int i = 0; i < fieldCount; i++) {
-            RexLocalRef project = projects.get(i);
-            if (project.index != i) {
-                assert !fail
-                        : "program "
-                                + toString()
-                                + "' does not project identity for input row type '"
-                                + inputRowType
-                                + "', field #"
-                                + i;
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns whether this program projects precisely its input fields. It may or may not apply a
-     * condition.
-     */
-    public boolean projectsOnlyIdentity() {
-        if (projects.size() != inputRowType.getFieldCount()) {
-            return false;
-        }
-        for (int i = 0; i < projects.size(); i++) {
-            RexLocalRef project = projects.get(i);
-            if (project.index != i) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns whether this program returns its input exactly.
-     *
-     * <p>This is a stronger condition than {@link #projectsIdentity(boolean)}.
-     */
-    public boolean isTrivial() {
-        return getCondition() == null && projectsOnlyIdentity();
-    }
-
-    /**
-     * Gets reference counts for each expression in the program, where the references are detected
-     * from later expressions in the same program, as well as the project list and condition.
-     * Expressions with references counts greater than 1 are true common sub-expressions.
-     *
-     * @return array of reference counts; the ith element in the returned array is the number of
-     *     references to getExprList()[i]
-     */
-    public int[] getReferenceCounts() {
-        if (refCounts != null) {
-            return refCounts;
-        }
-        refCounts = new int[exprs.size()];
-        ReferenceCounter refCounter = new ReferenceCounter(refCounts);
-        RexUtil.apply(refCounter, exprs, null);
-        if (condition != null) {
-            refCounter.visitLocalRef(condition);
-        }
-        for (RexLocalRef project : projects) {
-            refCounter.visitLocalRef(project);
-        }
-        return refCounts;
-    }
-
-    /** Returns whether an expression is constant. */
-    public boolean isConstant(RexNode ref) {
-        return ref.accept(new ConstantFinder());
-    }
-
-    public @Nullable RexNode gatherExpr(RexNode expr) {
-        return expr.accept(new Marshaller());
-    }
-
-    /**
-     * Returns the input field that an output field is populated from, or -1 if it is populated from
-     * an expression.
-     */
-    public int getSourceField(int outputOrdinal) {
-        assert (outputOrdinal >= 0) && (outputOrdinal < this.projects.size());
-        RexLocalRef project = projects.get(outputOrdinal);
-        int index = project.index;
-        while (true) {
-            RexNode expr = exprs.get(index);
-            if (expr instanceof RexCall
-                    && ((RexCall) expr).getOperator() == SqlStdOperatorTable.IN_FENNEL) {
-                // drill through identity function
-                expr = ((RexCall) expr).getOperands().get(0);
-            }
-            if (expr instanceof RexLocalRef) {
-                index = ((RexLocalRef) expr).index;
-            } else if (expr instanceof RexInputRef) {
-                return ((RexInputRef) expr).index;
-            } else {
-                return -1;
-            }
-        }
-    }
-
-    /** Returns whether this program is a permutation of its inputs. */
-    public boolean isPermutation() {
-        if (projects.size() != inputRowType.getFieldList().size()) {
-            return false;
-        }
-        for (int i = 0; i < projects.size(); ++i) {
-            if (getSourceField(i) < 0) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** Returns a permutation, if this program is a permutation, otherwise null. */
-    public @Nullable Permutation getPermutation() {
-        Permutation permutation = new Permutation(projects.size());
-        if (projects.size() != inputRowType.getFieldList().size()) {
-            return null;
-        }
-        for (int i = 0; i < projects.size(); ++i) {
-            int sourceField = getSourceField(i);
-            if (sourceField < 0) {
-                return null;
-            }
-            permutation.set(i, sourceField);
-        }
-        return permutation;
-    }
-
-    /**
-     * Returns the set of correlation variables used (read) by this program.
-     *
-     * @return set of correlation variable names
-     */
-    public Set<String> getCorrelVariableNames() {
-        final Set<String> paramIdSet = new HashSet<>();
-        RexUtil.apply(
-                new RexVisitorImpl<Void>(true) {
-                    @Override
-                    public Void visitCorrelVariable(RexCorrelVariable correlVariable) {
-                        paramIdSet.add(correlVariable.getName());
-                        return null;
-                    }
-                },
-                exprs,
-                null);
-        return paramIdSet;
-    }
-
-    /**
-     * Returns whether this program is in canonical form.
-     *
-     * @param litmus What to do if an error is detected (program is not in canonical form)
-     * @param rexBuilder Rex builder
-     * @return whether in canonical form
-     */
-    public boolean isNormalized(Litmus litmus, RexBuilder rexBuilder) {
-        final RexProgram normalizedProgram = normalize(rexBuilder, null);
-        String normalized = normalizedProgram.toString();
-        String string = toString();
-        if (!normalized.equals(string)) {
-            final String message =
-                    "Program is not normalized:\n" + "program:    {}\n" + "normalized: {}\n";
-            return litmus.fail(message, string, normalized);
-        }
-        return litmus.succeed();
-    }
-
-    /**
-     * Creates a simplified/normalized copy of this program.
-     *
-     * @param rexBuilder Rex builder
-     * @param simplify Simplifier to simplify (in addition to normalizing), or null to not simplify
-     * @return Normalized program
-     */
-    public RexProgram normalize(RexBuilder rexBuilder, @Nullable RexSimplify simplify) {
-        // Normalize program by creating program builder from the program, then
-        // converting to a program. getProgram does not need to normalize
-        // because the builder was normalized on creation.
-        assert isValid(Litmus.THROW, null);
-        final RexProgramBuilder builder =
-                RexProgramBuilder.create(
-                        rexBuilder,
-                        inputRowType,
-                        exprs,
-                        projects,
-                        condition,
-                        outputRowType,
-                        true,
-                        simplify);
-        return builder.getProgram(false);
-    }
-
-    @Deprecated // to be removed before 2.0
-    public RexProgram normalize(RexBuilder rexBuilder, boolean simplify) {
-        final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
-        return normalize(
-                rexBuilder,
-                simplify ? new RexSimplify(rexBuilder, predicates, RexUtil.EXECUTOR) : null);
-    }
-
-    /**
-     * Returns a partial mapping of a set of project expressions.
-     *
-     * <p>The mapping is an inverse function. Every target has a source field, but a source might
-     * have 0, 1 or more targets. Project expressions that do not consist of a mapping are ignored.
-     *
-     * @param inputFieldCount Number of input fields
-     * @return Mapping of a set of project expressions, never null
-     */
-    public Mappings.TargetMapping getPartialMapping(int inputFieldCount) {
-        Mappings.TargetMapping mapping =
-                Mappings.create(MappingType.INVERSE_FUNCTION, inputFieldCount, projects.size());
-        for (Ord<RexLocalRef> exp : Ord.zip(projects)) {
-            RexNode rexNode = expandLocalRef(exp.e);
-            if (rexNode instanceof RexInputRef) {
-                mapping.set(((RexInputRef) rexNode).getIndex(), exp.i);
-            }
-        }
-        return mapping;
-    }
-
-    // ~ Inner Classes ----------------------------------------------------------
-
-    /** Visitor which walks over a program and checks validity. */
-    static class Checker extends RexChecker {
-        private final List<RelDataType> internalExprTypeList;
-
-        /**
-         * Creates a Checker.
-         *
-         * @param inputRowType Types of the input fields
-         * @param internalExprTypeList Types of the internal expressions
-         * @param context Context of the enclosing {@link RelNode}, or null
-         * @param litmus Whether to fail
-         */
-        Checker(
-                RelDataType inputRowType,
-                List<RelDataType> internalExprTypeList,
-                RelNode.Context context,
-                Litmus litmus) {
-            super(inputRowType, context, litmus);
-            this.internalExprTypeList = internalExprTypeList;
-        }
-
-        /**
-         * Overrides {@link RexChecker} method, because {@link RexLocalRef} is is illegal in most
-         * rex expressions, but legal in a program.
-         */
-        @Override
-        public Boolean visitLocalRef(RexLocalRef localRef) {
-            final int index = localRef.getIndex();
-            if ((index < 0) || (index >= internalExprTypeList.size())) {
-                ++failCount;
-                return litmus.fail(null);
-            }
-            if (!RelOptUtil.eq(
-                    "type1",
-                    localRef.getType(),
-                    "type2",
-                    internalExprTypeList.get(index),
-                    litmus)) {
-                ++failCount;
-                return litmus.fail(null);
-            }
-            return litmus.succeed();
-        }
-    }
-
-    /** A RexShuttle used in the implementation of {@link RexProgram#expandLocalRef}. */
-    static class ExpansionShuttle extends RexShuttle {
-        private final List<RexNode> exprs;
-
-        ExpansionShuttle(List<RexNode> exprs) {
-            this.exprs = exprs;
-        }
-
-        @Override
-        public RexNode visitLocalRef(RexLocalRef localRef) {
-            RexNode tree = exprs.get(localRef.getIndex());
-            return tree.accept(this);
-        }
-    }
-
-    /** Walks over an expression and determines whether it is constant. */
-    private class ConstantFinder extends RexUtil.ConstantFinder {
-        @Override
-        public Boolean visitLocalRef(RexLocalRef localRef) {
-            final RexNode expr = exprs.get(localRef.index);
-            return expr.accept(this);
-        }
-
-        @Override
-        public Boolean visitOver(RexOver over) {
-            return false;
-        }
-
-        @Override
-        public Boolean visitCorrelVariable(RexCorrelVariable correlVariable) {
-            // Correlating variables are constant WITHIN A RESTART, so that's
-            // good enough.
-            return true;
-        }
-    }
-
-    /**
-     * Given an expression in a program, creates a clone of the expression with sub-expressions
-     * (represented by {@link RexLocalRef}s) fully expanded.
-     */
-    private class Marshaller extends RexVisitorImpl<@Nullable RexNode> {
-        Marshaller() {
-            super(false);
-        }
-
-        @Override
-        public RexNode visitInputRef(RexInputRef inputRef) {
-            return inputRef;
-        }
-
-        @Override
-        public @Nullable RexNode visitLocalRef(RexLocalRef localRef) {
-            final RexNode expr = exprs.get(localRef.index);
-            return expr.accept(this);
-        }
-
-        @Override
-        public RexNode visitLiteral(RexLiteral literal) {
-            return literal;
-        }
-
-        @Override
-        public RexNode visitCall(RexCall call) {
-            final List<RexNode> newOperands = new ArrayList<>();
-            for (RexNode operand : call.getOperands()) {
-                newOperands.add(castNonNull(operand.accept(this)));
-            }
-            return call.clone(call.getType(), newOperands);
-        }
-
-        @Override
-        public RexNode visitOver(RexOver over) {
-            return visitCall(over);
-        }
-
-        @Override
-        public RexNode visitCorrelVariable(RexCorrelVariable correlVariable) {
-            return correlVariable;
-        }
-
-        @Override
-        public RexNode visitDynamicParam(RexDynamicParam dynamicParam) {
-            return dynamicParam;
-        }
-
-        @Override
-        public RexNode visitRangeRef(RexRangeRef rangeRef) {
-            return rangeRef;
-        }
-
-        @Override
-        public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
-            final RexNode referenceExpr = fieldAccess.getReferenceExpr().accept(this);
-            return new RexFieldAccess(
-                    requireNonNull(referenceExpr, "referenceExpr must not be null"),
-                    fieldAccess.getField(),
-                    fieldAccess.getType());
-        }
-    }
-
-    /** Visitor which marks which expressions are used. */
-    private static class ReferenceCounter extends RexVisitorImpl<Void> {
-        private final int[] refCounts;
-
-        ReferenceCounter(int[] refCounts) {
-            super(true);
-            this.refCounts = refCounts;
-        }
-
-        @Override
-        public Void visitLocalRef(RexLocalRef localRef) {
-            final int index = localRef.getIndex();
-            refCounts[index]++;
-            return null;
-        }
-    }
-}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexShuttle.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexShuttle.java
deleted file mode 100644
index 1c753da9de3..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexShuttle.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.calcite.rex;
-
-import com.google.common.collect.ImmutableList;
-import org.apache.calcite.sql.SqlAggFunction;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Passes over a row-expression, calling a handler method for each node, appropriate to the type of
- * the node.
- *
- * <p>Like {@link RexVisitor}, this is an instance of the {@link
- * org.apache.calcite.util.Glossary#VISITOR_PATTERN Visitor Pattern}. Use <code> RexShuttle</code>
- * if you would like your methods to return a value.
- *
- * <p>FLINK modifications (backport of CALCITE-6764): Lines 208 ~ 211
- */
-public class RexShuttle implements RexVisitor<RexNode> {
-    // ~ Methods ----------------------------------------------------------------
-
-    @Override
-    public RexNode visitOver(RexOver over) {
-        boolean[] update = {false};
-        List<RexNode> clonedOperands = visitList(over.operands, update);
-        SqlAggFunction overAggregator = visitOverAggFunction(over.getAggOperator());
-        RexWindow window = visitWindow(over.getWindow());
-        if (update[0] || (window != over.getWindow()) || overAggregator != over.getAggOperator()) {
-            // REVIEW jvs 8-Mar-2005:  This doesn't take into account
-            // the fact that a rewrite may have changed the result type.
-            // To do that, we would need to take a RexBuilder and
-            // watch out for special operators like CAST and NEW where
-            // the type is embedded in the original call.
-            return new RexOver(
-                    over.getType(),
-                    overAggregator,
-                    clonedOperands,
-                    window,
-                    over.isDistinct(),
-                    over.ignoreNulls());
-        } else {
-            return over;
-        }
-    }
-
-    public SqlAggFunction visitOverAggFunction(SqlAggFunction op) {
-        return op;
-    }
-
-    public RexWindow visitWindow(RexWindow window) {
-        boolean[] update = {false};
-        List<RexFieldCollation> clonedOrderKeys = visitFieldCollations(window.orderKeys, update);
-        List<RexNode> clonedPartitionKeys = visitList(window.partitionKeys, update);
-        final RexWindowBound lowerBound = window.getLowerBound().accept(this);
-        final RexWindowBound upperBound = window.getUpperBound().accept(this);
-        if (!update[0]
-                && lowerBound == window.getLowerBound()
-                && upperBound == window.getUpperBound()) {
-            return window;
-        }
-        boolean rows = window.isRows();
-        if (lowerBound.isUnboundedPreceding() && upperBound.isUnboundedFollowing()) {
-            // RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-            //   is equivalent to
-            // ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-            //   but we prefer "RANGE"
-            rows = false;
-        }
-        return new RexWindow(
-                clonedPartitionKeys,
-                clonedOrderKeys,
-                lowerBound,
-                upperBound,
-                rows,
-                window.getExclude());
-    }
-
-    @Override
-    public RexNode visitSubQuery(RexSubQuery subQuery) {
-        boolean[] update = {false};
-        List<RexNode> clonedOperands = visitList(subQuery.operands, update);
-        if (update[0]) {
-            return subQuery.clone(subQuery.getType(), clonedOperands);
-        } else {
-            return subQuery;
-        }
-    }
-
-    @Override
-    public RexNode visitTableInputRef(RexTableInputRef ref) {
-        return ref;
-    }
-
-    @Override
-    public RexNode visitPatternFieldRef(RexPatternFieldRef fieldRef) {
-        return fieldRef;
-    }
-
-    @Override
-    public RexNode visitCall(final RexCall call) {
-        boolean[] update = {false};
-        List<RexNode> clonedOperands = visitList(call.operands, update);
-        if (update[0]) {
-            // REVIEW jvs 8-Mar-2005:  This doesn't take into account
-            // the fact that a rewrite may have changed the result type.
-            // To do that, we would need to take a RexBuilder and
-            // watch out for special operators like CAST and NEW where
-            // the type is embedded in the original call.
-            return call.clone(call.getType(), clonedOperands);
-        } else {
-            return call;
-        }
-    }
-
-    /**
-     * Visits each of an array of expressions and returns an array of the results.
-     *
-     * @param exprs Array of expressions
-     * @param update If not null, sets this to true if any of the expressions was modified
-     * @return Array of visited expressions
-     */
-    protected RexNode[] visitArray(RexNode[] exprs, boolean[] update) {
-        RexNode[] clonedOperands = new RexNode[exprs.length];
-        for (int i = 0; i < exprs.length; i++) {
-            RexNode operand = exprs[i];
-            RexNode clonedOperand = operand.accept(this);
-            if ((clonedOperand != operand) && (update != null)) {
-                update[0] = true;
-            }
-            clonedOperands[i] = clonedOperand;
-        }
-        return clonedOperands;
-    }
-
-    /**
-     * Visits each of a list of expressions and returns a list of the results.
-     *
-     * @param exprs List of expressions
-     * @param update If not null, sets this to true if any of the expressions was modified
-     * @return Array of visited expressions
-     */
-    protected List<RexNode> visitList(List<? extends RexNode> exprs, boolean[] update) {
-        ImmutableList.Builder<RexNode> clonedOperands = ImmutableList.builder();
-        for (RexNode operand : exprs) {
-            RexNode clonedOperand = operand.accept(this);
-            if ((clonedOperand != operand) && (update != null)) {
-                update[0] = true;
-            }
-            clonedOperands.add(clonedOperand);
-        }
-        return clonedOperands.build();
-    }
-
-    /**
-     * Visits each of a list of field collations and returns a list of the results.
-     *
-     * @param collations List of field collations
-     * @param update If not null, sets this to true if any of the expressions was modified
-     * @return Array of visited field collations
-     */
-    protected List<RexFieldCollation> visitFieldCollations(
-            List<RexFieldCollation> collations, boolean[] update) {
-        ImmutableList.Builder<RexFieldCollation> clonedOperands = ImmutableList.builder();
-        for (RexFieldCollation collation : collations) {
-            RexNode clonedOperand = collation.left.accept(this);
-            if ((clonedOperand != collation.left) && (update != null)) {
-                update[0] = true;
-                collation = new RexFieldCollation(clonedOperand, collation.right);
-            }
-            clonedOperands.add(collation);
-        }
-        return clonedOperands.build();
-    }
-
-    @Override
-    public RexNode visitCorrelVariable(RexCorrelVariable variable) {
-        return variable;
-    }
-
-    @Override
-    public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
-        RexNode before = fieldAccess.getReferenceExpr();
-        RexNode after = before.accept(this);
-
-        if (before == after) {
-            return fieldAccess;
-        } else {
-            return new RexFieldAccess(after, fieldAccess.getField(), fieldAccess.getType());
-        }
-    }
-
-    @Override
-    public RexNode visitInputRef(RexInputRef inputRef) {
-        return inputRef;
-    }
-
-    @Override
-    public RexNode visitLocalRef(RexLocalRef localRef) {
-        return localRef;
-    }
-
-    @Override
-    public RexNode visitLiteral(RexLiteral literal) {
-        return literal;
-    }
-
-    @Override
-    public RexNode visitDynamicParam(RexDynamicParam dynamicParam) {
-        return dynamicParam;
-    }
-
-    @Override
-    public RexNode visitRangeRef(RexRangeRef rangeRef) {
-        return rangeRef;
-    }
-
-    @Override
-    public RexNode visitLambda(RexLambda lambda) {
-        lambda.getExpression().accept(this);
-        return lambda;
-    }
-
-    @Override
-    public RexNode visitLambdaRef(RexLambdaRef lambdaRef) {
-        return lambdaRef;
-    }
-
-    /**
-     * Applies this shuttle to each expression in a list.
-     *
-     * @return whether any of the expressions changed
-     */
-    public final <T extends RexNode> boolean mutate(List<T> exprList) {
-        int changeCount = 0;
-        for (int i = 0; i < exprList.size(); i++) {
-            T expr = exprList.get(i);
-            T expr2 = (T) apply(expr); // Avoid NPE if expr is null
-            if (expr != expr2) {
-                ++changeCount;
-                exprList.set(i, expr2);
-            }
-        }
-        return changeCount > 0;
-    }
-
-    /**
-     * Applies this shuttle to each expression in a list and returns the resulting list. Does not
-     * modify the initial list.
-     *
-     * <p>Returns null if and only if {@code exprList} is null.
-     */
-    public final <T extends RexNode> List<T> apply(List<T> exprList) {
-        if (exprList == null) {
-            return exprList;
-        }
-        final List<T> list2 = new ArrayList<>(exprList);
-        if (mutate(list2)) {
-            return list2;
-        } else {
-            return exprList;
-        }
-    }
-
-    /** Applies this shuttle to an expression, or returns null if the expression is null. */
-    public final RexNode apply(RexNode expr) {
-        return (expr == null) ? expr : expr.accept(this);
-    }
-}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexUtil.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexUtil.java
index 7202821feb4..94532cb5a12 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -2486,11 +2486,9 @@ public class RexUtil {
             expr.accept(this);
             final RexNode normalizedExpr = lookup(expr);
             if (normalizedExpr != expr) {
-                // ----- FLINK MODIFICATION BEGIN -----
                 fieldAccess =
                         new RexFieldAccess(
                                 normalizedExpr, fieldAccess.getField(), fieldAccess.getType());
-                // ----- FLINK MODIFICATION END -----
             }
             return register(fieldAccess);
         }
@@ -2845,6 +2843,22 @@ public class RexUtil {
         public Void visitCorrelVariable(RexCorrelVariable var) {
             throw Util.FoundOne.NULL;
         }
+
+        @Override
+        public Void visitSubQuery(RexSubQuery subQuery) {
+            if (!deep) {
+                return null;
+            }
+
+            for (RexNode operand : subQuery.operands) {
+                operand.accept(this);
+            }
+
+            if (!RelOptUtil.getVariablesUsed(subQuery.rel).isEmpty()) {
+                throw Util.FoundOne.NULL;
+            }
+            return null;
+        }
     }
 
     /** Shuttle that fixes up an expression to match changes in nullability of input fields. */
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 11c15a194fa..232b86c58b0 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -45,6 +45,7 @@ import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.rel.type.TimeFrame;
 import org.apache.calcite.rel.type.TimeFrameSet;
 import org.apache.calcite.runtime.FlatLists.ComparableList;
+import org.apache.calcite.runtime.variant.VariantValue;
 import org.apache.calcite.sql.SqlIntervalQualifier;
 import org.apache.calcite.sql.SqlUtil;
 import org.apache.calcite.sql.fun.SqlLibraryOperators;
@@ -120,6 +121,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TimeZone;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BinaryOperator;
 import java.util.function.Consumer;
@@ -127,6 +129,7 @@ import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
+import java.util.zip.CRC32;
 
 import static java.lang.Byte.parseByte;
 import static java.lang.Double.parseDouble;
@@ -270,12 +273,34 @@ public class SqlFunctions {
         return condition;
     }
 
-    /** SQL TO_BASE64(string) function. */
+    public static String uuidToString(UUID uuid) {
+        return uuid.toString();
+    }
+
+    public static UUID binaryToUuid(ByteString bytes) {
+        if (bytes.length() < 16) {
+            throw new IllegalArgumentException("Need at least 16 bytes for UUID");
+        }
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes.getBytes());
+        long mostSignificantBits = byteBuffer.getLong();
+        long leastSignificantBits = byteBuffer.getLong();
+        return new UUID(mostSignificantBits, leastSignificantBits);
+    }
+
+    public static ByteString uuidToBinary(UUID uuid) {
+        byte[] dest = new byte[16];
+        ByteBuffer byteBuffer = ByteBuffer.wrap(dest);
+        byteBuffer.putLong(uuid.getMostSignificantBits());
+        byteBuffer.putLong(uuid.getLeastSignificantBits());
+        return new ByteString(dest);
+    }
+
+    /** SQL TO_BASE64(string)/BASE64(string) function. */
     public static String toBase64(String string) {
         return toBase64_(string.getBytes(UTF_8));
     }
 
-    /** SQL TO_BASE64(string) function for binary string. */
+    /** SQL TO_BASE64(string)/BASE64(string) function for binary string. */
     public static String toBase64(ByteString string) {
         return toBase64_(string.getBytes());
     }
@@ -294,7 +319,7 @@ public class SqlFunctions {
         return str.substring(0, str.length() - 1);
     }
 
-    /** SQL FROM_BASE64(string) function. */
+    /** SQL FROM_BASE64(string)/UNBASE64(string) function. */
     public static @Nullable ByteString fromBase64(String base64) {
         try {
             base64 = FROM_BASE64_REGEXP.matcher(base64).replaceAll("");
@@ -338,6 +363,46 @@ public class SqlFunctions {
         return Hex.encodeHexString(byteString.getBytes());
     }
 
+    /** SQL HEX(varchar) function. */
+    public static String hex(String value) {
+        return Hex.encodeHexString(value.getBytes(UTF_8));
+    }
+
+    /** SQL BIN(long) function. */
+    public static String bin(long value) {
+        int zeros = Long.numberOfLeadingZeros(value);
+        if (zeros == Long.SIZE) {
+            return "0";
+        } else {
+            int length = Long.SIZE - zeros;
+            byte[] bytes = new byte[length];
+            for (int index = length - 1; index >= 0; index--) {
+                bytes[index] = (byte) ((value & 0x1) == 1 ? '1' : '0');
+                value >>>= 1;
+            }
+            // CHECKSTYLE: IGNORE 1
+            return new String(bytes, UTF_8);
+        }
+    }
+
+    /** SQL CRC32(string) function. */
+    public static long crc32(String value) {
+        final CRC32 crc32 = new CRC32();
+        crc32.reset();
+        byte[] bytes = value.getBytes(UTF_8);
+        crc32.update(bytes, 0, bytes.length);
+        return crc32.getValue();
+    }
+
+    /** SQL CRC32(string) function for binary string. */
+    public static long crc32(ByteString value) {
+        final CRC32 crc32 = new CRC32();
+        crc32.reset();
+        byte[] bytes = value.getBytes();
+        crc32.update(bytes, 0, bytes.length);
+        return crc32.getValue();
+    }
+
     /** SQL MD5(string) function. */
     public static String md5(String string) {
         return DigestUtils.md5Hex(string.getBytes(UTF_8));
@@ -953,6 +1018,26 @@ public class SqlFunctions {
         }
     }
 
+    /** SQL {@code SPLIT_PART(string, string, int)} function. */
+    public static String splitPart(String s, String delimiter, int n) {
+        if (Strings.isNullOrEmpty(s) || Strings.isNullOrEmpty(delimiter)) {
+            return "";
+        }
+
+        String[] parts = s.split(delimiter, -1);
+        int partCount = parts.length;
+
+        if (n < 0) {
+            n = partCount + n + 1;
+        }
+
+        if (n <= 0 || n > partCount) {
+            return "";
+        }
+
+        return parts[n - 1];
+    }
+
     /** SQL {@code SPLIT(string)} function. */
     public static List<String> split(String s) {
         return split(s, ",");
@@ -1692,6 +1777,31 @@ public class SqlFunctions {
         }
     }
 
+    /**
+     * Oracle's {@code CONVERT(charValue, destCharsetName[, srcCharsetName])} function, return null
+     * if s is null or empty.
+     */
+    public static String convertOracle(String s, String... args) {
+        final Charset src;
+        final Charset dest;
+        if (args.length == 1) {
+            // srcCharsetName is not specified
+            src = Charset.defaultCharset();
+            dest = SqlUtil.getCharset(args[0]);
+        } else {
+            dest = SqlUtil.getCharset(args[0]);
+            src = SqlUtil.getCharset(args[1]);
+        }
+        byte[] bytes = s.getBytes(src);
+        final CharsetDecoder decoder = dest.newDecoder();
+        final ByteBuffer buffer = ByteBuffer.wrap(bytes);
+        try {
+            return decoder.decode(buffer).toString();
+        } catch (CharacterCodingException ex) {
+            throw RESOURCE.charsetEncoding(s, dest.name()).ex();
+        }
+    }
+
     /** State for {@code PARSE_URL}. */
     @Deterministic
     public static class ParseUrlFunction {
@@ -1780,7 +1890,7 @@ public class SqlFunctions {
 
     public static String trim(boolean left, boolean right, String seek, String s, boolean strict) {
         if (strict && seek.length() != 1) {
-            throw RESOURCE.trimError().ex();
+            throw RESOURCE.trimError(seek).ex();
         }
         int j = s.length();
         if (right) {
@@ -2408,6 +2518,40 @@ public class SqlFunctions {
         throw notArithmetic("+", b0, b1);
     }
 
+    // checked +
+
+    static byte intToByte(int value) {
+        if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
+            throw new ArithmeticException(
+                    "integer overflow: Value " + value + " does not fit in a TINYINT");
+        }
+        return (byte) value;
+    }
+
+    static short intToShort(int value) {
+        if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+            throw new ArithmeticException(
+                    "integer overflow: Value " + value + " does not fit in a SMALLINT");
+        }
+        return (short) value;
+    }
+
+    public static byte checkedPlus(byte b0, byte b1) {
+        return intToByte(b0 + b1);
+    }
+
+    public static short checkedPlus(short b0, short b1) {
+        return intToShort(b0 + b1);
+    }
+
+    public static int checkedPlus(int b0, int b1) {
+        return Math.addExact(b0, b1);
+    }
+
+    public static long checkedPlus(long b0, long b1) {
+        return Math.addExact(b0, b1);
+    }
+
     // -
 
     /** SQL <code>-</code> operator applied to int values. */
@@ -2461,6 +2605,40 @@ public class SqlFunctions {
         throw notArithmetic("-", b0, b1);
     }
 
+    // checked -
+
+    public static byte checkedMinus(byte b0, byte b1) {
+        return intToByte(b0 - b1);
+    }
+
+    public static short checkedMinus(short b0, short b1) {
+        return intToShort(b0 - b1);
+    }
+
+    public static int checkedMinus(int b0, int b1) {
+        return Math.subtractExact(b0, b1);
+    }
+
+    public static long checkedMinus(long b0, long b1) {
+        return Math.subtractExact(b0, b1);
+    }
+
+    public static byte checkedUnaryMinus(byte b) {
+        return intToByte(-b);
+    }
+
+    public static short checkedUnaryMinus(short b) {
+        return intToShort(-b);
+    }
+
+    public static int checkedUnaryMinus(int b) {
+        return Math.subtractExact(0, b);
+    }
+
+    public static long checkedUnaryMinus(long b) {
+        return Math.subtractExact(0, b);
+    }
+
     // /
 
     /** SQL <code>/</code> operator applied to int values. */
@@ -2524,6 +2702,34 @@ public class SqlFunctions {
         return BigDecimal.valueOf(b0).divide(b1, RoundingMode.HALF_DOWN).longValue();
     }
 
+    public static byte checkedDivide(byte b0, byte b1) {
+        return intToByte(b0 / b1);
+    }
+
+    public static short checkedDivide(short b0, short b1) {
+        return intToShort(b0 / b1);
+    }
+
+    public static int checkedDivide(int b0, int b1) {
+        // Implementation taken from Java 19
+        int q = b0 / b1;
+        if ((b0 & b1 & q) >= 0) {
+            return q;
+        } else {
+            throw new ArithmeticException("integer overflow");
+        }
+    }
+
+    public static long checkedDivide(long b0, long b1) {
+        // Implementation taken from Java 19
+        long q = b0 / b1;
+        if ((b0 & b1 & q) >= 0) {
+            return q;
+        } else {
+            throw new ArithmeticException("integer overflow");
+        }
+    }
+
     // *
 
     /** SQL <code>*</code> operator applied to int values. */
@@ -2577,6 +2783,24 @@ public class SqlFunctions {
         throw notArithmetic("*", b0, b1);
     }
 
+    // checked *
+
+    public static byte checkedMultiply(byte b0, byte b1) {
+        return intToByte(b0 * b1);
+    }
+
+    public static short checkedMultiply(short b0, short b1) {
+        return intToShort(b0 * b1);
+    }
+
+    public static int checkedMultiply(int b0, int b1) {
+        return Math.multiplyExact(b0, b1);
+    }
+
+    public static long checkedMultiply(long b0, long b1) {
+        return Math.multiplyExact(b0, b1);
+    }
+
     /** SQL <code>SAFE_ADD</code> function applied to long values. */
     public static @Nullable Long safeAdd(long b0, long b1) {
         try {
@@ -2912,6 +3136,58 @@ public class SqlFunctions {
         return bitsSet;
     }
 
+    /**
+     * Helper function for implementing MySQL <code>BIT_COUNT</code>. Counts the number of bits set
+     * in a boolean value.
+     */
+    public static long bitCountMySQL(Boolean b) {
+        return Long.bitCount(b ? 1L : 0L);
+    }
+
+    /**
+     * Helper function for implementing MySQL <code>BIT_COUNT</code>. Counts the number of bits set
+     * in a string value.
+     */
+    public static long bitCountMySQL(String b) {
+        try {
+            return bitCount(new BigDecimal(b));
+        } catch (Exception ignore) {
+            return 0;
+        }
+    }
+
+    /**
+     * Helper function for implementing MySQL <code>BIT_COUNT</code>. Counts the number of bits set
+     * in a number value.
+     */
+    public static long bitCountMySQL(Number b) {
+        return bitCount(new BigDecimal(b.toString()));
+    }
+
+    /**
+     * Helper function for implementing MySQL <code>BIT_COUNT</code>. Counts the number of bits set
+     * in a date value.
+     */
+    public static long bitCountMySQL(java.sql.Date b) {
+        return bitCountMySQL(new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH).format(b));
+    }
+
+    /**
+     * Helper function for implementing MySQL <code>BIT_COUNT</code>. Counts the number of bits set
+     * in a time value.
+     */
+    public static long bitCountMySQL(Time b) {
+        return bitCountMySQL(new SimpleDateFormat("HHmmss", Locale.ENGLISH).format(b));
+    }
+
+    /**
+     * Helper function for implementing MySQL <code>BIT_COUNT</code>. Counts the number of bits set
+     * in a timestamp value.
+     */
+    public static long bitCountMySQL(Timestamp b) {
+        return bitCountMySQL(new SimpleDateFormat("yyyyMMddHHmmss", Locale.ENGLISH).format(b));
+    }
+
     /** Bitwise function <code>BIT_OR</code> applied to integer values. */
     public static long bitOr(long b0, long b1) {
         return b0 | b1;
@@ -3802,12 +4078,18 @@ public class SqlFunctions {
     // Helpers
 
     /** Helper for implementing MIN. Somewhat similar to LEAST operator. */
-    public static <T extends Comparable<T>> T lesser(T b0, T b1) {
-        return b0 == null || b0.compareTo(b1) > 0 ? b1 : b0;
+    public static @Nullable <T extends Comparable<T>> T lesser(@Nullable T b0, @Nullable T b1) {
+        if (b0 == null) {
+            return b1;
+        }
+        if (b1 == null) {
+            return b0;
+        }
+        return b0.compareTo(b1) > 0 ? b1 : b0;
     }
 
     /** LEAST operator. */
-    public static <T extends Comparable<T>> T least(T b0, T b1) {
+    public static @Nullable <T extends Comparable<T>> T least(@Nullable T b0, @Nullable T b1) {
         return b0 == null || b1 != null && b0.compareTo(b1) > 0 ? b1 : b0;
     }
 
@@ -3875,13 +4157,41 @@ public class SqlFunctions {
         return b0 > b1 ? b1 : b0;
     }
 
+    public static @Nullable <T extends Comparable<T>> List<T> lesser(
+            @Nullable List<T> b0, @Nullable List<T> b1) {
+        if (b0 == null) {
+            return b1;
+        }
+        if (b1 == null) {
+            return b0;
+        }
+        return lt(b0, b1) ? b0 : b1;
+    }
+
+    public static @Nullable <T extends Comparable<T>> List<T> greater(
+            @Nullable List<T> b0, @Nullable List<T> b1) {
+        if (b0 == null) {
+            return b1;
+        }
+        if (b1 == null) {
+            return b0;
+        }
+        return gt(b0, b1) ? b0 : b1;
+    }
+
     /** Helper for implementing MAX. Somewhat similar to GREATEST operator. */
-    public static <T extends Comparable<T>> T greater(T b0, T b1) {
-        return b0 == null || b0.compareTo(b1) < 0 ? b1 : b0;
+    public static @Nullable <T extends Comparable<T>> T greater(@Nullable T b0, @Nullable T b1) {
+        if (b0 == null) {
+            return b1;
+        }
+        if (b1 == null) {
+            return b0;
+        }
+        return b0.compareTo(b1) < 0 ? b1 : b0;
     }
 
     /** GREATEST operator. */
-    public static <T extends Comparable<T>> T greatest(T b0, T b1) {
+    public static @Nullable <T extends Comparable<T>> T greatest(@Nullable T b0, @Nullable T b1) {
         return b0 == null || b1 != null && b0.compareTo(b1) < 0 ? b1 : b0;
     }
 
@@ -4072,6 +4382,10 @@ public class SqlFunctions {
         return v == null ? castNonNull(null) : toInt(v);
     }
 
+    // Method tagged as non-deterministic because it can throw.
+    // The DeterministicCodeOptimizer may otherwise try to lift it out of try-catch blocks.
+    // See https://issues.apache.org/jira/browse/CALCITE-6753
+    @NonDeterministic
     public static int toInt(String s) {
         return parseInt(s.trim());
     }
@@ -5379,6 +5693,32 @@ public class SqlFunctions {
         return timestampToTime(localTimestamp(root));
     }
 
+    /** SQL {@code SYSTIMESTAMP} function. */
+    @NonDeterministic
+    public static long sysTimestamp(DataContext root) {
+        return DataContext.Variable.SYS_TIMESTAMP.get(root);
+    }
+
+    /**
+     * SQL {@code SYSDATE} function.
+     *
+     * <p>When the date is before 1970-01-01 00:00:00, for example: 1969-12-31 23:59:59, the
+     * timestamp will return a negative value, such as -1000. The date(days since epoch) returned by
+     * timestampToDate(-1000) is 0, so we need to additionally judge the result of
+     * timestampToTime(-1000). If its value is less than 0, we need to reduce date by 1 to ensure
+     * the accuracy of date.
+     */
+    @NonDeterministic
+    public static int sysDate(DataContext root) {
+        final long timestamp = sysTimestamp(root);
+        int date = timestampToDate(timestamp);
+        final int time = timestampToTime(timestamp);
+        if (time < 0) {
+            --date;
+        }
+        return date;
+    }
+
     @NonDeterministic
     public static TimeZone timeZone(DataContext root) {
         return DataContext.Variable.TIME_ZONE.get(root);
@@ -5509,8 +5849,16 @@ public class SqlFunctions {
     }
 
     /** SQL {@code REPLACE(string, search, replacement)} function. */
-    public static String replace(String s, String search, String replacement) {
-        return s.replace(search, replacement);
+    public static String replace(
+            String s, String search, String replacement, boolean isCaseSensitive) {
+        if (search.isEmpty()) {
+            return s;
+        }
+        if (isCaseSensitive) {
+            return s.replace(search, replacement);
+        }
+        // for MSSQL's REPLACE function, search pattern is case-insensitive during matching
+        return org.apache.commons.lang3.StringUtils.replaceIgnoreCase(s, search, replacement);
     }
 
     /**
@@ -5547,6 +5895,9 @@ public class SqlFunctions {
      * Implements the {@code [ ... ]} operator on an object whose type is not known until runtime.
      */
     public static @Nullable Object item(Object object, Object index) {
+        if (object instanceof VariantValue) {
+            return ((VariantValue) object).item(index);
+        }
         if (object instanceof Map) {
             return mapItem((Map) object, index);
         }
@@ -6212,6 +6563,15 @@ public class SqlFunctions {
         return list;
     }
 
+    /** SQL {@code ARRAY_SLICE(array, start, length)} function. */
+    public static List arraySlice(List list, int start, int length) {
+        // return empty list if start/length are out of range of the array
+        if (start + length > list.size()) {
+            return Collections.emptyList();
+        }
+        return list.subList(start, start + length);
+    }
+
     /** SQL {@code ARRAY_TO_STRING(array, delimiter)} function. */
     public static String arrayToString(List list, String delimiter) {
         return arrayToString(list, delimiter, null);
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java
index 08853d654e6..89f8f3825af 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlIntervalQualifier.java
@@ -44,26 +44,10 @@ import static org.apache.calcite.util.Static.RESOURCE;
 
 /**
  * The class copied from Calcite because of <a
- * href="https://issues.apache.org/jira/browse/CALCITE-6581">CALCITE-6581</a>. Lines:
+ * href="https://issues.apache.org/jira/browse/CALCITE-6345">CALCITE-6345</a>. Lines:
  *
  * <ul>
- *   <li>513~515
- *   <li>526~528
- *   <li>576~578
- *   <li>618~620
- *   <li>655~657
- *   <li>692~695
- *   <li>730~733
- *   <li>768~770
- *   <li>812~812
- *   <li>855~857
- *   <li>926~928
- *   <li>963~965
- *   <li>1005~1007
- *   <li>1071~1073
- *   <li>1108~1110
- *   <li>1171~1173
- *   <li>1230~1232
+ *   <li>Flink modification 267~269
  * </ul>
  */
 public class SqlIntervalQualifier extends SqlNode {
@@ -280,7 +264,9 @@ public class SqlIntervalQualifier extends SqlNode {
 
     public int getStartPrecision(RelDataTypeSystem typeSystem) {
         if (startPrecision == RelDataType.PRECISION_NOT_SPECIFIED) {
+            // FLINK MODIFICATION BEGIN
             return typeSystem.getDefaultPrecision(typeName());
+            // FLINK MODIFICATION END
         } else {
             return startPrecision;
         }
@@ -559,9 +545,7 @@ public class SqlIntervalQualifier extends SqlNode {
         return new BigDecimal("0." + secondFracStr).multiply(THOUSAND);
     }
 
-    // FLINK MODIFICATION BEGIN
     private static int[] fillYearMonthIntervalValueArray(
-            // FLINK MODIFICATION END
             int sign, BigDecimal year, BigDecimal month) {
         int[] ret = new int[3];
 
@@ -572,9 +556,7 @@ public class SqlIntervalQualifier extends SqlNode {
         return ret;
     }
 
-    // FLINK MODIFICATION BEGIN
     private static int[] fillDayTimeIntervalValueArray(
-            // FLINK MODIFICATION END
             int sign,
             BigDecimal day,
             BigDecimal hour,
@@ -622,9 +604,7 @@ public class SqlIntervalQualifier extends SqlNode {
             checkLeadFieldInRange(typeSystem, sign, year, TimeUnit.YEAR, pos);
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillYearMonthIntervalValueArray(sign, year, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -664,9 +644,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillYearMonthIntervalValueArray(sign, year, month);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -701,9 +679,7 @@ public class SqlIntervalQualifier extends SqlNode {
             checkLeadFieldInRange(typeSystem, sign, month, TimeUnit.MONTH, pos);
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillYearMonthIntervalValueArray(sign, ZERO, month);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -738,10 +714,8 @@ public class SqlIntervalQualifier extends SqlNode {
             checkLeadFieldInRange(typeSystem, sign, quarter, TimeUnit.QUARTER, pos);
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             final BigDecimal months = quarter.multiply(BigDecimal.valueOf(3));
             return fillYearMonthIntervalValueArray(sign, ZERO, months);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -776,10 +750,8 @@ public class SqlIntervalQualifier extends SqlNode {
             checkLeadFieldInRange(typeSystem, sign, week, TimeUnit.WEEK, pos);
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             final BigDecimal days = week.multiply(BigDecimal.valueOf(7));
             return fillDayTimeIntervalValueArray(sign, days, ZERO, ZERO, ZERO, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -814,9 +786,7 @@ public class SqlIntervalQualifier extends SqlNode {
             checkLeadFieldInRange(typeSystem, sign, day, TimeUnit.DAY, pos);
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, day, ZERO, ZERO, ZERO, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -856,9 +826,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, day, hour, ZERO, ZERO, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -901,9 +869,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, day, hour, minute, ZERO, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -972,9 +938,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, day, hour, minute, second, secondFrac);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -1009,9 +973,7 @@ public class SqlIntervalQualifier extends SqlNode {
             checkLeadFieldInRange(typeSystem, sign, hour, TimeUnit.HOUR, pos);
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, ZERO, hour, ZERO, ZERO, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -1051,9 +1013,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, ZERO, hour, minute, ZERO, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -1117,9 +1077,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, ZERO, hour, minute, second, secondFrac);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -1154,9 +1112,7 @@ public class SqlIntervalQualifier extends SqlNode {
             checkLeadFieldInRange(typeSystem, sign, minute, TimeUnit.MINUTE, pos);
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, ZERO, ZERO, minute, ZERO, ZERO);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -1217,9 +1173,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, ZERO, ZERO, minute, second, secondFrac);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
@@ -1276,9 +1230,7 @@ public class SqlIntervalQualifier extends SqlNode {
             }
 
             // package values up for return
-            // FLINK MODIFICATION BEGIN
             return fillDayTimeIntervalValueArray(sign, ZERO, ZERO, ZERO, second, secondFrac);
-            // FLINK MODIFICATION END
         } else {
             throw invalidValueException(pos, originalValue);
         }
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlUtil.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlUtil.java
index 3f2e070228a..2cf64bfbf87 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlUtil.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlUtil.java
@@ -77,7 +77,7 @@ import static org.apache.calcite.util.Static.RESOURCE;
 /**
  * Flink modification because of CALCITE-7027
  *
- * <p>Lines 827 ~ 833, should be removed after upgrading to 1.40.
+ * <p>Lines 830 ~ 836, should be removed after upgrading to 1.40.
  */
 public abstract class SqlUtil {
     // ~ Constants --------------------------------------------------------------
@@ -315,6 +315,7 @@ public abstract class SqlUtil {
                     // when it has 0 args, not "LOCALTIME()".
                     return;
                 case FUNCTION_STAR: // E.g. "COUNT(*)"
+                case FUNCTION_ID_CONSTANT: // E.g. "PI()"
                 case FUNCTION: // E.g. "RANK()"
                 case ORDERED_FUNCTION: // E.g. "STRING_AGG(x)"
                     // fall through - dealt with below
@@ -382,7 +383,9 @@ public abstract class SqlUtil {
             // look up in the standard sql operator table to see if it is a function
             // with empty argument list, e.g. LOCALTIME, we should not quote
             // such identifier cause quoted `LOCALTIME` always represents a sql identifier.
-            if (asFunctionID || operator.getSyntax() == SqlSyntax.FUNCTION_ID) {
+            if (asFunctionID
+                    || operator.getSyntax() == SqlSyntax.FUNCTION_ID
+                    || operator.getSyntax() == SqlSyntax.FUNCTION_ID_CONSTANT) {
                 writer.keyword(identifier.getSimple());
                 unparsedAsFunc = true;
             }
@@ -1023,7 +1026,6 @@ public abstract class SqlUtil {
      * @param charset charset
      * @throws RuntimeException If the given value cannot be represented in the given charset
      */
-    @SuppressWarnings("BetaApi")
     public static void validateCharset(ByteString value, Charset charset) {
         if (charset == StandardCharsets.UTF_8) {
             final byte[] bytes = value.getBytes();
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 32a99292c42..6bfc2cedf73 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -90,7 +90,7 @@ import static org.apache.calcite.linq4j.Nullness.castNonNull;
  * Implementation of {@link org.apache.calcite.sql.SqlOperatorTable} containing the standard
  * operators and functions.
  *
- * <p>Lines 765 ~ 767, 785 ~ 787, 796 ~ 798, 807 ~ 809, 818 ~ 820, 829 ~ 831, 840 ~ 842, Flink
+ * <p>Lines 822 ~ 824, 842 ~ 844, 853 ~ 855, 864 ~ 866, 875 ~ 877, 886 ~ 888, 897 ~ 899, Flink
  * changes the return type of the {@code IS [NOT] JSON ...} predicates from {@link
  * ReturnTypes#BOOLEAN_NULLABLE} to {@link ReturnTypes#BOOLEAN} so that they always return a
  * non-nullable {@code BOOLEAN}, see also FLINK-39943.
@@ -180,7 +180,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
 
     /**
      * <code>DEFAULT</code> operator indicates that an argument to a function call is to take its
-     * default value..
+     * default value.
      */
     public static final SqlSpecialOperator DEFAULT = new SqlDefaultOperator();
 
@@ -277,6 +277,17 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                     InferTypes.FIRST_KNOWN,
                     OperandTypes.DIVISION_OPERATOR);
 
+    /** Checked version of arithmetic division operator, '<code>/</code>'. */
+    public static final SqlBinaryOperator CHECKED_DIVIDE =
+            new SqlBinaryOperator(
+                    "/",
+                    SqlKind.CHECKED_DIVIDE,
+                    60,
+                    true,
+                    ReturnTypes.QUOTIENT_NULLABLE,
+                    InferTypes.FIRST_KNOWN,
+                    OperandTypes.DIVISION_OPERATOR);
+
     /**
      * Arithmetic remainder operator, '<code>%</code>', an alternative to {@link #MOD} allowed if
      * under certain conformance levels.
@@ -325,6 +336,17 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                     InferTypes.FIRST_KNOWN,
                     OperandTypes.DIVISION_OPERATOR);
 
+    /** Checked version of integer division. @see DIVIDE_INTEGER. */
+    public static final SqlBinaryOperator CHECKED_DIVIDE_INTEGER =
+            new SqlBinaryOperator(
+                    "/INT",
+                    SqlKind.CHECKED_DIVIDE,
+                    60,
+                    true,
+                    ReturnTypes.INTEGER_QUOTIENT_NULLABLE,
+                    InferTypes.FIRST_KNOWN,
+                    OperandTypes.DIVISION_OPERATOR);
+
     /** Dot operator, '<code>.</code>', used for referencing fields of records. */
     public static final SqlOperator DOT = new SqlDotOperator();
 
@@ -511,6 +533,19 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                     InferTypes.FIRST_KNOWN,
                     OperandTypes.MINUS_OPERATOR);
 
+    /** Checked version of infix arithmetic minus operator, '<code>-</code>'. */
+    public static final SqlBinaryOperator CHECKED_MINUS =
+            new SqlMonotonicBinaryOperator(
+                    "-",
+                    SqlKind.CHECKED_MINUS,
+                    40,
+                    true,
+
+                    // Same type inference strategy as sum
+                    ReturnTypes.NULLABLE_SUM,
+                    InferTypes.FIRST_KNOWN,
+                    OperandTypes.MINUS_OPERATOR);
+
     /** Arithmetic multiplication operator, '<code>*</code>'. */
     public static final SqlBinaryOperator MULTIPLY =
             new SqlMonotonicBinaryOperator(
@@ -522,6 +557,17 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                     InferTypes.FIRST_KNOWN,
                     OperandTypes.MULTIPLY_OPERATOR);
 
+    /** Checked version of arithmetic multiplication operator, '<code>*</code>'. */
+    public static final SqlBinaryOperator CHECKED_MULTIPLY =
+            new SqlMonotonicBinaryOperator(
+                    "*",
+                    SqlKind.CHECKED_TIMES,
+                    60,
+                    true,
+                    ReturnTypes.PRODUCT_NULLABLE,
+                    InferTypes.FIRST_KNOWN,
+                    OperandTypes.MULTIPLY_OPERATOR);
+
     /** Logical not-equals operator, '<code>&lt;&gt;</code>'. */
     public static final SqlBinaryOperator NOT_EQUALS =
             new SqlBinaryOperator(
@@ -555,6 +601,17 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                     InferTypes.FIRST_KNOWN,
                     OperandTypes.PLUS_OPERATOR);
 
+    /** Checked version of infix arithmetic plus operator, '<code>+</code>'. */
+    public static final SqlBinaryOperator CHECKED_PLUS =
+            new SqlMonotonicBinaryOperator(
+                    "+",
+                    SqlKind.CHECKED_PLUS,
+                    40,
+                    true,
+                    ReturnTypes.NULLABLE_SUM,
+                    InferTypes.FIRST_KNOWN,
+                    OperandTypes.PLUS_OPERATOR);
+
     /** Infix datetime plus operator, '<code>DATETIME + INTERVAL</code>'. */
     public static final SqlSpecialOperator DATETIME_PLUS = new SqlDatetimePlusOperator();
 
@@ -909,6 +966,16 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                     InferTypes.RETURN_TYPE,
                     OperandTypes.NUMERIC_OR_INTERVAL);
 
+    /** Checked version of prefix arithmetic minus operator, '<code>-</code>'. */
+    public static final SqlPrefixOperator CHECKED_UNARY_MINUS =
+            new SqlPrefixOperator(
+                    "-",
+                    SqlKind.CHECKED_MINUS_PREFIX,
+                    80,
+                    ReturnTypes.ARG0,
+                    InferTypes.RETURN_TYPE,
+                    OperandTypes.NUMERIC_OR_INTERVAL);
+
     /**
      * Prefix arithmetic plus operator, '<code>+</code>'.
      *
@@ -1444,8 +1511,14 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
     public static final SqlFunction SUBSTRING = new SqlSubstringFunction();
 
     /**
-     * The {@code REPLACE(string, search, replace)} function. Not standard SQL, but in Oracle and
-     * Postgres.
+     * The {@code REPLACE(string, search, replace)} function. Not standard SQL, but in Oracle,
+     * PostgreSQL and Microsoft SQL Server.
+     *
+     * <p>REPLACE behaves a little different in Microsoft SQL Server, whose search pattern is
+     * case-insensitive during matching.
+     *
+     * <p>For example, {@code REPLACE(('ciAao', 'a', 'ciao'))} returns "ciAciaoo" in both Oracle and
+     * PostgreSQL, but returns "ciciaociaoo" in Microsoft SQL Server.
      */
     public static final SqlFunction REPLACE =
             SqlBasicFunction.create(
@@ -1705,7 +1778,23 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                             ReturnTypes.DOUBLE,
                             OperandTypes.NILADIC,
                             SqlFunctionCategory.NUMERIC)
-                    .withSyntax(SqlSyntax.FUNCTION_ID);
+                    .withSyntax(SqlSyntax.FUNCTION_ID_CONSTANT);
+
+    /** The {@code TYPEOF} function. */
+    public static final SqlFunction TYPEOF =
+            SqlBasicFunction.create(
+                    "TYPEOF",
+                    ReturnTypes.VARCHAR,
+                    OperandTypes.VARIANT,
+                    SqlFunctionCategory.STRING);
+
+    /** The {@code VARIANTNULL} function. */
+    public static final SqlFunction VARIANTNULL =
+            SqlBasicFunction.create(
+                    "VARIANTNULL",
+                    ReturnTypes.VARIANT,
+                    OperandTypes.NILADIC,
+                    SqlFunctionCategory.SYSTEM);
 
     /** {@code FIRST} function to be used within {@code MATCH_RECOGNIZE}. */
     public static final SqlFunction FIRST =
@@ -1820,19 +1909,6 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
                     OperandTypes.family(
                             SqlTypeFamily.ANY, SqlTypeFamily.DATETIME, SqlTypeFamily.DATETIME));
 
-    /**
-     * Use of the <code>IN_FENNEL</code> operator forces the argument to be evaluated in Fennel.
-     * Otherwise acts as identity function.
-     */
-    public static final SqlFunction IN_FENNEL =
-            new SqlMonotonicUnaryFunction(
-                    "$IN_FENNEL",
-                    SqlKind.OTHER_FUNCTION,
-                    ReturnTypes.ARG0,
-                    null,
-                    OperandTypes.ANY,
-                    SqlFunctionCategory.SYSTEM);
-
     /**
      * The SQL <code>CAST</code> operator.
      *
@@ -1974,7 +2050,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
      * <p>MAP is not standard SQL.
      */
     public static final SqlOperator ITEM =
-            new SqlItemOperator("ITEM", OperandTypes.ARRAY_OR_MAP, 1, true);
+            new SqlItemOperator("ITEM", OperandTypes.ARRAY_OR_MAP_OR_VARIANT, 1, true);
 
     /** The ARRAY Value Constructor. e.g. "<code>ARRAY[1, 2, 3]</code>". */
     public static final SqlArrayValueConstructor ARRAY_VALUE_CONSTRUCTOR =
@@ -2550,4 +2626,17 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
             return floor ? SqlStdOperatorTable.FLOOR : SqlStdOperatorTable.CEIL;
         }
     }
+
+    /**
+     * Returns the operator for standard {@code CONVERT} and Oracle's {@code CONVERT} with the given
+     * library.
+     */
+    public static SqlOperator getConvertFuncByConformance(SqlConformance conformance) {
+        if (SqlConformanceEnum.ORACLE_10 == conformance
+                || SqlConformanceEnum.ORACLE_12 == conformance) {
+            return SqlLibraryOperators.CONVERT_ORACLE;
+        } else {
+            return SqlStdOperatorTable.CONVERT;
+        }
+    }
 }
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java
index 00803660b79..157256e8973 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/BasicSqlType.java
@@ -144,7 +144,7 @@ public class BasicSqlType extends AbstractSqlType {
     // ~ Methods ----------------------------------------------------------------
 
     /** Constructs a type with nullablity. */
-    BasicSqlType createWithNullability(boolean nullable) {
+    public BasicSqlType createWithNullability(boolean nullable) {
         if (nullable == this.isNullable) {
             return this;
         }
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java
index 75380a252d9..7975e6ed40e 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRule.java
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.calcite.sql.type;
 
 import com.google.common.collect.ImmutableMap;
@@ -213,6 +212,9 @@ public class SqlTypeAssignmentRule implements SqlTypeMappingRule {
         rules.add(SqlTypeName.COLUMN_LIST, EnumSet.of(SqlTypeName.COLUMN_LIST));
         // ----- FLINK MODIFICATION END -----
 
+        // UUID is assignable from
+        rules.add(SqlTypeName.UUID, EnumSet.of(SqlTypeName.UUID));
+
         // ANY is assignable from ...
         rule.clear();
         rule.add(SqlTypeName.TINYINT);
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java
index 5ceb0203eb6..00f3c43fcb7 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java
@@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
 import java.nio.charset.Charset;
 import java.util.List;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 import static org.apache.calcite.util.Static.RESOURCE;
 
@@ -39,10 +40,8 @@ import static org.apache.calcite.util.Static.RESOURCE;
  * <p>FLINK modifications are at lines
  *
  * <ol>
- *   <li>Should be removed after fixing CALCITE-6342: Lines 113-115
- *   <li>Added in FLINK-39695 (backport of CALCITE-6764): Lines 243 ~ 266
- *   <li>Should be removed after fix of FLINK-31350: Lines 527-539.
- *   <li>Should be removed after fixing CALCITE-6342: Lines 606-618
+ *   <li>Should be removed after fixing CALCITE-6342: Lines 527-529.
+ *   <li>Should be removed after fix of FLINK-31350: Lines 604-616.
  * </ol>
  */
 public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
@@ -67,7 +66,7 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
     @Override
     public RelDataType createSqlType(SqlTypeName typeName, int precision) {
         if (typeName.allowsScale()) {
-            return createSqlType(typeName, precision, typeName.getDefaultScale());
+            return createSqlType(typeName, precision, typeSystem.getDefaultScale(typeName));
         }
         final int maxPrecision = typeSystem.getMaxPrecision(typeName);
         if (maxPrecision >= 0 && precision > maxPrecision) {
@@ -84,24 +83,27 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
         return canonize(newType);
     }
 
+    @SuppressWarnings("deprecation") // [CALCITE-6598]
     @Override
     public RelDataType createSqlType(SqlTypeName typeName, int precision, int scale) {
         assertBasic(typeName);
         assert (precision >= 0) || (precision == RelDataType.PRECISION_NOT_SPECIFIED);
-        final int maxPrecision = typeSystem.getMaxPrecision(typeName);
-        if (maxPrecision >= 0 && precision > maxPrecision) {
-            precision = maxPrecision;
-        }
-        if (precision != RelDataType.PRECISION_NOT_SPECIFIED
-                && precision < typeSystem.getMinPrecision(typeName)) {
-            throw RESOURCE.invalidPrecisionForDecimalType(precision, maxPrecision).ex();
+        if (precision != RelDataType.PRECISION_NOT_SPECIFIED) {
+            final int minPrecision = typeSystem.getMinPrecision(typeName);
+            final int maxPrecision = typeSystem.getMaxPrecision(typeName);
+            if (maxPrecision >= 0 && precision > maxPrecision) {
+                precision = maxPrecision;
+            }
+            if (precision < minPrecision) {
+                throw RESOURCE.invalidPrecisionForDecimalType(precision, maxPrecision).ex();
+            }
         }
-        if (scale != RelDataType.SCALE_NOT_SPECIFIED && scale < typeSystem.getMinScale(typeName)) {
-            throw RESOURCE.invalidScaleForDecimalType(
-                            scale,
-                            typeSystem.getMinScale(typeName),
-                            typeSystem.getMaxNumericScale())
-                    .ex();
+        if (scale != RelDataType.SCALE_NOT_SPECIFIED) {
+            final int minScale = typeSystem.getMinScale(typeName);
+            final int maxScale = typeSystem.getMaxNumericScale();
+            if (scale < minScale) {
+                throw RESOURCE.invalidScaleForDecimalType(scale, minScale, maxScale).ex();
+            }
         }
         RelDataType newType = new BasicSqlType(typeSystem, typeName, precision, scale);
         newType = SqlTypeUtil.addCharsetAndCollation(newType, this);
@@ -110,9 +112,7 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
 
     @Override
     public RelDataType createUnknownType() {
-        // FLINK MODIFICATION BEGIN
-        return canonize(new UnknownSqlType(this));
-        // FLINK MODIFICATION END
+        return createSqlType(SqlTypeName.UNKNOWN);
     }
 
     @Override
@@ -174,9 +174,11 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
     }
 
     @Override
-    public @Nullable RelDataType leastRestrictive(List<RelDataType> types) {
-        assert types != null;
-        assert types.size() >= 1;
+    public @Nullable RelDataType leastRestrictive(
+            List<RelDataType> types, SqlTypeMappingRule mappingRule) {
+        requireNonNull(types, "types");
+        requireNonNull(mappingRule, "mappingRule");
+        checkArgument(types.size() >= 1, "types.size >= 1");
 
         RelDataType type0 = types.get(0);
         if (type0.getSqlTypeName() != null) {
@@ -184,13 +186,14 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
             if (resultType != null) {
                 return resultType;
             }
-            return leastRestrictiveByCast(types);
+            return leastRestrictiveByCast(types, mappingRule);
         }
 
-        return super.leastRestrictive(types);
+        return super.leastRestrictive(types, mappingRule);
     }
 
-    private @Nullable RelDataType leastRestrictiveByCast(List<RelDataType> types) {
+    private @Nullable RelDataType leastRestrictiveByCast(
+            List<RelDataType> types, SqlTypeMappingRule mappingRule) {
         RelDataType resultType = types.get(0);
         boolean anyNullable = resultType.isNullable();
         for (int i = 1; i < types.size(); i++) {
@@ -204,10 +207,10 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
                 anyNullable = true;
             }
 
-            if (SqlTypeUtil.canCastFrom(type, resultType, false)) {
+            if (SqlTypeUtil.canCastFrom(type, resultType, mappingRule)) {
                 resultType = type;
             } else {
-                if (!SqlTypeUtil.canCastFrom(resultType, type, false)) {
+                if (!SqlTypeUtil.canCastFrom(resultType, type, mappingRule)) {
                     return null;
                 }
             }
@@ -240,8 +243,6 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
         return canonize(newType);
     }
 
-    // ----- FLINK MODIFICATION BEGIN -----
-    // Backport from Calcite (CALCITE-6764)
     @Override
     public RelDataType enforceTypeWithNullability(final RelDataType type, final boolean nullable) {
         final RelDataType newType;
@@ -263,8 +264,6 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
         return canonize(newType);
     }
 
-    // ----- FLINK MODIFICATION END -----
-
     private static void assertBasic(SqlTypeName typeName) {
         assert typeName != null;
         assert typeName != SqlTypeName.MULTISET : "use createMultisetType() instead";
@@ -275,6 +274,7 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
                 : "use createSqlIntervalType() instead";
     }
 
+    @SuppressWarnings("deprecation") // [CALCITE-6598]
     private @Nullable RelDataType leastRestrictiveSqlType(List<RelDataType> types) {
         RelDataType resultType = null;
         int nullCount = 0;
@@ -525,9 +525,8 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
                 }
 
                 // FLINK MODIFICATION BEGIN
-                // in case we compare TIME(STAMP) and TIME(STAMP)_LTZ we should adjust the precision
-                // as well
                 if (type.getSqlTypeName().getFamily() == resultType.getSqlTypeName().getFamily()
+                        // FLINK MODIFICATION END
                         && type.getSqlTypeName().allowsPrec()
                         && type.getPrecision() != resultType.getPrecision()) {
                     final int precision =
@@ -536,7 +535,6 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
 
                     resultType = createSqlType(type.getSqlTypeName(), precision);
                 }
-                // FLINK MODIFICATION END
             } else {
                 return null;
             }
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
index 636590d5eec..105f6c0682f 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
@@ -89,7 +89,7 @@ public abstract class SqlTypeUtil {
      *     same charset and collation of same charset
      */
     public static boolean isCharTypeComparable(List<RelDataType> argTypes) {
-        assert argTypes != null;
+        requireNonNull(argTypes, "argTypes");
         assert argTypes.size() >= 2;
 
         // Filter out ANY and NULL elements.
@@ -574,17 +574,34 @@ public abstract class SqlTypeUtil {
             }
             return true;
         }
-        RelDataType comp1 = t1.getComponentType();
-        RelDataType comp2 = t2.getComponentType();
-        if ((comp1 != null) || (comp2 != null)) {
-            if ((comp1 == null) || (comp2 == null)) {
+        SqlTypeName t1Name = t1.getSqlTypeName();
+        SqlTypeName t2Name = t2.getSqlTypeName();
+        if (t1Name == SqlTypeName.ARRAY || t1Name == SqlTypeName.MULTISET) {
+            if (t1Name != t2Name) {
                 return false;
             }
-            if (!sameNamedType(comp1, comp2)) {
+
+            RelDataType comp1 = requireNonNull(t1.getComponentType());
+            RelDataType comp2 = requireNonNull(t2.getComponentType());
+            return sameNamedType(comp1, comp2);
+        }
+
+        if (t1Name == SqlTypeName.MAP) {
+            if (t1Name != t2Name) {
                 return false;
             }
+
+            RelDataType keyType1 = requireNonNull(t1.getKeyType());
+            RelDataType keyType2 = requireNonNull(t2.getKeyType());
+            if (!sameNamedType(keyType1, keyType2)) {
+                return false;
+            }
+            RelDataType valueType1 = requireNonNull(t1.getValueType());
+            RelDataType valueType2 = requireNonNull(t2.getValueType());
+            return sameNamedType(valueType1, valueType2);
         }
-        return t1.getSqlTypeName() == t2.getSqlTypeName();
+
+        return t1Name == t2Name;
     }
 
     /**
@@ -751,6 +768,10 @@ public abstract class SqlTypeUtil {
         return t.getFamily() == SqlTypeFamily.ANY;
     }
 
+    private static boolean isVariant(RelDataType t) {
+        return t.getFamily() == SqlTypeFamily.VARIANT;
+    }
+
     public static boolean isMeasure(RelDataType t) {
         return t instanceof MeasureSqlType;
     }
@@ -861,7 +882,7 @@ public abstract class SqlTypeUtil {
             return canCastFrom(
                     toType, requireNonNull(fromType.getMeasureElementType()), typeMappingRule);
         }
-        if (isAny(toType) || isAny(fromType)) {
+        if (isAny(toType) || isAny(fromType) || isVariant(toType) || isVariant(fromType)) {
             return true;
         }
 
@@ -870,6 +891,11 @@ public abstract class SqlTypeUtil {
         if (toTypeName == SqlTypeName.UNKNOWN) {
             return true;
         }
+        if (toType.getSqlTypeName() == SqlTypeName.UUID) {
+            return fromType.getSqlTypeName() == SqlTypeName.UUID
+                    || fromType.getFamily() == SqlTypeFamily.CHARACTER
+                    || fromType.getFamily() == SqlTypeFamily.BINARY;
+        }
         if (toType.isStruct() || fromType.isStruct()) {
             if (toTypeName == SqlTypeName.DISTINCT) {
                 if (fromTypeName == SqlTypeName.DISTINCT) {
@@ -1049,7 +1075,7 @@ public abstract class SqlTypeUtil {
 
         // TODO jvs 28-Dec-2004:  support row types, user-defined types,
         // interval types, multiset types, etc
-        assert typeName != null;
+        requireNonNull(typeName, "typeName");
 
         final SqlTypeNameSpec typeNameSpec;
         if (isAtomic(type)
@@ -1432,6 +1458,15 @@ public abstract class SqlTypeUtil {
      * @return Whether types are comparable
      */
     public static boolean isComparable(RelDataType type1, RelDataType type2) {
+        // FLINK MODIFICATION BEGIN Calcite-7230
+        final RelDataTypeFamily family1 = family(type1);
+        final RelDataTypeFamily family2 = family(type2);
+
+        // If one of the arguments is of type 'NULL', return true.
+        if (family1 == SqlTypeFamily.NULL || family2 == SqlTypeFamily.NULL) {
+            return true;
+        }
+        // FLINK MODIFICATION END
         if (type1.isStruct() != type2.isStruct()) {
             return false;
         }
@@ -1450,8 +1485,28 @@ public abstract class SqlTypeUtil {
             return true;
         }
 
-        final RelDataTypeFamily family1 = family(type1);
-        final RelDataTypeFamily family2 = family(type2);
+        SqlTypeName type1Name = type1.getSqlTypeName();
+        SqlTypeName type2Name = type2.getSqlTypeName();
+        if (type1Name == SqlTypeName.ARRAY || type1Name == SqlTypeName.MULTISET) {
+            if (type2Name != type1Name) {
+                return false;
+            }
+            RelDataType elementType1 = requireNonNull(type1.getComponentType());
+            RelDataType elementType2 = requireNonNull(type2.getComponentType());
+            return isComparable(elementType1, elementType2);
+        }
+
+        if (type1Name == SqlTypeName.MAP) {
+            if (type2Name != type1Name) {
+                return false;
+            }
+            RelDataType keyType1 = requireNonNull(type1.getKeyType());
+            RelDataType keyType2 = requireNonNull(type2.getKeyType());
+            RelDataType valueType1 = requireNonNull(type1.getValueType());
+            RelDataType valueType2 = requireNonNull(type2.getValueType());
+            return isComparable(keyType1, keyType2) && isComparable(valueType1, valueType2);
+        }
+
         if (family1 == family2) {
             return true;
         }
@@ -1467,12 +1522,8 @@ public abstract class SqlTypeUtil {
         }
 
         // We can implicitly convert from character to date
-        if (family1 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family2)
-                || family2 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family1)) {
-            return true;
-        }
-
-        return false;
+        return family1 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family2)
+                || family2 == SqlTypeFamily.CHARACTER && canConvertStringInCompare(family1);
     }
 
     /**
@@ -1760,6 +1811,7 @@ public abstract class SqlTypeUtil {
     }
 
     /** Returns a DECIMAL type with the maximum precision for the current type system. */
+    @SuppressWarnings("deprecation") // [CALCITE-6598]
     public static RelDataType getMaxPrecisionScaleDecimal(RelDataTypeFactory factory) {
         int maxPrecision = factory.getTypeSystem().getMaxNumericPrecision();
         int maxScale = factory.getTypeSystem().getMaxNumericScale();
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index ffd2447f126..f8f1ffd217e 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -33,7 +33,6 @@ import org.apache.calcite.rel.type.DynamicRecordType;
 import org.apache.calcite.rel.type.RelCrossType;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rel.type.RelRecordType;
@@ -134,6 +133,7 @@ import org.checkerframework.checker.nullness.qual.KeyFor;
 import org.checkerframework.checker.nullness.qual.NonNull;
 import org.checkerframework.checker.nullness.qual.Nullable;
 import org.checkerframework.checker.nullness.qual.PolyNull;
+import org.checkerframework.checker.nullness.qual.RequiresNonNull;
 import org.checkerframework.dataflow.qual.Pure;
 import org.slf4j.Logger;
 
@@ -163,7 +163,7 @@ import java.util.function.Consumer;
 import java.util.function.Supplier;
 import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
+import java.util.stream.Stream;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
@@ -187,39 +187,39 @@ import static org.apache.calcite.util.Util.first;
  * <p>Lines 241 ~ 244, Flink improves error message for functions without appropriate arguments in
  * handleUnresolvedFunction.
  *
- * <p>Lines 1378 ~ 1380, CALCITE-7217, should be removed after upgrading Calcite to 1.41.0.
+ * <p>Lines 1390 ~ 1392, CALCITE-7217, should be removed after upgrading Calcite to 1.41.0.
  *
- * <p>Lines 2139 ~ 2153, Flink improves error message for functions without appropriate arguments in
+ * <p>Lines 2165 ~ 2179, Flink improves error message for functions without appropriate arguments in
  * handleUnresolvedFunction at {@link SqlValidatorImpl#handleUnresolvedFunction}.
  *
- * <p>Lines 2408 ~ 2419
+ * <p>Lines 2434 ~ 2445
  *
- * <p>Lines 2575 ~ 2577, CALCITE-7471 should be removed after upgrading Calcite to 1.42.0.
+ * <p>Lines 2601 ~ 2603, CALCITE-7471 should be removed after upgrading Calcite to 1.42.0.
  *
- * <p>Lines 2691 ~ 2710, CALCITE-7217, CALCITE-7312 should be removed after upgrading Calcite to
+ * <p>Lines 2717 ~ 2736, CALCITE-7217, CALCITE-7312 should be removed after upgrading Calcite to
  * 1.42.0.
  *
- * <p>Line 2741 ~2759, set the correct scope for VECTOR_SEARCH.
+ * <p>Lines 2767 ~ 2785, set the correct scope for VECTOR_SEARCH.
  *
- * <p>Lines 4293 ~ 4297, 7029 ~ 7035 Flink improves Optimize the retrieval of sub-operands in
+ * <p>Lines 4335 ~ 4339, 7183 ~ 7189 Flink improves Optimize the retrieval of sub-operands in
  * SqlCall when using NamedParameters at {@link SqlValidatorImpl#checkRollUp}.
  *
- * <p>Lines 5760 ~ 5766, FLINK-24352 Add null check for temporal table check on SqlSnapshot.
+ * <p>Lines 5914 ~ 5920, FLINK-24352 Add null check for temporal table check on SqlSnapshot.
  *
- * <p>Lines 6177-6192, CALCITE-7538 should be removed after upgrading Calcite to 1.42.0.
+ * <p>Lines 6331-6346, CALCITE-7538 should be removed after upgrading Calcite to 1.42.0.
  *
- * <p>Lines 6202-6204, CALCITE-7466 should be removed after upgrading Calcite to 1.42.0.
+ * <p>Lines 6356-6358, CALCITE-7466 should be removed after upgrading Calcite to 1.42.0.
  *
- * <p>Lines 6258-6260, CALCITE-7470 should be removed after upgrading Calcite to 1.42.0.
+ * <p>Lines 6412-6414, CALCITE-7470 should be removed after upgrading Calcite to 1.42.0.
  *
- * <p>Lines 7120-7130, Added in FLINK-39695 (backport of CALCITE-6764): propagate parent record
+ * <p>Lines 7274-7282, Added in FLINK-39695 (backport of CALCITE-6764): propagate parent record
  * nullability to nested fields.
  *
- * <p>Lines 7695-7718, CALCITE-7486 should be removed after upgrading Calcite to 1.42.0.
+ * <p>Lines 7879-7902, CALCITE-7486 should be removed after upgrading Calcite to 1.42.0.
  *
- * <p>Lines 7765-7782, CALCITE-7486 should be removed after upgrading Calcite to 1.42.0.
+ * <p>Lines 7949-7966, CALCITE-7486 should be removed after upgrading Calcite to 1.42.0.
  *
- * <p>Lines 7827-7835, CALCITE-7486 should be removed after upgrading Calcite to 1.42.0.
+ * <p>Lines 8011-8019, CALCITE-7486 should be removed after upgrading Calcite to 1.42.0.
  */
 public class SqlValidatorImpl implements SqlValidatorWithHints {
     // ~ Static fields/initializers ---------------------------------------------
@@ -1196,15 +1196,27 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
             RelDataType type = namespace.getType();
 
             if (node == top) {
-                // A top-level namespace must not return any must-filter fields.
+                final FilterRequirement filterRequirement = namespace.getFilterRequirement();
+
+                // Either of the following two conditions result in an invalid query:
+                // 1) A top-level namespace must not return any must-filter fields.
                 // A non-top-level namespace (e.g. a subquery) may return must-filter
                 // fields; these are neutralized if the consuming query filters on them.
-                final ImmutableBitSet mustFilterFields = namespace.getMustFilterFields();
-                if (!mustFilterFields.isEmpty()) {
+                // 2) A top-level namespace must not have any remnant-must-filter fields.
+                // Remnant must filter fields are fields that are not selected and cannot
+                // be defused unless a bypass field defuses it.
+                if (!filterRequirement.filterFields.isEmpty()
+                        || !filterRequirement.remnantFilterFields.isEmpty()) {
+                    Stream<String> mustFilterStream =
+                            filterRequirement.filterFields.stream()
+                                    .mapToObj(namespace.getRowType().getFieldNames()::get);
+                    Stream<String> remnantStream =
+                            filterRequirement.remnantFilterFields.stream()
+                                    .map(q -> q.suffix().get(0));
+
                     // Set of field names, sorted alphabetically for determinism.
                     Set<String> fieldNameSet =
-                            StreamSupport.stream(mustFilterFields.spliterator(), false)
-                                    .map(namespace.getRowType().getFieldNames()::get)
+                            Stream.concat(mustFilterStream, remnantStream)
                                     .collect(Collectors.toCollection(TreeSet::new));
                     throw newValidationError(
                             node, RESOURCE.mustFilterFieldsMissing(fieldNameSet.toString()));
@@ -2012,19 +2024,31 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
 
     @Override
     public @Nullable SqlCall makeNullaryCall(SqlIdentifier id) {
-        if (id.names.size() == 1 && !id.isComponentQuoted(0)) {
+        if (!id.isComponentQuoted(id.names.size() - 1)) {
             final List<SqlOperator> list = new ArrayList<>();
             opTab.lookupOperatorOverloads(
                     id, null, SqlSyntax.FUNCTION, list, catalogReader.nameMatcher());
             for (SqlOperator operator : list) {
-                if (operator.getSyntax() == SqlSyntax.FUNCTION_ID) {
+                if (operator.getSyntax() == SqlSyntax.FUNCTION_ID
+                        || operator.getSyntax() == SqlSyntax.FUNCTION_ID_CONSTANT) {
                     // Even though this looks like an identifier, it is a
                     // actually a call to a function. Construct a fake
                     // call to this function, so we can use the regular
                     // operator validation.
-                    return new SqlBasicCall(
-                                    operator, ImmutableList.of(), id.getParserPosition(), null)
-                            .withExpanded(true);
+                    SqlCall sqlCall =
... 2873 lines suppressed ...


(flink) 03/04: [FLINK-39817][table] Replace deprecated `LogicalProject.create`

Posted by sn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git

commit 160aba5fd4a03213fd690f3141dfd3ef6e7492a6
Author: Sergey Nuyanzin <sn...@gmail.com>
AuthorDate: Sun Jun 21 10:37:38 2026 +0200

    [FLINK-39817][table] Replace deprecated `LogicalProject.create`
---
 .../table/planner/plan/rules/logical/WindowGroupReorderRule.java     | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRule.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRule.java
index b80bcc7c5f6..12f59c6b69b 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRule.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WindowGroupReorderRule.java
@@ -134,9 +134,10 @@ public class WindowGroupReorderRule
             LogicalProject project =
                     LogicalProject.create(
                             newLogicalWindow,
-                            Collections.emptyList(),
+                            window.getHints(),
                             projects,
-                            window.getRowType());
+                            window.getRowType(),
+                            window.getVariablesSet());
             call.transformTo(project);
         }
     }


(flink) 02/04: [FLINK-39817][table] Do not use deprecated `SqlPrettyWriter` constructor

Posted by sn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git

commit 0e852ba33e1844bcdb9c32710839c9301b51f0f9
Author: Sergey Nuyanzin <sn...@gmail.com>
AuthorDate: Sun Jun 21 10:33:13 2026 +0200

    [FLINK-39817][table] Do not use deprecated `SqlPrettyWriter` constructor
---
 .../sql/parser/ExtendedSqlRowTypeNameSpecTest.java      | 17 ++++++++++-------
 .../apache/flink/sql/parser/FlinkDDLDataTypeTest.java   |  6 +++++-
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/ExtendedSqlRowTypeNameSpecTest.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/ExtendedSqlRowTypeNameSpecTest.java
index d67aca371a8..5afac37429c 100644
--- a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/ExtendedSqlRowTypeNameSpecTest.java
+++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/ExtendedSqlRowTypeNameSpecTest.java
@@ -92,12 +92,15 @@ class ExtendedSqlRowTypeNameSpecTest {
                         .withParserFactory((SqlParserImplFactory) options.get("parserFactory"));
 
         return new SqlPrettyWriter(
-                new CalciteSqlDialect(
-                        SqlDialect.EMPTY_CONTEXT
-                                .withQuotedCasing(parserConfig.unquotedCasing())
-                                .withConformance(parserConfig.conformance())
-                                .withUnquotedCasing(parserConfig.unquotedCasing())
-                                .withIdentifierQuoteString(parserConfig.quoting().string)),
-                false);
+                SqlPrettyWriter.config()
+                        .withDialect(
+                                new CalciteSqlDialect(
+                                        SqlDialect.EMPTY_CONTEXT
+                                                .withQuotedCasing(parserConfig.unquotedCasing())
+                                                .withConformance(parserConfig.conformance())
+                                                .withUnquotedCasing(parserConfig.unquotedCasing())
+                                                .withIdentifierQuoteString(
+                                                        parserConfig.quoting().string)))
+                        .withAlwaysUseParentheses(false));
     }
 }
diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkDDLDataTypeTest.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkDDLDataTypeTest.java
index d6ff47750bc..71901fe9de5 100644
--- a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkDDLDataTypeTest.java
+++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkDDLDataTypeTest.java
@@ -546,7 +546,11 @@ class FlinkDDLDataTypeTest {
             SqlNodeList columns = sqlCreateTable.getColumnList();
             assertThat(columns.size()).isEqualTo(1);
             SqlDataTypeSpec dataTypeSpec = ((SqlRegularColumn) columns.get(0)).getType();
-            SqlWriter sqlWriter = new SqlPrettyWriter(factory.createSqlDialect(), false);
+            SqlWriter sqlWriter =
+                    new SqlPrettyWriter(
+                            SqlPrettyWriter.config()
+                                    .withDialect(factory.createSqlDialect())
+                                    .withAlwaysUseParentheses(false));
             dataTypeSpec.unparse(sqlWriter, 0, 0);
             // SqlDataTypeSpec does not take care of the nullable attribute unparse,
             // So we unparse nullable attribute specifically, this unparsing logic should


(flink) 04/04: [FLINK-39817][table] Replace deprecated `JavaConversions` in `TemporalJoinUtil`

Posted by sn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git

commit 316d926c34ef16e91fe27a580c7d7ae441fd586b
Author: Sergey Nuyanzin <sn...@gmail.com>
AuthorDate: Sun Jun 21 10:59:50 2026 +0200

    [FLINK-39817][table] Replace deprecated `JavaConversions` in `TemporalJoinUtil`
---
 .../flink/table/planner/plan/utils/TemporalJoinUtil.scala | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/TemporalJoinUtil.scala b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/TemporalJoinUtil.scala
index 7ab1f8c54ad..f1437ff32a3 100644
--- a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/TemporalJoinUtil.scala
+++ b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/utils/TemporalJoinUtil.scala
@@ -33,7 +33,6 @@ import org.apache.calcite.sql.{SqlFunction, SqlFunctionCategory, SqlKind}
 import org.apache.calcite.sql.fun.SqlStdOperatorTable
 import org.apache.calcite.util.mapping.IntPair
 
-import scala.collection.JavaConversions._
 import scala.collection.JavaConverters._
 import scala.collection.mutable
 
@@ -147,15 +146,15 @@ object TemporalJoinUtil {
   private def makePrimaryKeyCall(
       rexBuilder: RexBuilder,
       rightPrimaryKeyExpression: Seq[RexNode]): RexNode = {
-    rexBuilder.makeCall(TEMPORAL_JOIN_CONDITION_PRIMARY_KEY, rightPrimaryKeyExpression)
+    rexBuilder.makeCall(TEMPORAL_JOIN_CONDITION_PRIMARY_KEY, rightPrimaryKeyExpression.asJava)
   }
 
   private def makeLeftJoinKeyCall(rexBuilder: RexBuilder, keyExpression: Seq[RexNode]): RexNode = {
-    rexBuilder.makeCall(TEMPORAL_JOIN_LEFT_KEY, keyExpression)
+    rexBuilder.makeCall(TEMPORAL_JOIN_LEFT_KEY, keyExpression.asJava)
   }
 
   private def makeRightJoinKeyCall(rexBuilder: RexBuilder, keyExpression: Seq[RexNode]): RexNode = {
-    rexBuilder.makeCall(TEMPORAL_JOIN_RIGHT_KEY, keyExpression)
+    rexBuilder.makeCall(TEMPORAL_JOIN_RIGHT_KEY, keyExpression.asJava)
   }
 
   def makeProcTimeTemporalFunctionJoinConCall(
@@ -242,12 +241,12 @@ object TemporalJoinUtil {
 
   def isInitialRowTimeTemporalTableJoin(rexCall: RexCall): Boolean = {
     // (LEFT_TIME_ATTRIBUTE, RIGHT_TIME_ATTRIBUTE, LEFT_KEY, RIGHT_KEY)
-    rexCall.getOperator == INITIAL_TEMPORAL_JOIN_CONDITION && rexCall.operands.length == 4
+    rexCall.getOperator == INITIAL_TEMPORAL_JOIN_CONDITION && rexCall.operands.size() == 4
   }
 
   def isInitialProcTimeTemporalTableJoin(rexCall: RexCall): Boolean = {
     // (LEFT_TIME_ATTRIBUTE, LEFT_KEY, RIGHT_KEY)
-    rexCall.getOperator == INITIAL_TEMPORAL_JOIN_CONDITION && rexCall.operands.length == 3
+    rexCall.getOperator == INITIAL_TEMPORAL_JOIN_CONDITION && rexCall.operands.size() == 3
   }
 
   private def containsTemporalJoinCondition(condition: RexNode): Boolean = {
@@ -305,7 +304,7 @@ object TemporalJoinUtil {
 
   def isRowTimeTemporalFunctionJoinCon(rexCall: RexCall): Boolean = {
     // (LEFT_TIME_ATTRIBUTE, RIGHT_TIME_ATTRIBUTE, PRIMARY_KEY)
-    rexCall.getOperator == TEMPORAL_JOIN_CONDITION && rexCall.operands.length == 3
+    rexCall.getOperator == TEMPORAL_JOIN_CONDITION && rexCall.operands.size() == 3
   }
 
   def isTemporalFunctionJoin(rexBuilder: RexBuilder, joinInfo: JoinInfo): Boolean = {
@@ -328,7 +327,7 @@ object TemporalJoinUtil {
     // (LEFT_TIME_ATTRIBUTE, PRIMARY_KEY)
     // (LEFT_TIME_ATTRIBUTE, RIGHT_TIME_ATTRIBUTE, PRIMARY_KEY)
     rexCall.getOperator == TEMPORAL_JOIN_CONDITION &&
-    (rexCall.operands.length == 2 || rexCall.operands.length == 3)
+    (rexCall.operands.size() == 2 || rexCall.operands.size() == 3)
   }
 
   def validateTemporalFunctionCondition(