You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by tw...@apache.org on 2022/10/21 07:48:02 UTC
[flink] 02/02: [FLINK-20873][table] Upgrade Calcite to version 1.27
This is an automated email from the ASF dual-hosted git repository.
twalthr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git
commit 600dfc77a301601f997fdf2c4f479a41b5e0ef2f
Author: Sergey Nuyanzin <sn...@gmail.com>
AuthorDate: Fri Oct 7 15:42:33 2022 +0200
[FLINK-20873][table] Upgrade Calcite to version 1.27
This closes #20850.
---
.../delegation/hive/HiveParserCalcitePlanner.java | 1 +
flink-table/flink-sql-client/pom.xml | 6 +
flink-table/flink-sql-parser-hive/pom.xml | 10 +
.../parser/hive/FlinkHiveSqlParserImplTest.java | 24 +
flink-table/flink-sql-parser/pom.xml | 22 +-
.../sql/parser/validate/FlinkSqlConformance.java | 7 +
.../flink/sql/parser/FlinkSqlParserImplTest.java | 41 +-
flink-table/flink-table-planner/pom.xml | 50 +-
.../org/apache/calcite/rel/core/Correlate.java | 35 +-
.../java/org/apache/calcite/rel/core/Filter.java | 29 +-
.../org/apache/calcite/rel/core/Intersect.java | 9 +-
.../java/org/apache/calcite/rel/core/Minus.java | 2 +-
.../java/org/apache/calcite/rel/core/SetOp.java | 11 +-
.../java/org/apache/calcite/rel/core/Sort.java | 97 +-
.../java/org/apache/calcite/rel/core/Values.java | 33 +-
.../java/org/apache/calcite/rel/core/Window.java | 45 +-
.../calcite/rel/logical/LogicalCorrelate.java | 7 +-
.../apache/calcite/rel/logical/LogicalFilter.java | 36 +-
.../apache/calcite/rel/logical/LogicalSort.java | 14 +-
.../apache/calcite/rel/logical/LogicalUnion.java | 1 +
.../apache/calcite/rel/logical/LogicalValues.java | 35 +-
.../apache/calcite/rel/logical/LogicalWindow.java | 21 +-
.../java/org/apache/calcite/rex/RexLiteral.java | 1179 ----------------
.../java/org/apache/calcite/rex/RexSimplify.java | 637 ++++++---
.../org/apache/calcite/sql/SqlMatchRecognize.java | 428 ------
.../java/org/apache/calcite/sql/SqlTableRef.java | 80 --
.../calcite/sql/validate/SqlValidatorImpl.java | 1338 ++++++++++++------
.../apache/calcite/sql2rel/RelDecorrelator.java | 373 +++--
.../apache/calcite/sql2rel/SqlToRelConverter.java | 1452 ++++++++++++--------
.../table/planner/calcite/FlinkRelBuilder.java | 6 +-
.../table/planner/calcite/FlinkRexBuilder.java | 4 +-
.../table/planner/delegation/PlannerContext.java | 6 +-
.../functions/sql/SqlJsonObjectFunction.java | 158 ---
.../sql/SqlJsonObjectFunctionWrapper.java | 1 +
...FlinkAggregateExpandDistinctAggregatesRule.java | 6 +
.../plan/rules/logical/SubQueryDecorrelator.java | 5 +-
.../src/main/resources/META-INF/NOTICE | 10 +-
.../planner/calcite/FlinkLogicalRelFactories.scala | 2 +-
.../logical/LogicalWindowAggregateRuleBase.scala | 1 +
.../planner/plan/schema/FlinkTableFunction.scala | 7 +-
.../table/planner/catalog/JavaCatalogTableTest.xml | 31 +-
.../table/planner/plan/batch/sql/CalcTest.xml | 8 +-
.../plan/batch/sql/LegacyTableSourceTest.xml | 2 +-
.../batch/sql/join/NestedLoopSemiAntiJoinTest.xml | 8 +-
.../plan/batch/sql/join/SemiAntiJoinTest.xml | 8 +-
.../planner/plan/batch/table/SetOperatorsTest.xml | 4 +-
.../rules/logical/ConvertToNotInOrInRuleTest.xml | 2 +-
.../PushFilterInCalcIntoTableSourceRuleTest.xml | 4 +-
...PushFilterIntoLegacyTableSourceScanRuleTest.xml | 4 +-
.../PushFilterIntoTableSourceScanRuleTest.xml | 4 +-
.../logical/SimplifyFilterConditionRuleTest.xml | 8 +-
.../subquery/FlinkRewriteSubQueryRuleTest.xml | 2 +-
.../logical/subquery/SubQueryAntiJoinTest.xml | 8 +-
.../logical/subquery/SubQuerySemiJoinTest.xml | 8 +-
.../table/planner/plan/stream/sql/CalcTest.xml | 8 +-
.../plan/stream/sql/LegacyTableSourceTest.xml | 2 +-
.../planner/plan/stream/sql/join/JoinTest.xml | 6 +-
.../plan/stream/sql/join/SemiAntiJoinTest.xml | 12 +-
.../table/planner/plan/stream/table/CalcTest.xml | 4 +-
.../table/planner/plan/stream/table/JoinTest.xml | 4 +-
.../plan/metadata/FlinkRelMdHandlerTestBase.scala | 1 +
.../planner/plan/utils/FlinkRexUtilTest.scala | 13 +-
flink-table/pom.xml | 2 +-
63 files changed, 2981 insertions(+), 3401 deletions(-)
diff --git a/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/planner/delegation/hive/HiveParserCalcitePlanner.java b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/planner/delegation/hive/HiveParserCalcitePlanner.java
index 992d5af27d7..274813eaa21 100644
--- a/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/planner/delegation/hive/HiveParserCalcitePlanner.java
+++ b/flink-connectors/flink-connector-hive/src/main/java/org/apache/flink/table/planner/delegation/hive/HiveParserCalcitePlanner.java
@@ -1201,6 +1201,7 @@ public class HiveParserCalcitePlanner {
false,
gbKeyIndices,
-1,
+ null,
RelCollations.EMPTY,
groupSet.cardinality(),
gbInputRel,
diff --git a/flink-table/flink-sql-client/pom.xml b/flink-table/flink-sql-client/pom.xml
index 84a1577769b..6145e3950d3 100644
--- a/flink-table/flink-sql-client/pom.xml
+++ b/flink-table/flink-sql-client/pom.xml
@@ -164,6 +164,12 @@ under the License.
<groupId>org.apache.flink</groupId>
<artifactId>flink-sql-parser</artifactId>
<version>${project.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.calcite</groupId>
+ <artifactId>calcite-core</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<!-- test dependencies for HiveCatalog. The exclusions should be in sync with those flink-connector-hive -->
diff --git a/flink-table/flink-sql-parser-hive/pom.xml b/flink-table/flink-sql-parser-hive/pom.xml
index b2082468c1d..85ee6cb01b2 100644
--- a/flink-table/flink-sql-parser-hive/pom.xml
+++ b/flink-table/flink-sql-parser-hive/pom.xml
@@ -56,6 +56,12 @@ under the License.
<artifactId>guava</artifactId>
<version>${guava.version}</version>
<scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.checkerframework</groupId>
+ <artifactId>checker-qual</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.apache.calcite</groupId>
@@ -87,6 +93,10 @@ under the License.
<groupId>org.apiguardian</groupId>
<artifactId>apiguardian-api</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>com.google.errorprone</groupId>
+ <artifactId>error_prone_annotations</artifactId>
+ </exclusion>
</exclusions>
<scope>test</scope>
<type>test-jar</type>
diff --git a/flink-table/flink-sql-parser-hive/src/test/java/org/apache/flink/sql/parser/hive/FlinkHiveSqlParserImplTest.java b/flink-table/flink-sql-parser-hive/src/test/java/org/apache/flink/sql/parser/hive/FlinkHiveSqlParserImplTest.java
index 6ff6a16cc2a..1ca19985dd7 100644
--- a/flink-table/flink-sql-parser-hive/src/test/java/org/apache/flink/sql/parser/hive/FlinkHiveSqlParserImplTest.java
+++ b/flink-table/flink-sql-parser-hive/src/test/java/org/apache/flink/sql/parser/hive/FlinkHiveSqlParserImplTest.java
@@ -34,6 +34,8 @@ class FlinkHiveSqlParserImplTest extends SqlParserTest {
}
// ignore test methods that we don't support
+ // BEGIN
+ // DESCRIBE STATEMENT
@Disabled
@Test
void testDescribeStatement() {}
@@ -42,10 +44,32 @@ class FlinkHiveSqlParserImplTest extends SqlParserTest {
@Test
void testTableHintsInInsert() {}
+ // ARRAY_AGG
+ @Disabled
+ @Test
+ void testArrayAgg() {}
+
+ // DESCRIBE SCHEMA
@Disabled
@Test
void testDescribeSchema() {}
+ // EXPLAIN AS DOT
+ @Disabled
+ @Test
+ void testExplainAsDot() {}
+
+ // GROUP CONCAT
+ @Disabled
+ @Test
+ void testGroupConcat() {}
+
+ // STRING_AGG
+ @Disabled
+ @Test
+ void testStringAgg() {}
+ // END
+
@Test
void testShowDatabases() {
sql("show databases").ok("SHOW DATABASES");
diff --git a/flink-table/flink-sql-parser/pom.xml b/flink-table/flink-sql-parser/pom.xml
index bf1faef7a4a..5b1f80c6976 100644
--- a/flink-table/flink-sql-parser/pom.xml
+++ b/flink-table/flink-sql-parser/pom.xml
@@ -48,6 +48,12 @@ under the License.
<artifactId>guava</artifactId>
<version>${guava.version}</version>
<scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.checkerframework</groupId>
+ <artifactId>checker-qual</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.apache.calcite</groupId>
@@ -56,11 +62,11 @@ under the License.
<version>${calcite.version}</version>
<exclusions>
<!--
- "mvn dependency:tree" as of Calcite 1.26.0:
+ "mvn dependency:tree" as of Calcite 1.27.0:
- [INFO] +- org.apache.calcite:calcite-core:jar:1.26.0:compile
- [INFO] | +- org.apache.calcite.avatica:avatica-core:jar:1.17.0:compile
- [INFO] | +- org.apiguardian:apiguardian-api:jar:1.1.0:compile
+ [INFO] +- org.apache.calcite:calcite-core:jar:1.27.0:compile
+ [INFO] | +- org.apache.calcite.avatica:avatica-core:jar:1.18.0:compile
+ [INFO] | \- org.checkerframework:checker-qual:jar:3.10.0:compile
Dependencies that are not needed for how we use Calcite right now.
-->
@@ -161,6 +167,10 @@ under the License.
<groupId>org.apiguardian</groupId>
<artifactId>apiguardian-api</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>com.google.errorprone</groupId>
+ <artifactId>error_prone_annotations</artifactId>
+ </exclusion>
</exclusions>
</dependency>
<dependency>
@@ -193,6 +203,10 @@ under the License.
<groupId>org.apiguardian</groupId>
<artifactId>apiguardian-api</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>com.google.errorprone</groupId>
+ <artifactId>error_prone_annotations</artifactId>
+ </exclusion>
</exclusions>
<scope>test</scope>
<type>test-jar</type>
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 88ba81531ff..f2c449d2627 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
@@ -18,7 +18,9 @@
package org.apache.flink.sql.parser.validate;
+import org.apache.calcite.sql.fun.SqlLibrary;
import org.apache.calcite.sql.validate.SqlConformance;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
/** Sql conformance used for flink to set specific sql dialect parser. * */
public enum FlinkSqlConformance implements SqlConformance {
@@ -176,4 +178,9 @@ public enum FlinkSqlConformance implements SqlConformance {
public boolean allowQualifyingCommonColumn() {
return true;
}
+
+ @Override
+ public SqlLibrary semantics() {
+ return SqlConformanceEnum.DEFAULT.semantics();
+ }
}
diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
index 27d57acc88f..741afa6d010 100644
--- a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
+++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
@@ -64,14 +64,39 @@ class FlinkSqlParserImplTest extends SqlParserTest {
sql("desc catalog a").ok("DESCRIBE CATALOG `A`");
}
- /**
- * Here we override the super method to avoid test error from `describe schema` supported in
- * original calcite.
- */
+ // ignore test methods that we don't support
+ // BEGIN
+ // ARRAY_AGG
+ @Disabled
+ @Test
+ void testArrayAgg() {}
+
+ // DESCRIBE SCHEMA
@Disabled
@Test
void testDescribeSchema() {}
+ // DESCRIBE STATEMENT
+ @Disabled
+ @Test
+ void testDescribeStatement() {}
+
+ // GROUP CONCAT
+ @Disabled
+ @Test
+ void testGroupConcat() {}
+
+ // EXPLAIN AS DOT
+ @Disabled
+ @Test
+ void testExplainAsDot() {}
+
+ // STRING_AGG
+ @Disabled
+ @Test
+ void testStringAgg() {}
+ // END
+
@Test
void testUseCatalog() {
sql("use catalog a").ok("USE CATALOG `A`");
@@ -286,14 +311,6 @@ class FlinkSqlParserImplTest extends SqlParserTest {
.ok("SHOW COLUMNS IN `CATALOG1`.`DB1`.`TBL` NOT LIKE '%'");
}
- /**
- * Here we override the super method to avoid test error from `describe statement` supported in
- * original calcite.
- */
- @Disabled
- @Test
- void testDescribeStatement() {}
-
@Test
void testAlterTable() {
sql("alter table t1 rename to t2").ok("ALTER TABLE `T1` RENAME TO `T2`");
diff --git a/flink-table/flink-table-planner/pom.xml b/flink-table/flink-table-planner/pom.xml
index 7bc30283358..e1fd5ac359d 100644
--- a/flink-table/flink-table-planner/pom.xml
+++ b/flink-table/flink-table-planner/pom.xml
@@ -42,6 +42,12 @@ under the License.
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.checkerframework</groupId>
+ <artifactId>checker-qual</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
@@ -81,6 +87,17 @@ under the License.
</exclusion>
</exclusions>
</dependency>
+ <!-- Use newer version v1.1.2 as in Calcite because org.junit.jupiter:junit-jupiter contains it -->
+ <dependency>
+ <artifactId>apiguardian-api</artifactId>
+ <groupId>org.apiguardian</groupId>
+ <version>1.1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.checkerframework</groupId>
+ <artifactId>checker-qual</artifactId>
+ <version>3.10.0</version>
+ </dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-sql-parser-hive</artifactId>
@@ -111,22 +128,22 @@ under the License.
<version>${calcite.version}</version>
<exclusions>
<!--
- "mvn dependency:tree" as of Calcite 1.26.0:
-
- [INFO] +- org.apache.calcite:calcite-core:jar:1.26.0:compile
- [INFO] | +- org.apache.calcite:calcite-linq4j:jar:1.26.0:compile
- [INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.12.1:compile
- [INFO] | +- org.apiguardian:apiguardian-api:jar:1.1.0:compile
- [INFO] | +- com.esri.geometry:esri-geometry-api:jar:2.2.0:runtime
- [INFO] | +- com.fasterxml.jackson.core:jackson-core:jar:2.12.1:runtime
- [INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.12.1:runtime
+ "mvn dependency:tree" as of Calcite 1.27.0:
+
+ [INFO] +- org.apache.calcite:calcite-core:jar:1.27.0:compile
+ [INFO] | +- org.apache.calcite:calcite-linq4j:jar:1.27.0:compile
+ [INFO] | +- com.esri.geometry:esri-geometry-api:jar:2.2.0:compile
+ [INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.13.2:compile
+ [INFO] | +- org.apache.calcite.avatica:avatica-core:jar:1.18.0:compile
+ [INFO] | +- com.fasterxml.jackson.core:jackson-core:jar:2.13.2:compile
+ [INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.13.2.2:compile
[INFO] | +- com.jayway.jsonpath:json-path:jar:2.4.0:runtime
[INFO] | | \- net.minidev:json-smart:jar:2.3:runtime
[INFO] | | \- net.minidev:accessors-smart:jar:1.2:runtime
[INFO] | | \- org.ow2.asm:asm:jar:5.0.4:runtime
- [INFO] | +- commons-codec:commons-codec:jar:1.10:runtime
- [INFO] | +- org.apache.commons:commons-lang3:jar:3.3.2:compile
- [INFO] | \- commons-io:commons-io:jar:2.4:compile
+ [INFO] | +- commons-codec:commons-codec:jar:1.15:runtime
+ [INFO] | \- commons-io:commons-io:jar:2.11.0:compile
+
Dependencies that are not needed for how we use Calcite right now.
-->
@@ -179,6 +196,10 @@ under the License.
<groupId>org.apiguardian</groupId>
<artifactId>apiguardian-api</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>com.google.errorprone</groupId>
+ <artifactId>error_prone_annotations</artifactId>
+ </exclusion>
</exclusions>
</dependency>
@@ -347,6 +368,7 @@ under the License.
<include>com.google.guava:failureaccess</include>
<include>commons-codec:commons-codec</include>
<include>commons-io:commons-io</include>
+ <include>org.checkerframework:checker-qual</include>
<!-- flink-table-planner dependencies -->
<include>org.apache.flink:flink-sql-parser</include>
@@ -373,6 +395,10 @@ under the License.
<pattern>org.apache.commons.io</pattern>
<shadedPattern>org.apache.flink.calcite.shaded.org.apache.commons.io</shadedPattern>
</relocation>
+ <relocation>
+ <pattern>org.checkerframework</pattern>
+ <shadedPattern>org.apache.flink.calcite.shaded.org.checkerframework</shadedPattern>
+ </relocation>
<!-- These are relocated to match package names, but not included in this jar -->
<relocation>
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Correlate.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Correlate.java
index 8df4da8b823..62d507eb125 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Correlate.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Correlate.java
@@ -18,6 +18,8 @@
package org.apache.calcite.rel.core;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
@@ -34,12 +36,14 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.Litmus;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collections;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
+import static java.util.Objects.requireNonNull;
+
/**
* A relational operator that performs nested-loop joins.
*
@@ -75,7 +79,7 @@ public abstract class Correlate extends BiRel implements Hintable {
protected final CorrelationId correlationId;
protected final ImmutableBitSet requiredColumns;
protected final JoinRelType joinType;
- protected final com.google.common.collect.ImmutableList<RelHint> hints;
+ protected final ImmutableList<RelHint> hints;
// ~ Constructors -----------------------------------------------------------
@@ -101,10 +105,10 @@ public abstract class Correlate extends BiRel implements Hintable {
JoinRelType joinType) {
super(cluster, traitSet, left, right);
assert !joinType.generatesNullsOnLeft() : "Correlate has invalid join type " + joinType;
- this.joinType = Objects.requireNonNull(joinType);
- this.correlationId = Objects.requireNonNull(correlationId);
- this.requiredColumns = Objects.requireNonNull(requiredColumns);
- this.hints = com.google.common.collect.ImmutableList.copyOf(hints);
+ this.joinType = requireNonNull(joinType, "joinType");
+ this.correlationId = requireNonNull(correlationId, "correlationId");
+ this.requiredColumns = requireNonNull(requiredColumns, "requiredColumns");
+ this.hints = ImmutableList.copyOf(hints);
}
/**
@@ -141,15 +145,16 @@ public abstract class Correlate extends BiRel implements Hintable {
*
* @param input Input representation
*/
- public Correlate(RelInput input) {
+ protected Correlate(RelInput input) {
this(
input.getCluster(),
input.getTraitSet(),
input.getInputs().get(0),
input.getInputs().get(1),
- new CorrelationId((Integer) input.get("correlation")),
+ new CorrelationId(
+ requireNonNull((Integer) input.get("correlation"), "correlation")),
input.getBitSet("requiredColumns"),
- input.getEnum("joinType", JoinRelType.class));
+ requireNonNull(input.getEnum("joinType", JoinRelType.class), "joinType"));
}
// ~ Methods ----------------------------------------------------------------
@@ -190,7 +195,7 @@ public abstract class Correlate extends BiRel implements Hintable {
joinType,
getCluster().getTypeFactory(),
null,
- com.google.common.collect.ImmutableList.of());
+ ImmutableList.of());
case ANTI:
case SEMI:
return left.getRowType();
@@ -232,7 +237,7 @@ public abstract class Correlate extends BiRel implements Hintable {
@Override
public Set<CorrelationId> getVariablesSet() {
- return com.google.common.collect.ImmutableSet.of(correlationId);
+ return ImmutableSet.of(correlationId);
}
@Override
@@ -248,7 +253,7 @@ public abstract class Correlate extends BiRel implements Hintable {
}
@Override
- public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+ public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
double rowCount = mq.getRowCount(this);
final double rightRowCount = right.estimateRowCount(mq);
@@ -258,9 +263,15 @@ public abstract class Correlate extends BiRel implements Hintable {
}
Double restartCount = mq.getRowCount(getLeft());
+ if (restartCount == null) {
+ return planner.getCostFactory().makeInfiniteCost();
+ }
// RelMetadataQuery.getCumulativeCost(getRight()); does not work for
// RelSubset, so we ask planner to cost-estimate right relation
RelOptCost rightCost = planner.getCost(getRight(), mq);
+ if (rightCost == null) {
+ return planner.getCostFactory().makeInfiniteCost();
+ }
RelOptCost rescanCost = rightCost.multiplyBy(Math.max(1.0, restartCount - 1));
return planner.getCostFactory()
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Filter.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Filter.java
index 34a28def16b..b28ca203207 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Filter.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Filter.java
@@ -18,7 +18,6 @@
package org.apache.calcite.rel.core;
-import org.apache.calcite.config.CalciteSystemProperty;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
@@ -38,10 +37,15 @@ import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.util.Litmus;
+import org.apiguardian.api.API;
+import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.Objects;
+import static java.util.Objects.requireNonNull;
+
/**
* Relational expression that iterates over its input and returns elements for which <code>condition
* </code> evaluates to <code>true</code>.
@@ -71,6 +75,7 @@ public abstract class Filter extends SingleRel implements Hintable {
* @param child input relational expression
* @param condition boolean expression which determines whether a row is allowed to pass
*/
+ @SuppressWarnings("method.invocation.invalid")
protected Filter(
RelOptCluster cluster,
RelTraitSet traits,
@@ -78,11 +83,10 @@ public abstract class Filter extends SingleRel implements Hintable {
RelNode child,
RexNode condition) {
super(cluster, traits, child);
- assert condition != null;
- assert RexUtil.isFlat(condition) : condition;
- this.condition = condition;
- // Too expensive for everyday use:
- assert !CalciteSystemProperty.DEBUG.value() || isValid(Litmus.THROW, null);
+ this.condition = requireNonNull(condition, "condition");
+ assert RexUtil.isFlat(condition)
+ : "RexUtil.isFlat should be true for condition " + condition;
+ assert isValid(Litmus.THROW, null);
this.hints = com.google.common.collect.ImmutableList.copyOf(hints);
}
@@ -104,7 +108,7 @@ public abstract class Filter extends SingleRel implements Hintable {
input.getCluster(),
input.getTraitSet(),
input.getInput(),
- input.getExpression("condition"));
+ requireNonNull(input.getExpression("condition"), "condition"));
}
// ~ Methods ----------------------------------------------------------------
@@ -116,6 +120,7 @@ public abstract class Filter extends SingleRel implements Hintable {
public abstract Filter copy(RelTraitSet traitSet, RelNode input, RexNode condition);
+ @Override
public RelNode accept(RexShuttle shuttle) {
RexNode condition = shuttle.apply(this.condition);
if (this.condition == condition) {
@@ -134,7 +139,7 @@ public abstract class Filter extends SingleRel implements Hintable {
}
@Override
- public boolean isValid(Litmus litmus, Context context) {
+ public boolean isValid(Litmus litmus, @Nullable Context context) {
if (RexUtil.isNullabilityCast(getCluster().getTypeFactory(), condition)) {
return litmus.fail("Cast for just nullability not allowed");
}
@@ -147,7 +152,7 @@ public abstract class Filter extends SingleRel implements Hintable {
}
@Override
- public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+ public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
double dRows = mq.getRowCount(this);
double dCpu = mq.getRowCount(getInput());
double dIo = 0;
@@ -171,11 +176,14 @@ public abstract class Filter extends SingleRel implements Hintable {
return RelMdUtil.estimateFilteredRows(child, condition, mq);
}
+ @Override
public RelWriter explainTerms(RelWriter pw) {
return super.explainTerms(pw).item("condition", condition);
}
- protected boolean deepEquals0(Object obj) {
+ @API(since = "1.24", status = API.Status.INTERNAL)
+ @EnsuresNonNullIf(expression = "#1", result = true)
+ protected boolean deepEquals0(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -190,6 +198,7 @@ public abstract class Filter extends SingleRel implements Hintable {
&& getRowType().equalsSansFieldNames(o.getRowType());
}
+ @API(since = "1.24", status = API.Status.INTERNAL)
protected int deepHashCode0() {
return Objects.hash(traitSet, hints, input.deepHashCode(), condition);
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Intersect.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Intersect.java
index ad20e66fe37..9029903f50a 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Intersect.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Intersect.java
@@ -40,7 +40,7 @@ import java.util.List;
*/
public abstract class Intersect extends SetOp {
/** Creates an Intersect. */
- public Intersect(
+ protected Intersect(
RelOptCluster cluster,
RelTraitSet traits,
List<RelHint> hints,
@@ -64,7 +64,12 @@ public abstract class Intersect extends SetOp {
// REVIEW jvs 30-May-2005: I just pulled this out of a hat.
double dRows = Double.MAX_VALUE;
for (RelNode input : inputs) {
- dRows = Math.min(dRows, mq.getRowCount(input));
+ Double rowCount = mq.getRowCount(input);
+ if (rowCount == null) {
+ // Assume this input does not reduce row count
+ continue;
+ }
+ dRows = Math.min(dRows, rowCount);
}
dRows *= 0.25;
return dRows;
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Minus.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Minus.java
index ae3cb8c6d23..15196253055 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Minus.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Minus.java
@@ -44,7 +44,7 @@ import java.util.List;
*/
public abstract class Minus extends SetOp {
- public Minus(
+ protected Minus(
RelOptCluster cluster,
RelTraitSet traits,
List<RelHint> hints,
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/SetOp.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/SetOp.java
index 710fcaed874..93fe1131168 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/SetOp.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/SetOp.java
@@ -18,6 +18,7 @@
package org.apache.calcite.rel.core;
+import com.google.common.collect.ImmutableList;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptUtil;
@@ -46,10 +47,10 @@ import java.util.List;
public abstract class SetOp extends AbstractRelNode implements Hintable {
// ~ Instance fields --------------------------------------------------------
- protected com.google.common.collect.ImmutableList<RelNode> inputs;
+ protected ImmutableList<RelNode> inputs;
public final SqlKind kind;
public final boolean all;
- protected final com.google.common.collect.ImmutableList<RelHint> hints;
+ protected final ImmutableList<RelHint> hints;
// ~ Constructors -----------------------------------------------------------
@@ -65,9 +66,9 @@ public abstract class SetOp extends AbstractRelNode implements Hintable {
com.google.common.base.Preconditions.checkArgument(
kind == SqlKind.UNION || kind == SqlKind.INTERSECT || kind == SqlKind.EXCEPT);
this.kind = kind;
- this.inputs = com.google.common.collect.ImmutableList.copyOf(inputs);
+ this.inputs = ImmutableList.copyOf(inputs);
this.all = all;
- this.hints = com.google.common.collect.ImmutableList.copyOf(hints);
+ this.hints = ImmutableList.copyOf(hints);
}
/** Creates a SetOp. */
@@ -104,7 +105,7 @@ public abstract class SetOp extends AbstractRelNode implements Hintable {
public void replaceInput(int ordinalInParent, RelNode p) {
final List<RelNode> newInputs = new ArrayList<>(inputs);
newInputs.set(ordinalInParent, p);
- inputs = com.google.common.collect.ImmutableList.copyOf(newInputs);
+ inputs = ImmutableList.copyOf(newInputs);
recomputeDigest();
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Sort.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Sort.java
index 93c38fbdd44..f8408b91f94 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Sort.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Sort.java
@@ -18,6 +18,7 @@
package org.apache.calcite.rel.core;
+import com.google.common.collect.ImmutableList;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
@@ -33,9 +34,11 @@ import org.apache.calcite.rel.SingleRel;
import org.apache.calcite.rel.hint.Hintable;
import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.util.Util;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collections;
import java.util.List;
@@ -52,9 +55,9 @@ public abstract class Sort extends SingleRel implements Hintable {
// ~ Instance fields --------------------------------------------------------
public final RelCollation collation;
- public final RexNode offset;
- public final RexNode fetch;
- protected final com.google.common.collect.ImmutableList<RelHint> hints;
+ public final @Nullable RexNode offset;
+ public final @Nullable RexNode fetch;
+ protected final ImmutableList<RelHint> hints;
// ~ Constructors -----------------------------------------------------------
@@ -66,7 +69,8 @@ public abstract class Sort extends SingleRel implements Hintable {
* @param child input relational expression
* @param collation array of sort specifications
*/
- public Sort(RelOptCluster cluster, RelTraitSet traits, RelNode child, RelCollation collation) {
+ protected Sort(
+ RelOptCluster cluster, RelTraitSet traits, RelNode child, RelCollation collation) {
this(cluster, traits, Collections.emptyList(), child, collation, null, null);
}
@@ -101,19 +105,19 @@ public abstract class Sort extends SingleRel implements Hintable {
* @param offset Expression for number of rows to discard before returning first row
* @param fetch Expression for number of rows to fetch
*/
- public Sort(
+ protected Sort(
RelOptCluster cluster,
RelTraitSet traits,
List<RelHint> hints,
RelNode child,
RelCollation collation,
- RexNode offset,
- RexNode fetch) {
+ @Nullable RexNode offset,
+ @Nullable RexNode fetch) {
super(cluster, traits, child);
this.collation = collation;
this.offset = offset;
this.fetch = fetch;
- this.hints = com.google.common.collect.ImmutableList.copyOf(hints);
+ this.hints = ImmutableList.copyOf(hints);
assert traits.containsIfApplicable(collation)
: "traits=" + traits + ", collation=" + collation;
@@ -122,7 +126,7 @@ public abstract class Sort extends SingleRel implements Hintable {
}
/** Creates a Sort by parsing serialized output. */
- public Sort(RelInput input) {
+ protected Sort(RelInput input) {
this(
input.getCluster(),
input.getTraitSet().plus(input.getCollation()),
@@ -147,19 +151,68 @@ public abstract class Sort extends SingleRel implements Hintable {
RelTraitSet traitSet,
RelNode newInput,
RelCollation newCollation,
- RexNode offset,
- RexNode fetch);
+ @Nullable RexNode offset,
+ @Nullable RexNode fetch);
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The CPU cost of a Sort has three main cases:
+ *
+ * <ul>
+ * <li>If {@code fetch} is zero, CPU cost is zero; otherwise,
+ * <li>if the sort keys are empty, we don't need to sort, only step over the rows, and
+ * therefore the CPU cost is {@code min(fetch + offset, inputRowCount) * bytesPerRow};
+ * otherwise
+ * <li>we need to read and sort {@code inputRowCount} rows, with at most {@code min(fetch +
+ * offset, inputRowCount)} of them in the sort data structure at a time, giving a CPU cost
+ * of {@code inputRowCount * log(min(fetch + offset, inputRowCount)) * bytesPerRow}.
+ * </ul>
+ *
+ * <p>The cost model factors in row width via {@code bytesPerRow}, because sorts need to move
+ * rows around, not just compare them; by making the cost higher if rows are wider, we
+ * discourage pushing a Project through a Sort. We assume that each field is 4 bytes, and we add
+ * 3 'virtual fields' to represent the per-row overhead. Thus a 1-field row is (3 + 1) * 4 = 16
+ * bytes; a 5-field row is (3 + 5) * 4 = 32 bytes.
+ *
+ * <p>The cost model does not consider a 5-field sort to be more expensive than, say, a 2-field
+ * sort, because both sorts will compare just one field most of the time.
+ */
@Override
- public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
- // Higher cost if rows are wider discourages pushing a project through a
- // sort.
- final double rowCount = mq.getRowCount(this);
- final double bytesPerRow = getRowType().getFieldCount() * 4;
- final double cpu = Util.nLogN(rowCount) * bytesPerRow;
- return planner.getCostFactory().makeCost(rowCount, cpu, 0);
+ public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+ final double offsetValue = Util.first(doubleValue(offset), 0d);
+ assert offsetValue >= 0 : "offset should not be negative:" + offsetValue;
+
+ final double inCount = mq.getRowCount(input);
+ @Nullable Double fetchValue = doubleValue(fetch);
+ final double readCount;
+ if (fetchValue == null) {
+ readCount = inCount;
+ } else if (fetchValue <= 0) {
+ // Case 1. Read zero rows from input, therefore CPU cost is zero.
+ return planner.getCostFactory().makeCost(inCount, 0, 0);
+ } else {
+ readCount = Math.min(inCount, offsetValue + fetchValue);
+ }
+
+ final double bytesPerRow = (3 + getRowType().getFieldCount()) * 4;
+
+ final double cpu;
+ if (collation.getFieldCollations().isEmpty()) {
+ // Case 2. If sort keys are empty, CPU cost is cheaper because we are just
+ // stepping over the first "readCount" rows, rather than sorting all
+ // "inCount" them. (Presumably we are applying FETCH and/or OFFSET,
+ // otherwise this Sort is a no-op.)
+ cpu = readCount * bytesPerRow;
+ } else {
+ // Case 3. Read and sort all "inCount" rows, keeping "readCount" in the
+ // sort data structure at a time.
+ cpu = Util.nLogM(inCount, readCount) * bytesPerRow;
+ }
+ return planner.getCostFactory().makeCost(readCount, cpu, 0);
}
+ @Override
public RelNode accept(RexShuttle shuttle) {
RexNode offset = shuttle.apply(this.offset);
RexNode fetch = shuttle.apply(this.fetch);
@@ -208,6 +261,7 @@ public abstract class Sort extends SingleRel implements Hintable {
input, Objects.requireNonNull(field).getFieldIndex()));
}
+ @Override
public RelWriter explainTerms(RelWriter pw) {
super.explainTerms(pw);
if (pw.nest()) {
@@ -226,7 +280,12 @@ public abstract class Sort extends SingleRel implements Hintable {
}
@Override
- public com.google.common.collect.ImmutableList<RelHint> getHints() {
+ public ImmutableList<RelHint> getHints() {
return hints;
}
+
+ /** Returns the double value of a node if it is a literal, otherwise null. */
+ private static @Nullable Double doubleValue(@Nullable RexNode r) {
+ return r instanceof RexLiteral ? ((RexLiteral) r).getValueAs(Double.class) : null;
+ }
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Values.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Values.java
index e4a6eba4785..8032241426d 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Values.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Values.java
@@ -18,6 +18,7 @@
package org.apache.calcite.rel.core;
+import com.google.common.collect.ImmutableList;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
@@ -36,6 +37,7 @@ import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.sql.SqlExplainLevel;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.util.Pair;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collections;
import java.util.List;
@@ -52,7 +54,7 @@ public abstract class Values extends AbstractRelNode implements Hintable {
public static final Predicate<? super Values> IS_EMPTY_J = Values::isEmpty;
- protected final com.google.common.collect.ImmutableList<RelHint> hints;
+ protected final ImmutableList<RelHint> hints;
@SuppressWarnings("Guava")
@Deprecated // to be removed before 2.0
@@ -65,9 +67,7 @@ public abstract class Values extends AbstractRelNode implements Hintable {
// ~ Instance fields --------------------------------------------------------
- public final com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tuples;
+ public final ImmutableList<ImmutableList<RexLiteral>> tuples;
// ~ Constructors -----------------------------------------------------------
@@ -87,14 +87,12 @@ public abstract class Values extends AbstractRelNode implements Hintable {
RelOptCluster cluster,
List<RelHint> hints,
RelDataType rowType,
- com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tuples,
+ ImmutableList<ImmutableList<RexLiteral>> tuples,
RelTraitSet traits) {
super(cluster, traits);
this.rowType = rowType;
this.tuples = tuples;
- this.hints = com.google.common.collect.ImmutableList.copyOf(hints);
+ this.hints = ImmutableList.copyOf(hints);
assert assertRowType();
}
@@ -120,7 +118,7 @@ public abstract class Values extends AbstractRelNode implements Hintable {
}
/** Creates a Values by parsing serialized output. */
- public Values(RelInput input) {
+ protected Values(RelInput input) {
this(
input.getCluster(),
input.getRowType("type"),
@@ -154,21 +152,18 @@ public abstract class Values extends AbstractRelNode implements Hintable {
return !isEmpty(values);
}
- public com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- getTuples(RelInput input) {
+ public ImmutableList<ImmutableList<RexLiteral>> getTuples(RelInput input) {
return input.getTuples("tuples");
}
/** Returns the rows of literals represented by this Values relational expression. */
- public com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- getTuples() {
+ public ImmutableList<ImmutableList<RexLiteral>> getTuples() {
return tuples;
}
/** Returns true if all tuples match rowType; otherwise, assert on mismatch. */
private boolean assertRowType() {
+ RelDataType rowType = getRowType();
for (List<RexLiteral> tuple : tuples) {
assert tuple.size() == rowType.getFieldCount();
for (Pair<RexLiteral, RelDataTypeField> pair :
@@ -190,11 +185,12 @@ public abstract class Values extends AbstractRelNode implements Hintable {
@Override
protected RelDataType deriveRowType() {
+ assert rowType != null : "rowType must not be null for " + this;
return rowType;
}
@Override
- public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+ public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
double dRows = mq.getRowCount(this);
// Assume CPU is negligible since values are precomputed.
@@ -204,15 +200,18 @@ public abstract class Values extends AbstractRelNode implements Hintable {
}
// implement RelNode
+ @Override
public double estimateRowCount(RelMetadataQuery mq) {
return tuples.size();
}
// implement RelNode
+ @Override
public RelWriter explainTerms(RelWriter pw) {
// A little adapter just to get the tuples to come out
// with curly brackets instead of square brackets. Plus
// more whitespace for readability.
+ RelDataType rowType = getRowType();
RelWriter relWriter =
super.explainTerms(pw)
// For rel digest, include the row type since a rendered
@@ -243,7 +242,7 @@ public abstract class Values extends AbstractRelNode implements Hintable {
}
@Override
- public com.google.common.collect.ImmutableList<RelHint> getHints() {
+ public ImmutableList<RelHint> getHints() {
return hints;
}
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Window.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Window.java
index 7784dd8364c..5b797d42186 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -18,6 +18,7 @@
package org.apache.calcite.rel.core;
+import com.google.common.collect.ImmutableList;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
@@ -46,6 +47,8 @@ import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.Util;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.checker.nullness.qual.RequiresNonNull;
import java.util.AbstractList;
import java.util.Collections;
@@ -68,9 +71,9 @@ import java.util.Objects;
* <p>Created by {@link org.apache.calcite.rel.rules.ProjectToWindowRule}.
*/
public abstract class Window extends SingleRel implements Hintable {
- public final com.google.common.collect.ImmutableList<Group> groups;
- public final com.google.common.collect.ImmutableList<RexLiteral> constants;
- protected final com.google.common.collect.ImmutableList<RelHint> hints;
+ public final ImmutableList<Group> groups;
+ public final ImmutableList<RexLiteral> constants;
+ protected final ImmutableList<RelHint> hints;
/**
* Creates a window relational expression.
@@ -83,7 +86,7 @@ public abstract class Window extends SingleRel implements Hintable {
* @param rowType Output row type
* @param groups Windows
*/
- public Window(
+ protected Window(
RelOptCluster cluster,
RelTraitSet traitSet,
List<RelHint> hints,
@@ -92,11 +95,11 @@ public abstract class Window extends SingleRel implements Hintable {
RelDataType rowType,
List<Group> groups) {
super(cluster, traitSet, input);
- this.constants = com.google.common.collect.ImmutableList.copyOf(constants);
+ this.constants = ImmutableList.copyOf(constants);
assert rowType != null;
this.rowType = rowType;
- this.groups = com.google.common.collect.ImmutableList.copyOf(groups);
- this.hints = com.google.common.collect.ImmutableList.copyOf(hints);
+ this.groups = ImmutableList.copyOf(groups);
+ this.hints = ImmutableList.copyOf(hints);
}
/**
@@ -159,6 +162,7 @@ public abstract class Window extends SingleRel implements Hintable {
return litmus.succeed();
}
+ @Override
public RelWriter explainTerms(RelWriter pw) {
super.explainTerms(pw);
for (Ord<Group> window : Ord.zip(groups)) {
@@ -170,10 +174,12 @@ public abstract class Window extends SingleRel implements Hintable {
public static ImmutableIntList getProjectOrdinals(final List<RexNode> exprs) {
return ImmutableIntList.copyOf(
new AbstractList<Integer>() {
+ @Override
public Integer get(int index) {
return ((RexSlot) exprs.get(index)).getIndex();
}
+ @Override
public int size() {
return exprs.size();
}
@@ -183,6 +189,7 @@ public abstract class Window extends SingleRel implements Hintable {
public static RelCollation getCollation(final List<RexFieldCollation> collations) {
return RelCollations.of(
new AbstractList<RelFieldCollation>() {
+ @Override
public RelFieldCollation get(int index) {
final RexFieldCollation collation = collations.get(index);
return new RelFieldCollation(
@@ -191,6 +198,7 @@ public abstract class Window extends SingleRel implements Hintable {
collation.getNullDirection());
}
+ @Override
public int size() {
return collations.size();
}
@@ -207,7 +215,7 @@ public abstract class Window extends SingleRel implements Hintable {
}
@Override
- public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+ public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
// Cost is proportional to the number of rows and the number of
// components (groups and aggregate functions). There is
// no I/O cost.
@@ -256,7 +264,7 @@ public abstract class Window extends SingleRel implements Hintable {
* List of {@link Window.RexWinAggCall} objects, each of which is a call to a {@link
* org.apache.calcite.sql.SqlAggFunction}.
*/
- public final com.google.common.collect.ImmutableList<RexWinAggCall> aggCalls;
+ public final ImmutableList<RexWinAggCall> aggCalls;
public Group(
ImmutableBitSet keys,
@@ -265,19 +273,21 @@ public abstract class Window extends SingleRel implements Hintable {
RexWindowBound upperBound,
RelCollation orderKeys,
List<RexWinAggCall> aggCalls) {
- this.keys = Objects.requireNonNull(keys);
+ this.keys = Objects.requireNonNull(keys, "keys");
this.isRows = isRows;
- this.lowerBound = Objects.requireNonNull(lowerBound);
- this.upperBound = Objects.requireNonNull(upperBound);
- this.orderKeys = Objects.requireNonNull(orderKeys);
- this.aggCalls = com.google.common.collect.ImmutableList.copyOf(aggCalls);
+ this.lowerBound = Objects.requireNonNull(lowerBound, "lowerBound");
+ this.upperBound = Objects.requireNonNull(upperBound, "upperBound");
+ this.orderKeys = Objects.requireNonNull(orderKeys, "orderKeys");
+ this.aggCalls = ImmutableList.copyOf(aggCalls);
this.digest = computeString();
}
+ @Override
public String toString() {
return digest;
}
+ @RequiresNonNull({"keys", "orderKeys", "lowerBound", "upperBound", "aggCalls"})
private String computeString() {
final StringBuilder buf = new StringBuilder("window(");
final int i = buf.length();
@@ -323,7 +333,7 @@ public abstract class Window extends SingleRel implements Hintable {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return this == obj || obj instanceof Group && this.digest.equals(((Group) obj).digest);
}
@@ -360,10 +370,12 @@ public abstract class Window extends SingleRel implements Hintable {
windowRel.getRowType().getFieldNames(),
windowRel.getInput().getRowType().getFieldCount());
return new AbstractList<AggregateCall>() {
+ @Override
public int size() {
return aggCalls.size();
}
+ @Override
public AggregateCall get(int index) {
final RexWinAggCall aggCall = aggCalls.get(index);
final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
@@ -374,6 +386,7 @@ public abstract class Window extends SingleRel implements Hintable {
aggCall.ignoreNulls,
getProjectOrdinals(aggCall.getOperands()),
-1,
+ null,
RelCollations.EMPTY,
aggCall.getType(),
fieldNames.get(aggCall.ordinal));
@@ -434,7 +447,7 @@ public abstract class Window extends SingleRel implements Hintable {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java
index 2c0703c5487..2147454271b 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalCorrelate.java
@@ -35,6 +35,8 @@ import org.apache.calcite.util.Litmus;
import java.util.Collections;
import java.util.List;
+import static java.util.Objects.requireNonNull;
+
/**
* A relational operator that performs nested-loop joins.
*
@@ -114,9 +116,10 @@ public final class LogicalCorrelate extends Correlate {
input.getTraitSet(),
input.getInputs().get(0),
input.getInputs().get(1),
- new CorrelationId((Integer) input.get("correlation")),
+ new CorrelationId(
+ (Integer) requireNonNull(input.get("correlation"), "correlation")),
input.getBitSet("requiredColumns"),
- input.getEnum("joinType", JoinRelType.class));
+ requireNonNull(input.getEnum("joinType", JoinRelType.class), "joinType"));
}
/** Creates a LogicalCorrelate. */
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
index 05852df6926..54eab93d6e4 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
@@ -18,6 +18,8 @@
package org.apache.calcite.rel.logical;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import org.apache.calcite.plan.Convention;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
@@ -34,7 +36,7 @@ import org.apache.calcite.rel.metadata.RelMdCollation;
import org.apache.calcite.rel.metadata.RelMdDistribution;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.util.Litmus;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.Objects;
@@ -72,23 +74,17 @@ public final class LogicalFilter extends Filter {
RexNode condition,
com.google.common.collect.ImmutableSet<CorrelationId> variablesSet) {
super(cluster, traitSet, hints, child, condition);
- this.variablesSet = Objects.requireNonNull(variablesSet);
- assert isValid(Litmus.THROW, null);
+ this.variablesSet = Objects.requireNonNull(variablesSet, "variablesSet");
}
+ @Deprecated // to be removed before 2.0
public LogicalFilter(
RelOptCluster cluster,
RelTraitSet traitSet,
RelNode child,
RexNode condition,
- com.google.common.collect.ImmutableSet<CorrelationId> variablesSet) {
- this(
- cluster,
- traitSet,
- com.google.common.collect.ImmutableList.of(),
- child,
- condition,
- variablesSet);
+ ImmutableSet<CorrelationId> variablesSet) {
+ this(cluster, traitSet, ImmutableList.of(), child, condition, variablesSet);
}
@Deprecated // to be removed before 2.0
@@ -99,30 +95,23 @@ public final class LogicalFilter extends Filter {
@Deprecated // to be removed before 2.0
public LogicalFilter(RelOptCluster cluster, RelNode child, RexNode condition) {
- this(
- cluster,
- cluster.traitSetOf(Convention.NONE),
- child,
- condition,
- com.google.common.collect.ImmutableSet.of());
+ this(cluster, cluster.traitSetOf(Convention.NONE), child, condition, ImmutableSet.of());
}
/** Creates a LogicalFilter by parsing serialized output. */
public LogicalFilter(RelInput input) {
super(input);
- this.variablesSet = com.google.common.collect.ImmutableSet.of();
+ this.variablesSet = ImmutableSet.of();
}
/** Creates a LogicalFilter. */
public static LogicalFilter create(final RelNode input, RexNode condition) {
- return create(input, condition, com.google.common.collect.ImmutableSet.of());
+ return create(input, condition, ImmutableSet.of());
}
/** Creates a LogicalFilter. */
public static LogicalFilter create(
- final RelNode input,
- RexNode condition,
- com.google.common.collect.ImmutableSet<CorrelationId> variablesSet) {
+ final RelNode input, RexNode condition, ImmutableSet<CorrelationId> variablesSet) {
final RelOptCluster cluster = input.getCluster();
final RelMetadataQuery mq = cluster.getMetadataQuery();
final RelTraitSet traitSet =
@@ -143,6 +132,7 @@ public final class LogicalFilter extends Filter {
return variablesSet;
}
+ @Override
public LogicalFilter copy(RelTraitSet traitSet, RelNode input, RexNode condition) {
assert traitSet.containsIfApplicable(Convention.NONE);
return new LogicalFilter(getCluster(), traitSet, hints, input, condition, variablesSet);
@@ -159,7 +149,7 @@ public final class LogicalFilter extends Filter {
}
@Override
- public boolean deepEquals(Object obj) {
+ public boolean deepEquals(@Nullable Object obj) {
return deepEquals0(obj) && variablesSet.equals(((LogicalFilter) obj).variablesSet);
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java
index e0eda26b83a..66e037b4523 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalSort.java
@@ -29,6 +29,7 @@ import org.apache.calcite.rel.RelShuttle;
import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rex.RexNode;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collections;
import java.util.List;
@@ -57,8 +58,8 @@ public final class LogicalSort extends Sort {
List<RelHint> hints,
RelNode input,
RelCollation collation,
- RexNode offset,
- RexNode fetch) {
+ @Nullable RexNode offset,
+ @Nullable RexNode fetch) {
super(cluster, traitSet, hints, input, collation, offset, fetch);
assert traitSet.containsIfApplicable(Convention.NONE);
}
@@ -77,7 +78,10 @@ public final class LogicalSort extends Sort {
* @param fetch Expression for number of rows to fetch
*/
public static LogicalSort create(
- RelNode input, RelCollation collation, RexNode offset, RexNode fetch) {
+ RelNode input,
+ RelCollation collation,
+ @Nullable RexNode offset,
+ @Nullable RexNode fetch) {
RelOptCluster cluster = input.getCluster();
collation = RelCollationTraitDef.INSTANCE.canonize(collation);
RelTraitSet traitSet = input.getTraitSet().replace(Convention.NONE).replace(collation);
@@ -91,8 +95,8 @@ public final class LogicalSort extends Sort {
RelTraitSet traitSet,
RelNode newInput,
RelCollation newCollation,
- RexNode offset,
- RexNode fetch) {
+ @Nullable RexNode offset,
+ @Nullable RexNode fetch) {
return new LogicalSort(
getCluster(), traitSet, hints, newInput, newCollation, offset, fetch);
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java
index 24de518359b..2e1992f6f5a 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalUnion.java
@@ -83,6 +83,7 @@ public final class LogicalUnion extends Union {
// ~ Methods ----------------------------------------------------------------
+ @Override
public LogicalUnion copy(RelTraitSet traitSet, List<RelNode> inputs, boolean all) {
assert traitSet.containsIfApplicable(Convention.NONE);
return new LogicalUnion(getCluster(), traitSet, hints, inputs, all);
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
index 7dd2c6b66c4..e76a9ecf192 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
@@ -18,6 +18,7 @@
package org.apache.calcite.rel.logical;
+import com.google.common.collect.ImmutableList;
import org.apache.calcite.plan.Convention;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
@@ -65,9 +66,7 @@ public class LogicalValues extends Values {
RelTraitSet traitSet,
List<RelHint> hints,
RelDataType rowType,
- com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tuples) {
+ ImmutableList<ImmutableList<RexLiteral>> tuples) {
super(cluster, hints, rowType, tuples, traitSet);
}
@@ -85,9 +84,7 @@ public class LogicalValues extends Values {
RelOptCluster cluster,
RelTraitSet traitSet,
RelDataType rowType,
- com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tuples) {
+ ImmutableList<ImmutableList<RexLiteral>> tuples) {
this(cluster, traitSet, Collections.emptyList(), rowType, tuples);
}
@@ -95,9 +92,7 @@ public class LogicalValues extends Values {
public LogicalValues(
RelOptCluster cluster,
RelDataType rowType,
- com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tuples) {
+ ImmutableList<ImmutableList<RexLiteral>> tuples) {
this(cluster, cluster.traitSetOf(Convention.NONE), rowType, tuples);
}
@@ -110,9 +105,7 @@ public class LogicalValues extends Values {
public static LogicalValues create(
RelOptCluster cluster,
final RelDataType rowType,
- final com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tuples) {
+ final ImmutableList<ImmutableList<RexLiteral>> tuples) {
final RelMetadataQuery mq = cluster.getMetadataQuery();
final RelTraitSet traitSet =
cluster.traitSetOf(Convention.NONE)
@@ -134,7 +127,7 @@ public class LogicalValues extends Values {
/** Creates a LogicalValues that outputs no rows of a given row type. */
public static LogicalValues createEmpty(RelOptCluster cluster, RelDataType rowType) {
- return create(cluster, rowType, com.google.common.collect.ImmutableList.of());
+ return create(cluster, rowType, ImmutableList.of());
}
/** Creates a LogicalValues that outputs one row and one column. */
@@ -145,15 +138,13 @@ public class LogicalValues extends Values {
.add("ZERO", SqlTypeName.INTEGER)
.nullable(false)
.build();
- final com.google.common.collect.ImmutableList<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tuples =
- com.google.common.collect.ImmutableList.of(
- com.google.common.collect.ImmutableList.of(
- cluster.getRexBuilder()
- .makeExactLiteral(
- BigDecimal.ZERO,
- rowType.getFieldList().get(0).getType())));
+ final ImmutableList<ImmutableList<RexLiteral>> tuples =
+ ImmutableList.of(
+ ImmutableList.of(
+ cluster.getRexBuilder()
+ .makeExactLiteral(
+ BigDecimal.ZERO,
+ rowType.getFieldList().get(0).getType())));
return create(cluster, rowType, tuples);
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
index 6c9cc5a1c5e..3208f4205d8 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
@@ -18,6 +18,9 @@
package org.apache.calcite.rel.logical;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptUtil;
@@ -41,6 +44,7 @@ import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.AbstractList;
import java.util.ArrayList;
@@ -133,8 +137,7 @@ public final class LogicalWindow extends Window {
final RelDataType outRowType = program.getOutputRowType();
// Build a list of distinct groups, partitions and aggregate
// functions.
- final com.google.common.collect.Multimap<WindowKey, RexOver> windowMap =
- com.google.common.collect.LinkedListMultimap.create();
+ final Multimap<WindowKey, RexOver> windowMap = LinkedListMultimap.create();
final int inputFieldCount = child.getRowType().getFieldCount();
@@ -163,7 +166,7 @@ public final class LogicalWindow extends Window {
// Build a list of groups, partitions, and aggregate functions. Each
// aggregate function will add its arguments as outputs of the input
// program.
- final Map<RexOver, RexOver> origToNewOver = new IdentityHashMap<>();
+ final IdentityHashMap<RexOver, RexOver> origToNewOver = new IdentityHashMap<>();
for (RexNode agg : program.getExprList()) {
if (agg instanceof RexOver) {
final RexOver origOver = (RexOver) agg;
@@ -243,6 +246,7 @@ public final class LogicalWindow extends Window {
// the output calc (if it exists).
RexShuttle shuttle =
new RexShuttle() {
+ @Override
public RexNode visitOver(RexOver over) {
// Look up the aggCall which this expr was translated to.
final Window.RexWinAggCall aggCall = aggMap.get(origToNewOver.get(over));
@@ -266,6 +270,7 @@ public final class LogicalWindow extends Window {
return new RexInputRef(index, over.getType());
}
+ @Override
public RexNode visitLocalRef(RexLocalRef localRef) {
final int index = localRef.getIndex();
if (index < inputFieldCount) {
@@ -298,10 +303,12 @@ public final class LogicalWindow extends Window {
private static List<RexNode> toInputRefs(final List<? extends RexNode> operands) {
return new AbstractList<RexNode>() {
+ @Override
public int size() {
return operands.size();
}
+ @Override
public RexNode get(int index) {
final RexNode operand = operands.get(index);
if (operand instanceof RexInputRef) {
@@ -345,7 +352,7 @@ public final class LogicalWindow extends Window {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj == this
|| obj instanceof WindowKey
&& groupSet.equals(((WindowKey) obj).groupSet)
@@ -357,15 +364,13 @@ public final class LogicalWindow extends Window {
}
private static void addWindows(
- com.google.common.collect.Multimap<WindowKey, RexOver> windowMap,
- RexOver over,
- final int inputFieldCount) {
+ Multimap<WindowKey, RexOver> windowMap, RexOver over, final int inputFieldCount) {
final RexWindow aggWindow = over.getWindow();
// Look up or create a window.
RelCollation orderKeys =
getCollation(
- com.google.common.collect.Lists.newArrayList(
+ Lists.newArrayList(
Util.filter(
aggWindow.orderKeys,
rexFieldCollation ->
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexLiteral.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexLiteral.java
deleted file mode 100644
index dfb68088c9f..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexLiteral.java
+++ /dev/null
@@ -1,1179 +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.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import org.apache.calcite.avatica.util.ByteString;
-import org.apache.calcite.avatica.util.DateTimeUtils;
-import org.apache.calcite.avatica.util.TimeUnit;
-import org.apache.calcite.config.CalciteSystemProperty;
-import org.apache.calcite.linq4j.function.Functions;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.runtime.FlatLists;
-import org.apache.calcite.runtime.GeoFunctions;
-import org.apache.calcite.runtime.Geometries;
-import org.apache.calcite.sql.SqlCollation;
-import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
-import org.apache.calcite.sql.parser.SqlParserUtil;
-import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.util.CompositeList;
-import org.apache.calcite.util.ConversionUtil;
-import org.apache.calcite.util.DateString;
-import org.apache.calcite.util.Litmus;
-import org.apache.calcite.util.NlsString;
-import org.apache.calcite.util.Sarg;
-import org.apache.calcite.util.TimeString;
-import org.apache.calcite.util.TimestampString;
-import org.apache.calcite.util.Util;
-
-import java.io.PrintWriter;
-import java.math.BigDecimal;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TimeZone;
-
-/**
- * Constant value in a row-expression.
- *
- * <p>There are several methods for creating literals in {@link RexBuilder}: {@link
- * RexBuilder#makeLiteral(boolean)} and so forth.
- *
- * <p>How is the value stored? In that respect, the class is somewhat of a black box. There is a
- * {@link #getValue} method which returns the value as an object, but the type of that value is
- * implementation detail, and it is best that your code does not depend upon that knowledge. It is
- * better to use task-oriented methods such as {@link #getValue2} and {@link #toJavaString}.
- *
- * <p>The allowable types and combinations are:
- *
- * <table>
- * <caption>Allowable types for RexLiteral instances</caption>
- * <tr>
- * <th>TypeName</th>
- * <th>Meaning</th>
- * <th>Value type</th>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#NULL}</td>
- * <td>The null value. It has its own special type.</td>
- * <td>null</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#BOOLEAN}</td>
- * <td>Boolean, namely <code>TRUE</code>, <code>FALSE</code> or <code>
- * UNKNOWN</code>.</td>
- * <td>{@link Boolean}, or null represents the UNKNOWN value</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#DECIMAL}</td>
- * <td>Exact number, for example <code>0</code>, <code>-.5</code>, <code>
- * 12345</code>.</td>
- * <td>{@link BigDecimal}</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#DOUBLE}</td>
- * <td>Approximate number, for example <code>6.023E-23</code>.</td>
- * <td>{@link BigDecimal}</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#DATE}</td>
- * <td>Date, for example <code>DATE '1969-04'29'</code></td>
- * <td>{@link Calendar};
- * also {@link Calendar} (UTC time zone)
- * and {@link Integer} (days since POSIX epoch)</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#TIME}</td>
- * <td>Time, for example <code>TIME '18:37:42.567'</code></td>
- * <td>{@link Calendar};
- * also {@link Calendar} (UTC time zone)
- * and {@link Integer} (milliseconds since midnight)</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#TIMESTAMP}</td>
- * <td>Timestamp, for example <code>TIMESTAMP '1969-04-29
- * 18:37:42.567'</code></td>
- * <td>{@link TimestampString};
- * also {@link Calendar} (UTC time zone)
- * and {@link Long} (milliseconds since POSIX epoch)</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#INTERVAL_DAY},
- * {@link SqlTypeName#INTERVAL_DAY_HOUR},
- * {@link SqlTypeName#INTERVAL_DAY_MINUTE},
- * {@link SqlTypeName#INTERVAL_DAY_SECOND},
- * {@link SqlTypeName#INTERVAL_HOUR},
- * {@link SqlTypeName#INTERVAL_HOUR_MINUTE},
- * {@link SqlTypeName#INTERVAL_HOUR_SECOND},
- * {@link SqlTypeName#INTERVAL_MINUTE},
- * {@link SqlTypeName#INTERVAL_MINUTE_SECOND},
- * {@link SqlTypeName#INTERVAL_SECOND}</td>
- * <td>Interval, for example <code>INTERVAL '4:3:2' HOUR TO SECOND</code></td>
- * <td>{@link BigDecimal};
- * also {@link Long} (milliseconds)</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#INTERVAL_YEAR},
- * {@link SqlTypeName#INTERVAL_YEAR_MONTH},
- * {@link SqlTypeName#INTERVAL_MONTH}</td>
- * <td>Interval, for example <code>INTERVAL '2-3' YEAR TO MONTH</code></td>
- * <td>{@link BigDecimal};
- * also {@link Integer} (months)</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#CHAR}</td>
- * <td>Character constant, for example <code>'Hello, world!'</code>, <code>
- * ''</code>, <code>_N'Bonjour'</code>, <code>_ISO-8859-1'It''s superman!'
- * COLLATE SHIFT_JIS$ja_JP$2</code>. These are always CHAR, never VARCHAR.</td>
- * <td>{@link NlsString};
- * also {@link String}</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#BINARY}</td>
- * <td>Binary constant, for example <code>X'7F34'</code>. (The number of hexits
- * must be even; see above.) These constants are always BINARY, never
- * VARBINARY.</td>
- * <td>{@link ByteBuffer};
- * also {@code byte[]}</td>
- * </tr>
- * <tr>
- * <td>{@link SqlTypeName#SYMBOL}</td>
- * <td>A symbol is a special type used to make parsing easier; it is not part of
- * the SQL standard, and is not exposed to end-users. It is used to hold a flag,
- * such as the LEADING flag in a call to the function <code>
- * TRIM([LEADING|TRAILING|BOTH] chars FROM string)</code>.</td>
- * <td>An enum class</td>
- * </tr>
- * </table>
- *
- * <p>Line 374 ~ 376: fix FLINK-20942, the file should be removed when upgrade to CALCITE 1.27.0
- * release.
- */
-public class RexLiteral extends RexNode {
- // ~ Instance fields --------------------------------------------------------
-
- /**
- * The value of this literal. Must be consistent with its type, as per {@link
- * #valueMatchesType}. For example, you can't store an {@link Integer} value here just because
- * you feel like it -- all numbers are represented by a {@link BigDecimal}. But since this field
- * is private, it doesn't really matter how the values are stored.
- */
- private final Comparable value;
-
- /** The real type of this literal, as reported by {@link #getType}. */
- private final RelDataType type;
-
- // TODO jvs 26-May-2006: Use SqlTypeFamily instead; it exists
- // for exactly this purpose (to avoid the confusion which results
- // from overloading SqlTypeName).
- /**
- * An indication of the broad type of this literal -- even if its type isn't a SQL type.
- * Sometimes this will be different than the SQL type; for example, all exact numbers, including
- * integers have typeName {@link SqlTypeName#DECIMAL}. See {@link #valueMatchesType} for the
- * definitive story.
- */
- private final SqlTypeName typeName;
-
- private static final ImmutableList<TimeUnit> TIME_UNITS =
- ImmutableList.copyOf(TimeUnit.values());
-
- // ~ Constructors -----------------------------------------------------------
-
- /** Creates a <code>RexLiteral</code>. */
- RexLiteral(Comparable value, RelDataType type, SqlTypeName typeName) {
- this.value = value;
- this.type = Objects.requireNonNull(type);
- this.typeName = Objects.requireNonNull(typeName);
- Preconditions.checkArgument(valueMatchesType(value, typeName, true));
- Preconditions.checkArgument((value == null) == type.isNullable());
- Preconditions.checkArgument(typeName != SqlTypeName.ANY);
- this.digest = computeDigest(RexDigestIncludeType.OPTIONAL);
- }
-
- // ~ Methods ----------------------------------------------------------------
-
- /**
- * Returns a string which concisely describes the definition of this rex literal. Two literals
- * are equivalent if and only if their digests are the same.
- *
- * <p>The digest does not contain the expression's identity, but does include the identity of
- * children.
- *
- * <p>Technically speaking 1:INT differs from 1:FLOAT, so we need data type in the literal's
- * digest, however we want to avoid extra verbosity of the {@link RelNode#getDigest()} for
- * readability purposes, so we omit type info in certain cases. For instance, 1:INT becomes 1
- * (INT is implied by default), however 1:BIGINT always holds the type
- *
- * <p>Here's a non-exhaustive list of the "well known cases":
- *
- * <ul>
- * <li>Hide "NOT NULL" for not null literals
- * <li>Hide INTEGER, BOOLEAN, SYMBOL, TIME(0), TIMESTAMP(0), DATE(0) types
- * <li>Hide collation when it matches IMPLICIT/COERCIBLE
- * <li>Hide charset when it matches default
- * <li>Hide CHAR(xx) when literal length is equal to the precision of the type. In other
- * words, use 'Bob' instead of 'Bob':CHAR(3)
- * <li>Hide BOOL for AND/OR arguments. In other words, AND(true, null) means null is BOOL.
- * <li>Hide types for literals in simple binary operations (e.g. +, -, *, /, comparison) when
- * type of the other argument is clear. See {@link RexCall#computeDigest(boolean)} For
- * instance: =(true. null) means null is BOOL. =($0, null) means the type of null matches
- * the type of $0.
- * </ul>
- *
- * @param includeType whether the digest should include type or not
- * @return digest
- */
- public final String computeDigest(RexDigestIncludeType includeType) {
- if (includeType == RexDigestIncludeType.OPTIONAL) {
- if (digest != null) {
- // digest is initialized with OPTIONAL, so cached value matches for
- // includeType=OPTIONAL as well
- return digest;
- }
- // Compute we should include the type or not
- includeType = digestIncludesType();
- } else if (digest != null && includeType == digestIncludesType()) {
- // The digest is always computed with includeType=OPTIONAL
- // If it happened to omit the type, we want to optimize computeDigest(NO_TYPE) as well
- // If the digest includes the type, we want to optimize computeDigest(ALWAYS)
- return digest;
- }
-
- return toJavaString(value, typeName, type, includeType);
- }
-
- /**
- * Returns true if {@link RexDigestIncludeType#OPTIONAL} digest would include data type.
- *
- * @see RexCall#computeDigest(boolean)
- * @return true if {@link RexDigestIncludeType#OPTIONAL} digest would include data type
- */
- RexDigestIncludeType digestIncludesType() {
- return shouldIncludeType(value, type);
- }
-
- /** Returns whether a value is appropriate for its type. (We have rules about these things!) */
- public static boolean valueMatchesType(Comparable value, SqlTypeName typeName, boolean strict) {
- if (value == null) {
- return true;
- }
- switch (typeName) {
- case BOOLEAN:
- // Unlike SqlLiteral, we do not allow boolean null.
- return value instanceof Boolean;
- case NULL:
- return false; // value should have been null
- case INTEGER: // not allowed -- use Decimal
- case TINYINT:
- case SMALLINT:
- if (strict) {
- throw Util.unexpected(typeName);
- }
- // fall through
- case DECIMAL:
- case DOUBLE:
- case FLOAT:
- case REAL:
- case BIGINT:
- return value instanceof BigDecimal;
- case DATE:
- return value instanceof DateString;
- case TIME:
- return value instanceof TimeString;
- case TIME_WITH_LOCAL_TIME_ZONE:
- return value instanceof TimeString;
- case TIMESTAMP:
- return value instanceof TimestampString;
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- return value instanceof TimestampString;
- 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:
- // The value of a DAY-TIME interval (whatever the start and end units,
- // even say HOUR TO MINUTE) is in milliseconds (perhaps fractional
- // milliseconds). The value of a YEAR-MONTH interval is in months.
- return value instanceof BigDecimal;
- case VARBINARY: // not allowed -- use Binary
- if (strict) {
- throw Util.unexpected(typeName);
- }
- // fall through
- case BINARY:
- return value instanceof ByteString;
- case VARCHAR: // not allowed -- use Char
- if (strict) {
- throw Util.unexpected(typeName);
- }
- // fall through
- case CHAR:
- // A SqlLiteral's charset and collation are optional; not so a
- // RexLiteral.
- return (value instanceof NlsString)
- && (((NlsString) value).getCharset() != null)
- && (((NlsString) value).getCollation() != null);
- case SARG:
- return value instanceof Sarg;
- case SYMBOL:
- return value instanceof Enum;
- case ROW:
- case MULTISET:
- return value instanceof List;
- case GEOMETRY:
- return value instanceof Geometries.Geom;
- case ANY:
- // Literal of type ANY is not legal. "CAST(2 AS ANY)" remains
- // an integer literal surrounded by a cast function.
- return false;
- default:
- throw Util.unexpected(typeName);
- }
- }
-
- /** Returns the strict literal type for a given type. */
- public static SqlTypeName strictTypeName(RelDataType type) {
- final SqlTypeName typeName = type.getSqlTypeName();
- switch (typeName) {
- case INTEGER:
- case TINYINT:
- case SMALLINT:
- return SqlTypeName.DECIMAL;
- case REAL:
- case FLOAT:
- return SqlTypeName.DOUBLE;
- case VARBINARY:
- return SqlTypeName.BINARY;
- case VARCHAR:
- return SqlTypeName.CHAR;
- default:
- return typeName;
- }
- }
-
- private static String toJavaString(
- Comparable value,
- SqlTypeName typeName,
- RelDataType type,
- RexDigestIncludeType includeType) {
- assert includeType != RexDigestIncludeType.OPTIONAL
- : "toJavaString must not be called with includeType=OPTIONAL";
- if (value == null) {
- return includeType == RexDigestIncludeType.NO_TYPE
- ? "null"
- : "null:" + type.getFullTypeString();
- }
- StringBuilder sb = new StringBuilder();
- appendAsJava(value, sb, typeName, type, false, includeType);
-
- if (includeType != RexDigestIncludeType.NO_TYPE) {
- sb.append(':');
- final String fullTypeString = type.getFullTypeString();
- if (!fullTypeString.endsWith("NOT NULL")) {
- sb.append(fullTypeString);
- } else {
- // Trim " NOT NULL". Apparently, the literal is not null, so we just print the data
- // type.
- sb.append(fullTypeString, 0, fullTypeString.length() - 9);
- }
- }
- return sb.toString();
- }
-
- /**
- * Computes if data type can be omitted from the digset.
- *
- * <p>For instance, {@code 1:BIGINT} has to keep data type while {@code 1:INT} should be
- * represented as just {@code 1}.
- *
- * <p>Implementation assumption: this method should be fast. In fact might call {@link
- * NlsString#getValue()} which could decode the string, however we rely on the cache there.
- *
- * @see RexLiteral#computeDigest(RexDigestIncludeType)
- * @param value value of the literal
- * @param type type of the literal
- * @return NO_TYPE when type can be omitted, ALWAYS otherwise
- */
- private static RexDigestIncludeType shouldIncludeType(Comparable value, RelDataType type) {
- if (type.isNullable()) {
- // This means "null literal", so we require a type for it
- // There might be exceptions like AND(null, true) which are handled by
- // RexCall#computeDigest
- return RexDigestIncludeType.ALWAYS;
- }
- // The variable here simplifies debugging (one can set a breakpoint at return)
- // final ensures we set the value in all the branches, and it ensures the value is set just
- // once
- final RexDigestIncludeType includeType;
- if (type.getSqlTypeName() == SqlTypeName.BOOLEAN
- || type.getSqlTypeName() == SqlTypeName.INTEGER
- || type.getSqlTypeName() == SqlTypeName.SYMBOL) {
- // We don't want false:BOOLEAN NOT NULL, so we don't print type information for
- // non-nullable BOOLEAN and INTEGER
- includeType = RexDigestIncludeType.NO_TYPE;
- } else if (type.getSqlTypeName() == SqlTypeName.CHAR && value instanceof NlsString) {
- NlsString nlsString = (NlsString) value;
-
- // Ignore type information for 'Bar':CHAR(3)
- if (((nlsString.getCharset() != null
- && type.getCharset().equals(nlsString.getCharset()))
- || (nlsString.getCharset() == null
- && SqlCollation.IMPLICIT
- .getCharset()
- .equals(type.getCharset())))
- && nlsString.getCollation().equals(type.getCollation())
- && ((NlsString) value).getValue().length() == type.getPrecision()) {
- includeType = RexDigestIncludeType.NO_TYPE;
- } else {
- includeType = RexDigestIncludeType.ALWAYS;
- }
- } else if (type.getPrecision() == 0
- && (type.getSqlTypeName() == SqlTypeName.TIME
- || type.getSqlTypeName() == SqlTypeName.TIMESTAMP
- || type.getSqlTypeName() == SqlTypeName.DATE)) {
- // Ignore type information for '12:23:20':TIME(0)
- // Note that '12:23:20':TIME WITH LOCAL TIME ZONE
- includeType = RexDigestIncludeType.NO_TYPE;
- } else {
- includeType = RexDigestIncludeType.ALWAYS;
- }
- return includeType;
- }
-
- /**
- * Returns whether a value is valid as a constant value, using the same criteria as {@link
- * #valueMatchesType}.
- */
- public static boolean validConstant(Object o, Litmus litmus) {
- if (o == null
- || o instanceof BigDecimal
- || o instanceof NlsString
- || o instanceof ByteString
- || o instanceof Boolean) {
- return litmus.succeed();
- } else if (o instanceof List) {
- List list = (List) o;
- for (Object o1 : list) {
- if (!validConstant(o1, litmus)) {
- return litmus.fail("not a constant: {}", o1);
- }
- }
- return litmus.succeed();
- } else if (o instanceof Map) {
- @SuppressWarnings("unchecked")
- final Map<Object, Object> map = (Map) o;
- for (Map.Entry entry : map.entrySet()) {
- if (!validConstant(entry.getKey(), litmus)) {
- return litmus.fail("not a constant: {}", entry.getKey());
- }
- if (!validConstant(entry.getValue(), litmus)) {
- return litmus.fail("not a constant: {}", entry.getValue());
- }
- }
- return litmus.succeed();
- } else {
- return litmus.fail("not a constant: {}", o);
- }
- }
-
- /**
- * Returns a list of the time units covered by an interval type such as HOUR TO SECOND. Adds
- * MILLISECOND if the end is SECOND, to deal with fractional seconds.
- */
- private static List<TimeUnit> getTimeUnits(SqlTypeName typeName) {
- final TimeUnit start = typeName.getStartUnit();
- final TimeUnit end = typeName.getEndUnit();
- final ImmutableList<TimeUnit> list = TIME_UNITS.subList(start.ordinal(), end.ordinal() + 1);
- if (end == TimeUnit.SECOND) {
- return CompositeList.of(list, ImmutableList.of(TimeUnit.MILLISECOND));
- }
- return list;
- }
-
- private String intervalString(BigDecimal v) {
- final List<TimeUnit> timeUnits = getTimeUnits(type.getSqlTypeName());
- final StringBuilder b = new StringBuilder();
- for (TimeUnit timeUnit : timeUnits) {
- final BigDecimal[] result = v.divideAndRemainder(timeUnit.multiplier);
- if (b.length() > 0) {
- b.append(timeUnit.separator);
- }
- final int width = b.length() == 0 ? -1 : width(timeUnit); // don't pad 1st
- pad(b, result[0].toString(), width);
- v = result[1];
- }
- if (Util.last(timeUnits) == TimeUnit.MILLISECOND) {
- while (b.toString().matches(".*\\.[0-9]*0")) {
- if (b.toString().endsWith(".0")) {
- b.setLength(b.length() - 2); // remove ".0"
- } else {
- b.setLength(b.length() - 1); // remove "0"
- }
- }
- }
- return b.toString();
- }
-
- private static void pad(StringBuilder b, String s, int width) {
- if (width >= 0) {
- for (int i = s.length(); i < width; i++) {
- b.append('0');
- }
- }
- b.append(s);
- }
-
- private static int width(TimeUnit timeUnit) {
- switch (timeUnit) {
- case MILLISECOND:
- return 3;
- case HOUR:
- case MINUTE:
- case SECOND:
- return 2;
- default:
- return -1;
- }
- }
-
- /** Prints the value this literal as a Java string constant. */
- public void printAsJava(PrintWriter pw) {
- Util.asStringBuilder(
- pw,
- sb -> appendAsJava(value, sb, typeName, type, true, RexDigestIncludeType.NO_TYPE));
- }
-
- /**
- * Appends the specified value in the provided destination as a Java string. The value must be
- * consistent with the type, as per {@link #valueMatchesType}.
- *
- * <p>Typical return values:
- *
- * <ul>
- * <li>true
- * <li>null
- * <li>"Hello, world!"
- * <li>1.25
- * <li>1234ABCD
- * </ul>
- *
- * @param value Value to be appended to the provided destination as a Java string
- * @param sb Destination to which to append the specified value
- * @param typeName Type name to be used for the transformation of the value to a Java string
- * @param type Type to be used for the transformation of the value to a Java string
- * @param includeType Whether to include the data type in the Java representation
- */
- private static void appendAsJava(
- Comparable value,
- StringBuilder sb,
- SqlTypeName typeName,
- RelDataType type,
- boolean java,
- RexDigestIncludeType includeType) {
- switch (typeName) {
- case CHAR:
- NlsString nlsString = (NlsString) value;
- if (java) {
- Util.printJavaString(sb, nlsString.getValue(), true);
- } else {
- boolean includeCharset =
- (nlsString.getCharsetName() != null)
- && !nlsString
- .getCharsetName()
- .equals(CalciteSystemProperty.DEFAULT_CHARSET.value());
- sb.append(nlsString.asSql(includeCharset, false));
- }
- break;
- case BOOLEAN:
- assert value instanceof Boolean;
- sb.append(value.toString());
- break;
- case DECIMAL:
- assert value instanceof BigDecimal;
- sb.append(value.toString());
- break;
- case DOUBLE:
- assert value instanceof BigDecimal;
- sb.append(Util.toScientificNotation((BigDecimal) value));
- break;
- case BIGINT:
- assert value instanceof BigDecimal;
- long narrowLong = ((BigDecimal) value).longValue();
- sb.append(String.valueOf(narrowLong));
- sb.append('L');
- break;
- case BINARY:
- assert value instanceof ByteString;
- sb.append("X'");
- sb.append(((ByteString) value).toString(16));
- sb.append("'");
- break;
- case NULL:
- assert value == null;
- sb.append("null");
- break;
- case SARG:
- assert value instanceof Sarg;
- //noinspection unchecked,rawtypes
- Util.asStringBuilder(sb, sb2 -> printSarg(sb2, (Sarg) value, type));
- break;
- case SYMBOL:
- assert value instanceof Enum;
- sb.append("FLAG(");
- sb.append(value.toString());
- sb.append(")");
- break;
- case DATE:
- assert value instanceof DateString;
- sb.append(value.toString());
- break;
- case TIME:
- case TIME_WITH_LOCAL_TIME_ZONE:
- assert value instanceof TimeString;
- sb.append(value.toString());
- break;
- case TIMESTAMP:
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- assert value instanceof TimestampString;
- sb.append(value.toString());
- break;
- 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:
- assert value instanceof BigDecimal;
- sb.append(value.toString());
- break;
- case MULTISET:
- case ROW:
- final List<RexLiteral> list = (List) value;
- Util.asStringBuilder(
- sb,
- sb2 ->
- Util.printList(
- sb,
- list.size(),
- (sb3, i) ->
- sb3.append(
- list.get(i).computeDigest(includeType))));
- break;
- case GEOMETRY:
- final String wkt = GeoFunctions.ST_AsWKT((Geometries.Geom) value);
- sb.append(wkt);
- break;
- default:
- assert valueMatchesType(value, typeName, true);
- throw Util.needToImplement(typeName);
- }
- }
-
- private static <C extends Comparable<C>> void printSarg(
- StringBuilder sb, Sarg<C> sarg, RelDataType type) {
- sarg.printTo(sb, (sb2, value) -> sb2.append(toLiteral(type, value)));
- }
-
- /**
- * Converts a value to a temporary literal, for the purposes of generating a digest. Literals of
- * type ROW and MULTISET require that their components are also literals.
- */
- private static RexLiteral toLiteral(RelDataType type, Comparable<?> value) {
- final SqlTypeName typeName = strictTypeName(type);
- switch (typeName) {
- case ROW:
- final List<Comparable<?>> fieldValues = (List) value;
- final List<RelDataTypeField> fields = type.getFieldList();
- final List<RexLiteral> fieldLiterals =
- FlatLists.of(
- Functions.generate(
- fieldValues.size(),
- i ->
- toLiteral(
- fields.get(i).getType(),
- fieldValues.get(i))));
- return new RexLiteral((Comparable) fieldLiterals, type, typeName);
-
- case MULTISET:
- final List<Comparable<?>> elementValues = (List) value;
- final List<RexLiteral> elementLiterals =
- FlatLists.of(
- Functions.generate(
- elementValues.size(),
- i ->
- toLiteral(
- type.getComponentType(),
- elementValues.get(i))));
- return new RexLiteral((Comparable) elementLiterals, type, typeName);
-
- default:
- return new RexLiteral(value, type, typeName);
- }
- }
-
- /**
- * Converts a Jdbc string into a RexLiteral. This method accepts a string, as returned by the
- * Jdbc method ResultSet.getString(), and restores the string into an equivalent RexLiteral. It
- * allows one to use Jdbc strings as a common format for data.
- *
- * <p>If a null literal is provided, then a null pointer will be returned.
- *
- * @param type data type of literal to be read
- * @param typeName type family of literal
- * @param literal the (non-SQL encoded) string representation, as returned by the Jdbc call to
- * return a column as a string
- * @return a typed RexLiteral, or null
- */
- public static RexLiteral fromJdbcString(
- RelDataType type, SqlTypeName typeName, String literal) {
- if (literal == null) {
- return null;
- }
-
- switch (typeName) {
- case CHAR:
- Charset charset = type.getCharset();
- SqlCollation collation = type.getCollation();
- NlsString str = new NlsString(literal, charset.name(), collation);
- return new RexLiteral(str, type, typeName);
- case BOOLEAN:
- boolean b = ConversionUtil.toBoolean(literal);
- return new RexLiteral(b, type, typeName);
- case DECIMAL:
- case DOUBLE:
- BigDecimal d = new BigDecimal(literal);
- return new RexLiteral(d, type, typeName);
- case BINARY:
- byte[] bytes = ConversionUtil.toByteArrayFromString(literal, 16);
- return new RexLiteral(new ByteString(bytes), type, typeName);
- case NULL:
- return new RexLiteral(null, type, typeName);
- 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:
- long millis = SqlParserUtil.intervalToMillis(literal, type.getIntervalQualifier());
- return new RexLiteral(BigDecimal.valueOf(millis), type, typeName);
- case INTERVAL_YEAR:
- case INTERVAL_YEAR_MONTH:
- case INTERVAL_MONTH:
- long months = SqlParserUtil.intervalToMonths(literal, type.getIntervalQualifier());
- return new RexLiteral(BigDecimal.valueOf(months), type, typeName);
- case DATE:
- case TIME:
- case TIMESTAMP:
- String format = getCalendarFormat(typeName);
- TimeZone tz = DateTimeUtils.UTC_ZONE;
- final Comparable v;
- switch (typeName) {
- case DATE:
- final Calendar cal =
- DateTimeUtils.parseDateFormat(
- literal, new SimpleDateFormat(format, Locale.ROOT), tz);
- if (cal == null) {
- throw new AssertionError(
- "fromJdbcString: invalid date/time value '" + literal + "'");
- }
- v = DateString.fromCalendarFields(cal);
- break;
- default:
- // Allow fractional seconds for times and timestamps
- assert format != null;
- final DateTimeUtils.PrecisionTime ts =
- DateTimeUtils.parsePrecisionDateTimeLiteral(
- literal, new SimpleDateFormat(format, Locale.ROOT), tz, -1);
- if (ts == null) {
- throw new AssertionError(
- "fromJdbcString: invalid date/time value '" + literal + "'");
- }
- switch (typeName) {
- case TIMESTAMP:
- v =
- TimestampString.fromCalendarFields(ts.getCalendar())
- .withFraction(ts.getFraction());
- break;
- case TIME:
- v =
- TimeString.fromCalendarFields(ts.getCalendar())
- .withFraction(ts.getFraction());
- break;
- default:
- throw new AssertionError();
- }
- }
- return new RexLiteral(v, type, typeName);
-
- case SYMBOL:
- // Symbols are for internal use
- default:
- throw new AssertionError("fromJdbcString: unsupported type");
- }
- }
-
- private static String getCalendarFormat(SqlTypeName typeName) {
- switch (typeName) {
- case DATE:
- return DateTimeUtils.DATE_FORMAT_STRING;
- case TIME:
- return DateTimeUtils.TIME_FORMAT_STRING;
- case TIMESTAMP:
- return DateTimeUtils.TIMESTAMP_FORMAT_STRING;
- default:
- throw new AssertionError("getCalendarFormat: unknown type");
- }
- }
-
- public SqlTypeName getTypeName() {
- return typeName;
- }
-
- public RelDataType getType() {
- return type;
- }
-
- @Override
- public SqlKind getKind() {
- return SqlKind.LITERAL;
- }
-
- /** Returns whether this literal's value is null. */
- public boolean isNull() {
- return value == null;
- }
-
- /**
- * Returns the value of this literal.
- *
- * <p>For backwards compatibility, returns DATE. TIME and TIMESTAMP as a {@link Calendar} value
- * in UTC time zone.
- */
- public Comparable getValue() {
- assert valueMatchesType(value, typeName, true) : value;
- if (value == null) {
- return null;
- }
- switch (typeName) {
- case TIME:
- case DATE:
- case TIMESTAMP:
- return getValueAs(Calendar.class);
- default:
- return value;
- }
- }
-
- /**
- * Returns the value of this literal, in the form that the calculator program builder wants it.
- */
- public Object getValue2() {
- if (value == null) {
- return null;
- }
- switch (typeName) {
- case CHAR:
- return getValueAs(String.class);
- case DECIMAL:
- case TIMESTAMP:
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- return getValueAs(Long.class);
- case DATE:
- case TIME:
- case TIME_WITH_LOCAL_TIME_ZONE:
- return getValueAs(Integer.class);
- default:
- return value;
- }
- }
-
- /** Returns the value of this literal, in the form that the rex-to-lix translator wants it. */
- public Object getValue3() {
- if (value == null) {
- return null;
- }
- switch (typeName) {
- case DECIMAL:
- assert value instanceof BigDecimal;
- return value;
- default:
- return getValue2();
- }
- }
-
- /** Returns the value of this literal, in the form that {@link RexInterpreter} wants it. */
- public Comparable getValue4() {
- if (value == null) {
- return null;
- }
- switch (typeName) {
- case TIMESTAMP:
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- return getValueAs(Long.class);
- case DATE:
- case TIME:
- case TIME_WITH_LOCAL_TIME_ZONE:
- return getValueAs(Integer.class);
- default:
- return value;
- }
- }
-
- /**
- * Returns the value of this literal as an instance of the specified class.
- *
- * <p>The following SQL types allow more than one form:
- *
- * <ul>
- * <li>CHAR as {@link NlsString} or {@link String}
- * <li>TIME as {@link TimeString}, {@link Integer} (milliseconds since midnight), {@link
- * Calendar} (in UTC)
- * <li>DATE as {@link DateString}, {@link Integer} (days since 1970-01-01), {@link Calendar}
- * <li>TIMESTAMP as {@link TimestampString}, {@link Long} (milliseconds since 1970-01-01
- * 00:00:00), {@link Calendar}
- * <li>DECIMAL as {@link BigDecimal} or {@link Long}
- * </ul>
- *
- * <p>Called with {@code clazz} = {@link Comparable}, returns the value in its native form.
- *
- * @param clazz Desired return type
- * @param <T> Return type
- * @return Value of this literal in the desired type
- */
- public <T> T getValueAs(Class<T> clazz) {
- if (value == null || clazz.isInstance(value)) {
- return clazz.cast(value);
- }
- switch (typeName) {
- case BINARY:
- if (clazz == byte[].class) {
- return clazz.cast(((ByteString) value).getBytes());
- }
- break;
- case CHAR:
- if (clazz == String.class) {
- return clazz.cast(((NlsString) value).getValue());
- } else if (clazz == Character.class) {
- return clazz.cast(((NlsString) value).getValue().charAt(0));
- }
- break;
- case VARCHAR:
- if (clazz == String.class) {
- return clazz.cast(((NlsString) value).getValue());
- }
- break;
- case DECIMAL:
- if (clazz == Long.class) {
- return clazz.cast(((BigDecimal) value).unscaledValue().longValue());
- }
- // fall through
- case BIGINT:
- case INTEGER:
- case SMALLINT:
- case TINYINT:
- case DOUBLE:
- case REAL:
- case FLOAT:
- if (clazz == Long.class) {
- return clazz.cast(((BigDecimal) value).longValue());
- } else if (clazz == Integer.class) {
- return clazz.cast(((BigDecimal) value).intValue());
- } else if (clazz == Short.class) {
- return clazz.cast(((BigDecimal) value).shortValue());
- } else if (clazz == Byte.class) {
- return clazz.cast(((BigDecimal) value).byteValue());
- } else if (clazz == Double.class) {
- return clazz.cast(((BigDecimal) value).doubleValue());
- } else if (clazz == Float.class) {
- return clazz.cast(((BigDecimal) value).floatValue());
- }
- break;
- case DATE:
- if (clazz == Integer.class) {
- return clazz.cast(((DateString) value).getDaysSinceEpoch());
- } else if (clazz == Calendar.class) {
- return clazz.cast(((DateString) value).toCalendar());
- }
- break;
- case TIME:
- if (clazz == Integer.class) {
- return clazz.cast(((TimeString) value).getMillisOfDay());
- } else if (clazz == Calendar.class) {
- // Note: Nanos are ignored
- return clazz.cast(((TimeString) value).toCalendar());
- }
- break;
- case TIME_WITH_LOCAL_TIME_ZONE:
- if (clazz == Integer.class) {
- // Milliseconds since 1970-01-01 00:00:00
- return clazz.cast(((TimeString) value).getMillisOfDay());
- }
- break;
- case TIMESTAMP:
- if (clazz == Long.class) {
- // Milliseconds since 1970-01-01 00:00:00
- return clazz.cast(((TimestampString) value).getMillisSinceEpoch());
- } else if (clazz == Calendar.class) {
- // Note: Nanos are ignored
- return clazz.cast(((TimestampString) value).toCalendar());
- }
- break;
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- if (clazz == Long.class) {
- // Milliseconds since 1970-01-01 00:00:00
- return clazz.cast(((TimestampString) value).getMillisSinceEpoch());
- } else if (clazz == Calendar.class) {
- // Note: Nanos are ignored
- return clazz.cast(((TimestampString) value).toCalendar());
- }
- break;
- 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:
- if (clazz == Integer.class) {
- return clazz.cast(((BigDecimal) value).intValue());
- } else if (clazz == Long.class) {
- return clazz.cast(((BigDecimal) value).longValue());
- } else if (clazz == String.class) {
- return clazz.cast(intervalString(getValueAs(BigDecimal.class).abs()));
- } else if (clazz == Boolean.class) {
- // return whether negative
- return clazz.cast(getValueAs(BigDecimal.class).signum() < 0);
- }
- break;
- }
- throw new AssertionError("cannot convert " + typeName + " literal to " + clazz);
- }
-
- public static boolean booleanValue(RexNode node) {
- return (Boolean) ((RexLiteral) node).value;
- }
-
- public boolean isAlwaysTrue() {
- if (typeName != SqlTypeName.BOOLEAN) {
- return false;
- }
- return booleanValue(this);
- }
-
- public boolean isAlwaysFalse() {
- if (typeName != SqlTypeName.BOOLEAN) {
- return false;
- }
- return !booleanValue(this);
- }
-
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- return (obj instanceof RexLiteral)
- && equals(((RexLiteral) obj).value, value)
- && equals(((RexLiteral) obj).type, type);
- }
-
- public int hashCode() {
- return Objects.hash(value, type);
- }
-
- public static Comparable value(RexNode node) {
- return findValue(node);
- }
-
- public static int intValue(RexNode node) {
- final Comparable value = findValue(node);
- return ((Number) value).intValue();
- }
-
- public static String stringValue(RexNode node) {
- final Comparable value = findValue(node);
- return (value == null) ? null : ((NlsString) value).getValue();
- }
-
- private static Comparable findValue(RexNode node) {
- if (node instanceof RexLiteral) {
- return ((RexLiteral) node).value;
- }
- if (node instanceof RexCall) {
- final RexCall call = (RexCall) node;
- final SqlOperator operator = call.getOperator();
- if (operator == SqlStdOperatorTable.CAST) {
- return findValue(call.getOperands().get(0));
- }
- if (operator == SqlStdOperatorTable.UNARY_MINUS) {
- final BigDecimal value = (BigDecimal) findValue(call.getOperands().get(0));
- return value.negate();
- }
- }
- throw new AssertionError("not a literal: " + node);
- }
-
- public static boolean isNullLiteral(RexNode node) {
- return (node instanceof RexLiteral) && (((RexLiteral) node).value == null);
- }
-
- private static boolean equals(Object o1, Object o2) {
- return Objects.equals(o1, o2);
- }
-
- public <R> R accept(RexVisitor<R> visitor) {
- return visitor.visitLiteral(this);
- }
-
- public <R, P> R accept(RexBiVisitor<R, P> visitor, P arg) {
- return visitor.visitLiteral(this, arg);
- }
-}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexSimplify.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexSimplify.java
index ea59497adfd..66bf009ea47 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -45,8 +45,7 @@ import org.apache.calcite.util.Pair;
import org.apache.calcite.util.RangeSets;
import org.apache.calcite.util.Sarg;
import org.apache.calcite.util.Util;
-
-import javax.annotation.Nonnull;
+import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.BitSet;
@@ -58,9 +57,10 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
+import static java.util.Objects.requireNonNull;
+import static org.apache.calcite.linq4j.Nullness.castNonNull;
import static org.apache.calcite.rex.RexUnknownAs.FALSE;
import static org.apache.calcite.rex.RexUnknownAs.TRUE;
import static org.apache.calcite.rex.RexUnknownAs.UNKNOWN;
@@ -68,14 +68,8 @@ import static org.apache.calcite.rex.RexUnknownAs.UNKNOWN;
/**
* Context required to simplify a row-expression.
*
- * <p>Copied to fix Calcite 1.26 bugs, should be removed for the next Calcite upgrade.
- *
- * <p>Changes (line numbers are from the original RexSimplify file):
- *
- * <ol>
- * <li>CALCITE-4364 & FLINK-19811: Line 1307, Line 1764, Line 2638 ~ Line 2656.
- * <li>CALCITE-4446 & FLINK-22015: Line 2542 ~ Line 2548, Line 2614 ~ Line 2619.
- * </ol>
+ * <p>Currently, is a workaround for <a href="https://issues.apache.org/jira/browse/FLINK-29237">
+ * Line 2822 ~ Line 2827. After fixing should be removed
*/
public class RexSimplify {
private final boolean paranoid;
@@ -111,12 +105,12 @@ public class RexSimplify {
boolean predicateElimination,
boolean paranoid,
RexExecutor executor) {
- this.rexBuilder = Objects.requireNonNull(rexBuilder);
- this.predicates = Objects.requireNonNull(predicates);
- this.defaultUnknownAs = Objects.requireNonNull(defaultUnknownAs);
+ this.rexBuilder = requireNonNull(rexBuilder, "rexBuilder");
+ this.predicates = requireNonNull(predicates, "predicates");
+ this.defaultUnknownAs = requireNonNull(defaultUnknownAs, "defaultUnknownAs");
this.predicateElimination = predicateElimination;
this.paranoid = paranoid;
- this.executor = Objects.requireNonNull(executor);
+ this.executor = requireNonNull(executor, "executor");
this.strong = new Strong();
}
@@ -307,6 +301,8 @@ public class RexSimplify {
case FALSE:
case TRUE:
return rexBuilder.makeLiteral(unknownAs.toBoolean());
+ default:
+ break;
}
}
return rexBuilder.makeNullLiteral(e.getType());
@@ -345,7 +341,7 @@ public class RexSimplify {
case SEARCH:
return simplifySearch((RexCall) e, unknownAs);
case LIKE:
- return simplifyLike((RexCall) e);
+ return simplifyLike((RexCall) e, unknownAs);
case MINUS_PREFIX:
return simplifyUnaryMinus((RexCall) e, unknownAs);
case PLUS_PREFIX:
@@ -359,6 +355,63 @@ public class RexSimplify {
}
}
+ /** Applies NOT to an expression. */
+ RexNode not(RexNode e) {
+ return RexUtil.not(rexBuilder, e);
+ }
+
+ /** Applies IS NOT FALSE to an expression. */
+ RexNode isNotFalse(RexNode e) {
+ return e.isAlwaysTrue()
+ ? rexBuilder.makeLiteral(true)
+ : e.isAlwaysFalse()
+ ? rexBuilder.makeLiteral(false)
+ : e.getKind() == SqlKind.NOT
+ ? isNotTrue(((RexCall) e).operands.get(0))
+ : predicates.isEffectivelyNotNull(e)
+ ? e // would "CAST(e AS BOOLEAN NOT NULL)" better?
+ : rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_FALSE, e);
+ }
+
+ /** Applies IS NOT TRUE to an expression. */
+ RexNode isNotTrue(RexNode e) {
+ return e.isAlwaysTrue()
+ ? rexBuilder.makeLiteral(false)
+ : e.isAlwaysFalse()
+ ? rexBuilder.makeLiteral(true)
+ : e.getKind() == SqlKind.NOT
+ ? isNotFalse(((RexCall) e).operands.get(0))
+ : predicates.isEffectivelyNotNull(e)
+ ? not(e)
+ : rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE, e);
+ }
+
+ /** Applies IS TRUE to an expression. */
+ RexNode isTrue(RexNode e) {
+ return e.isAlwaysTrue()
+ ? rexBuilder.makeLiteral(true)
+ : e.isAlwaysFalse()
+ ? rexBuilder.makeLiteral(false)
+ : e.getKind() == SqlKind.NOT
+ ? isFalse(((RexCall) e).operands.get(0))
+ : predicates.isEffectivelyNotNull(e)
+ ? e // would "CAST(e AS BOOLEAN NOT NULL)" better?
+ : rexBuilder.makeCall(SqlStdOperatorTable.IS_TRUE, e);
+ }
+
+ /** Applies IS FALSE to an expression. */
+ RexNode isFalse(RexNode e) {
+ return e.isAlwaysTrue()
+ ? rexBuilder.makeLiteral(false)
+ : e.isAlwaysFalse()
+ ? rexBuilder.makeLiteral(true)
+ : e.getKind() == SqlKind.NOT
+ ? isTrue(((RexCall) e).operands.get(0))
+ : predicates.isEffectivelyNotNull(e)
+ ? not(e)
+ : rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, e);
+ }
+
/** Runs simplification inside a non-specialized node. */
private RexNode simplifyGenericNode(RexCall e) {
final List<RexNode> operands = new ArrayList<>(e.operands);
@@ -369,11 +422,13 @@ public class RexSimplify {
return rexBuilder.makeCall(e.getType(), e.getOperator(), operands);
}
- private RexNode simplifyLike(RexCall e) {
+ private RexNode simplifyLike(RexCall e, RexUnknownAs unknownAs) {
if (e.operands.get(1) instanceof RexLiteral) {
final RexLiteral literal = (RexLiteral) e.operands.get(1);
- if (literal.getValueAs(String.class).equals("%")) {
- return rexBuilder.makeLiteral(true);
+ if ("%".equals(literal.getValueAs(String.class))) {
+ // "x LIKE '%'" simplifies to "x = x"
+ final RexNode x = e.operands.get(0);
+ return simplify(rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, x, x), unknownAs);
}
}
return simplifyGenericNode(e);
@@ -434,9 +489,7 @@ public class RexSimplify {
return cmp.ref;
case LESS_THAN:
case NOT_EQUALS: // x!=true
- return simplify(
- rexBuilder.makeCall(SqlStdOperatorTable.NOT, cmp.ref),
- unknownAs);
+ return simplify(not(cmp.ref), unknownAs);
case GREATER_THAN:
/* this is false, but could be null if x is null */
if (!cmp.ref.getType().isNullable()) {
@@ -449,15 +502,15 @@ public class RexSimplify {
return rexBuilder.makeLiteral(true);
}
break;
+ default:
+ break;
}
}
if (cmp.literal.isAlwaysFalse()) {
switch (cmp.kind) {
case EQUALS:
case LESS_THAN_OR_EQUAL:
- return simplify(
- rexBuilder.makeCall(SqlStdOperatorTable.NOT, cmp.ref),
- unknownAs);
+ return simplify(not(cmp.ref), unknownAs);
case NOT_EQUALS:
case GREATER_THAN:
return cmp.ref;
@@ -473,6 +526,8 @@ public class RexSimplify {
return rexBuilder.makeLiteral(false);
}
break;
+ default:
+ break;
}
}
}
@@ -595,10 +650,7 @@ public class RexSimplify {
doneTerms.set(i);
final RexNode t2 = simplify.simplify(t, unknownAs);
terms.set(i, t2);
- final RexNode inverse =
- simplify.simplify(
- rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE, t2),
- RexUnknownAs.UNKNOWN);
+ final RexNode inverse = simplify.simplify(isNotTrue(t2), RexUnknownAs.UNKNOWN);
final RelOptPredicateList newPredicates =
simplify.predicates.union(
rexBuilder,
@@ -625,71 +677,95 @@ public class RexSimplify {
private RexNode simplifyNot(RexCall call, RexUnknownAs unknownAs) {
final RexNode a = call.getOperands().get(0);
+ final List<RexNode> newOperands;
switch (a.getKind()) {
case NOT:
// NOT NOT x ==> x
return simplify(((RexCall) a).getOperands().get(0), unknownAs);
+
+ case SEARCH:
+ // NOT SEARCH(x, Sarg[(-inf, 10) OR NULL) ==> SEARCH(x, Sarg[[10, +inf)])
+ final RexCall call2 = (RexCall) a;
+ final RexNode ref = call2.operands.get(0);
+ final RexLiteral literal = (RexLiteral) call2.operands.get(1);
+ final Sarg sarg = literal.getValueAs(Sarg.class);
+ return simplifySearch(
+ call2.clone(
+ call2.type,
+ ImmutableList.of(
+ ref,
+ rexBuilder.makeLiteral(
+ requireNonNull(sarg, "sarg").negate(),
+ literal.getType(),
+ literal.getTypeName()))),
+ unknownAs.negate());
+
case LITERAL:
if (a.getType().getSqlTypeName() == SqlTypeName.BOOLEAN
&& !RexLiteral.isNullLiteral(a)) {
return rexBuilder.makeLiteral(!RexLiteral.booleanValue(a));
}
- }
- final SqlKind negateKind = a.getKind().negate();
- if (a.getKind() != negateKind) {
- return simplify(
- rexBuilder.makeCall(RexUtil.op(negateKind), ((RexCall) a).getOperands()),
- unknownAs);
- }
- final SqlKind negateKind2 = a.getKind().negateNullSafe();
- if (a.getKind() != negateKind2) {
- return simplify(
- rexBuilder.makeCall(RexUtil.op(negateKind2), ((RexCall) a).getOperands()),
- unknownAs);
- }
- if (a.getKind() == SqlKind.AND) {
- // NOT distributivity for AND
- final List<RexNode> newOperands = new ArrayList<>();
- for (RexNode operand : ((RexCall) a).getOperands()) {
- newOperands.add(
- simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, operand), unknownAs));
- }
- return simplify(rexBuilder.makeCall(SqlStdOperatorTable.OR, newOperands), unknownAs);
- }
- if (a.getKind() == SqlKind.OR) {
- // NOT distributivity for OR
- final List<RexNode> newOperands = new ArrayList<>();
- for (RexNode operand : ((RexCall) a).getOperands()) {
- newOperands.add(
- simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, operand), unknownAs));
- }
- return simplify(rexBuilder.makeCall(SqlStdOperatorTable.AND, newOperands), unknownAs);
- }
- if (a.getKind() == SqlKind.CASE) {
- final List<RexNode> newOperands = new ArrayList<>();
- List<RexNode> operands = ((RexCall) a).getOperands();
- for (int i = 0; i < operands.size(); i += 2) {
- if (i + 1 == operands.size()) {
- newOperands.add(rexBuilder.makeCall(SqlStdOperatorTable.NOT, operands.get(i)));
- } else {
- newOperands.add(operands.get(i));
- newOperands.add(
- rexBuilder.makeCall(SqlStdOperatorTable.NOT, operands.get(i + 1)));
+ break;
+
+ case AND:
+ // NOT distributivity for AND
+ newOperands = new ArrayList<>();
+ for (RexNode operand : ((RexCall) a).getOperands()) {
+ newOperands.add(simplify(not(operand), unknownAs));
+ }
+ return simplify(
+ rexBuilder.makeCall(SqlStdOperatorTable.OR, newOperands), unknownAs);
+
+ case OR:
+ // NOT distributivity for OR
+ newOperands = new ArrayList<>();
+ for (RexNode operand : ((RexCall) a).getOperands()) {
+ newOperands.add(simplify(not(operand), unknownAs));
+ }
+ return simplify(
+ rexBuilder.makeCall(SqlStdOperatorTable.AND, newOperands), unknownAs);
+
+ case CASE:
+ newOperands = new ArrayList<>();
+ List<RexNode> operands = ((RexCall) a).getOperands();
+ for (int i = 0; i < operands.size(); i += 2) {
+ if (i + 1 == operands.size()) {
+ newOperands.add(not(operands.get(i)));
+ } else {
+ newOperands.add(operands.get(i));
+ newOperands.add(not(operands.get(i + 1)));
+ }
+ }
+ return simplify(
+ rexBuilder.makeCall(SqlStdOperatorTable.CASE, newOperands), unknownAs);
+
+ case IN:
+ case NOT_IN:
+ // do not try to negate
+ break;
+
+ default:
+ final SqlKind negateKind = a.getKind().negate();
+ if (a.getKind() != negateKind) {
+ return simplify(
+ rexBuilder.makeCall(
+ RexUtil.op(negateKind), ((RexCall) a).getOperands()),
+ unknownAs);
+ }
+ final SqlKind negateKind2 = a.getKind().negateNullSafe();
+ if (a.getKind() != negateKind2) {
+ return simplify(
+ rexBuilder.makeCall(
+ RexUtil.op(negateKind2), ((RexCall) a).getOperands()),
+ unknownAs);
}
- }
- return simplify(rexBuilder.makeCall(SqlStdOperatorTable.CASE, newOperands), unknownAs);
}
+
RexNode a2 = simplify(a, unknownAs.negate());
if (a == a2) {
return call;
}
- if (a2.isAlwaysTrue()) {
- return rexBuilder.makeLiteral(false);
- }
- if (a2.isAlwaysFalse()) {
- return rexBuilder.makeLiteral(true);
- }
- return rexBuilder.makeCall(SqlStdOperatorTable.NOT, a2);
+ return not(a2);
}
private RexNode simplifyUnaryMinus(RexCall call, RexUnknownAs unknownAs) {
@@ -705,14 +781,14 @@ public class RexSimplify {
return simplify(call.getOperands().get(0), unknownAs);
}
- private @Nonnull RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) {
+ private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) {
final SqlKind kind = call.getKind();
final RexNode a = call.getOperands().get(0);
final RexNode simplified = simplifyIs1(kind, a, unknownAs);
return simplified == null ? call : simplified;
}
- private RexNode simplifyIs1(SqlKind kind, RexNode a, RexUnknownAs unknownAs) {
+ private @Nullable RexNode simplifyIs1(SqlKind kind, RexNode a, RexUnknownAs unknownAs) {
// UnknownAs.FALSE corresponds to x IS TRUE evaluation
// UnknownAs.TRUE to x IS NOT FALSE
// Note that both UnknownAs.TRUE and UnknownAs.FALSE only changes the meaning of Unknown
@@ -725,13 +801,13 @@ public class RexSimplify {
return simplify(a, unknownAs);
}
if (kind == SqlKind.IS_FALSE && unknownAs == RexUnknownAs.FALSE) {
- return simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, a), unknownAs);
+ return simplify(not(a), unknownAs);
}
if (kind == SqlKind.IS_NOT_FALSE && unknownAs == RexUnknownAs.TRUE) {
return simplify(a, unknownAs);
}
if (kind == SqlKind.IS_NOT_TRUE && unknownAs == RexUnknownAs.TRUE) {
- return simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, a), unknownAs);
+ return simplify(not(a), unknownAs);
}
final RexNode pred = simplifyIsPredicate(kind, a);
if (pred != null) {
@@ -741,7 +817,7 @@ public class RexSimplify {
return simplifyIs2(kind, a, unknownAs);
}
- private RexNode simplifyIsPredicate(SqlKind kind, RexNode a) {
+ private @Nullable RexNode simplifyIsPredicate(SqlKind kind, RexNode a) {
if (!RexUtil.isReferenceOrAccess(a, true)) {
return null;
}
@@ -758,7 +834,7 @@ public class RexSimplify {
return null;
}
- private RexNode simplifyIs2(SqlKind kind, RexNode a, RexUnknownAs unknownAs) {
+ private @Nullable RexNode simplifyIs2(SqlKind kind, RexNode a, RexUnknownAs unknownAs) {
final RexNode simplified;
switch (kind) {
case IS_NULL:
@@ -777,32 +853,40 @@ public class RexSimplify {
return simplified;
}
break;
+
case IS_TRUE:
- case IS_NOT_FALSE:
// x IS TRUE ==> x (if x is not nullable)
+ if (predicates.isEffectivelyNotNull(a)) {
+ return simplify(a, unknownAs);
+ }
+ simplified = simplify(a, RexUnknownAs.FALSE);
+ if (simplified == a) {
+ return null;
+ }
+ return isTrue(simplified);
+
+ case IS_NOT_FALSE:
// x IS NOT FALSE ==> x (if x is not nullable)
if (predicates.isEffectivelyNotNull(a)) {
return simplify(a, unknownAs);
- } else {
- RexNode newSub =
- simplify(
- a,
- kind == SqlKind.IS_TRUE
- ? RexUnknownAs.FALSE
- : RexUnknownAs.TRUE);
- if (newSub == a) {
- return null;
- }
- return rexBuilder.makeCall(RexUtil.op(kind), ImmutableList.of(newSub));
}
+ simplified = simplify(a, RexUnknownAs.TRUE);
+ if (simplified == a) {
+ return null;
+ }
+ return isNotFalse(simplified);
+
case IS_FALSE:
case IS_NOT_TRUE:
// x IS NOT TRUE ==> NOT x (if x is not nullable)
// x IS FALSE ==> NOT x (if x is not nullable)
if (predicates.isEffectivelyNotNull(a)) {
- return simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, a), unknownAs);
+ return simplify(not(a), unknownAs);
}
break;
+
+ default:
+ break;
}
switch (a.getKind()) {
case NOT:
@@ -815,6 +899,8 @@ public class RexSimplify {
final SqlOperator notKind = RexUtil.op(kind.negateNullSafe());
final RexNode arg = ((RexCall) a).operands.get(0);
return simplify(rexBuilder.makeCall(notKind, arg), UNKNOWN);
+ default:
+ break;
}
final RexNode a2 = simplify(a, UNKNOWN);
if (a != a2) {
@@ -823,7 +909,7 @@ public class RexSimplify {
return null; // cannot be simplified
}
- private RexNode simplifyIsNotNull(RexNode a) {
+ private @Nullable RexNode simplifyIsNotNull(RexNode a) {
// Simplify the argument first,
// call ourselves recursively to see whether we can make more progress.
// For example, given
@@ -871,7 +957,7 @@ public class RexSimplify {
}
}
- private RexNode simplifyIsNull(RexNode a) {
+ private @Nullable RexNode simplifyIsNull(RexNode a) {
// Simplify the argument first,
// call ourselves recursively to see whether we can make more progress.
// For example, given
@@ -915,7 +1001,7 @@ public class RexSimplify {
* @param rexNode Rex node to validate the strong policy
* @throws AssertionError If the validation fails
*/
- private void validateStrongPolicy(RexNode rexNode) {
+ private static void validateStrongPolicy(RexNode rexNode) {
if (hasCustomNullabilityRules(rexNode.getKind())) {
return;
}
@@ -934,6 +1020,9 @@ public class RexSimplify {
.map(RexNode::getType)
.noneMatch(RelDataType::isNullable);
}
+ break;
+ default:
+ break;
}
}
@@ -947,7 +1036,7 @@ public class RexSimplify {
* @param sqlKind Sql kind to check
* @return {@code true} if specified {@link SqlKind} has custom nullability rules
*/
- private boolean hasCustomNullabilityRules(SqlKind sqlKind) {
+ private static boolean hasCustomNullabilityRules(SqlKind sqlKind) {
switch (sqlKind) {
case CAST:
case ITEM:
@@ -1085,7 +1174,7 @@ public class RexSimplify {
* If boolean is true, simplify cond in input branch and return new branch. Otherwise, simply
* return input branch.
*/
- private CaseBranch generateBranch(
+ private static CaseBranch generateBranch(
boolean simplifyCond, RexSimplify simplifier, CaseBranch branch) {
if (simplifyCond) {
// the previous branch was merged, time to simplify it and
@@ -1148,6 +1237,7 @@ public class RexSimplify {
enum SafeRexVisitor implements RexVisitor<Boolean> {
INSTANCE;
+ @SuppressWarnings("ImmutableEnumChecker")
private final Set<SqlKind> safeOps;
SafeRexVisitor() {
@@ -1264,29 +1354,24 @@ public class RexSimplify {
return r.accept(SafeRexVisitor.INSTANCE);
}
- private static RexNode simplifyBooleanCase(
+ private @Nullable RexNode simplifyBooleanCase(
RexBuilder rexBuilder,
List<CaseBranch> inputBranches,
- RexUnknownAs unknownAs,
+ @SuppressWarnings("unused") RexUnknownAs unknownAs,
RelDataType branchType) {
RexNode result;
// prepare all condition/branches for boolean interpretation
// It's done here make these interpretation changes available to case2or simplifications
- // but not interfere with the normal simplifcation recursion
+ // but not interfere with the normal simplification recursion
List<CaseBranch> branches = new ArrayList<>();
for (CaseBranch branch : inputBranches) {
if ((branches.size() > 0 && !isSafeExpression(branch.cond))
|| !isSafeExpression(branch.value)) {
return null;
}
- RexNode cond;
- RexNode value;
- if (branch.cond.getType().isNullable()) {
- cond = rexBuilder.makeCall(SqlStdOperatorTable.IS_TRUE, branch.cond);
- } else {
- cond = branch.cond;
- }
+ final RexNode cond = isTrue(branch.cond);
+ final RexNode value;
if (!branchType.equals(branch.value.getType())) {
value = rexBuilder.makeAbstractCast(branchType, branch.value);
} else {
@@ -1371,7 +1456,7 @@ public class RexSimplify {
operands.forEach(t -> sargCollector.accept(t, terms));
if (sargCollector.needToFix()) {
operands.clear();
- terms.forEach(t -> operands.add(sargCollector.fix(rexBuilder, t)));
+ terms.forEach(t -> operands.add(SargCollector.fix(rexBuilder, t, unknownAs)));
}
terms.clear();
@@ -1382,6 +1467,8 @@ public class RexSimplify {
switch (unknownAs) {
case FALSE:
return simplifyAnd2ForUnknownAsFalse(terms, notTerms, Comparable.class);
+ default:
+ break;
}
return simplifyAnd2(terms, notTerms);
}
@@ -1442,9 +1529,7 @@ public class RexSimplify {
}
// Add the NOT disjunctions back in.
for (RexNode notDisjunction : notTerms) {
- terms.add(
- simplify(
- rexBuilder.makeCall(SqlStdOperatorTable.NOT, notDisjunction), UNKNOWN));
+ terms.add(simplify(not(notDisjunction), UNKNOWN));
}
return RexUtil.composeConjunction(rexBuilder, terms);
}
@@ -1592,6 +1677,9 @@ public class RexSimplify {
if (comparison != null
&& comparison.kind != SqlKind.NOT_EQUALS) { // not supported yet
final C constant = comparison.literal.getValueAs(clazz);
+ if (constant == null) {
+ break;
+ }
final RexNode result =
processRange(
rexBuilder,
@@ -1620,6 +1708,9 @@ public class RexSimplify {
break;
case IS_NULL:
nullOperands.add(((RexCall) term).getOperands().get(0));
+ break;
+ default:
+ break;
}
}
// If one column should be null and is in a comparison predicate,
@@ -1679,7 +1770,7 @@ public class RexSimplify {
}
// Add the NOT disjunctions back in.
for (RexNode notDisjunction : notTerms) {
- terms.add(rexBuilder.makeCall(SqlStdOperatorTable.NOT, notDisjunction));
+ terms.add(not(notDisjunction));
}
// The negated terms: only deterministic expressions
for (RexNode negatedTerm : negatedTerms) {
@@ -1690,6 +1781,7 @@ public class RexSimplify {
return RexUtil.composeConjunction(rexBuilder, terms);
}
+ @SuppressWarnings("BetaApi")
private <C extends Comparable<C>> RexNode simplifyUsingPredicates(RexNode e, Class<C> clazz) {
if (predicates.pulledUpPredicates.isEmpty()) {
return e;
@@ -1702,6 +1794,9 @@ public class RexSimplify {
}
final C v0 = comparison.literal.getValueAs(clazz);
+ if (v0 == null) {
+ return e;
+ }
final RangeSet<C> rangeSet = rangeSet(comparison.kind, v0);
final RangeSet<C> rangeSet2 =
residue(comparison.ref, rangeSet, predicates.pulledUpPredicates, clazz);
@@ -1750,7 +1845,8 @@ public class RexSimplify {
* <li>{@code residue($0 < 10, [$0 < 20, $0 > 0])} returns {@code $0 < 10}
* </ul>
*/
- private <C extends Comparable<C>> RangeSet<C> residue(
+ @SuppressWarnings("BetaApi")
+ private static <C extends Comparable<C>> RangeSet<C> residue(
RexNode ref, RangeSet<C> r0, List<RexNode> predicates, Class<C> clazz) {
RangeSet<C> result = r0;
for (RexNode predicate : predicates) {
@@ -1766,6 +1862,7 @@ public class RexSimplify {
&& call.operands.get(1) instanceof RexLiteral) {
final RexLiteral literal = (RexLiteral) call.operands.get(1);
final C c1 = literal.getValueAs(clazz);
+ assert c1 != null : "value must not be null in " + literal;
switch (predicate.getKind()) {
case NOT_EQUALS:
// We want to intersect result with the range set of everything but
@@ -1793,6 +1890,9 @@ public class RexSimplify {
break; // short-cut
}
}
+ break;
+ default:
+ break;
}
}
return result;
@@ -1845,7 +1945,7 @@ public class RexSimplify {
terms.forEach(t -> sargCollector.accept(t, newTerms));
if (sargCollector.needToFix()) {
terms.clear();
- newTerms.forEach(t -> terms.add(sargCollector.fix(rexBuilder, t)));
+ newTerms.forEach(t -> terms.add(SargCollector.fix(rexBuilder, t, unknownAs)));
}
// CALCITE-3198 Auxiliary map to simplify cases like:
@@ -1896,9 +1996,13 @@ public class RexSimplify {
final Comparable comparable1 =
notEqualsComparison.literal.getValue();
final Comparable comparable2 =
- Comparison.of(prevNotEquals).literal.getValue();
+ castNonNull(Comparison.of(prevNotEquals))
+ .literal
+ .getValue();
//noinspection unchecked
- if (comparable1.compareTo(comparable2) != 0) {
+ if (comparable1 != null
+ && comparable2 != null
+ && comparable1.compareTo(comparable2) != 0) {
// X <> A OR X <> B => X IS NOT NULL OR NULL
final RexNode isNotNull =
rexBuilder.makeCall(
@@ -1927,6 +2031,8 @@ public class RexSimplify {
}
}
break;
+ default:
+ break;
}
}
return RexUtil.composeDisjunction(rexBuilder, terms);
@@ -1962,7 +2068,7 @@ public class RexSimplify {
for (Map<RexNode, Comparable> map : foo0.assignments()) {
for (RexNode predicate : predicates.pulledUpPredicates) {
final Comparable v = RexInterpreter.evaluate(predicate, map);
- if (!v.equals(true)) {
+ if (!Boolean.TRUE.equals(v)) {
continue assignment_loop;
}
}
@@ -1984,17 +2090,22 @@ public class RexSimplify {
if (v1 == NullSentinel.INSTANCE) {
v1 = unknownAs.toBoolean();
}
+ break;
+ default:
+ break;
}
}
if (!v0.equals(v1)) {
throw new AssertionError(
- "result mismatch: when applied to "
+ "result mismatch (unknown as "
+ + unknownAs
+ + "): when applied to "
+ map
- + ", "
+ + ",\n"
+ before
+ " yielded "
+ v0
- + ", and "
+ + ";\n"
+ simplified
+ " yielded "
+ v1);
@@ -2007,15 +2118,25 @@ public class RexSimplify {
final RexNode a = call.getOperands().get(0);
if (call.getOperands().get(1) instanceof RexLiteral) {
RexLiteral literal = (RexLiteral) call.getOperands().get(1);
- final Sarg sarg = literal.getValueAs(Sarg.class);
- if (sarg.containsNull) {
+ final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class));
+ if (sarg.isAll() || sarg.isNone()) {
+ return RexUtil.simpleSarg(rexBuilder, a, sarg, unknownAs);
+ }
+ // Remove null from sarg if the left-hand side is never null
+ if (sarg.nullAs != UNKNOWN) {
final RexNode simplified = simplifyIs1(SqlKind.IS_NULL, a, unknownAs);
if (simplified != null && simplified.isAlwaysFalse()) {
- final Sarg sarg2 = Sarg.of(false, sarg.rangeSet);
+ final Sarg sarg2 = Sarg.of(UNKNOWN, sarg.rangeSet);
final RexLiteral literal2 =
rexBuilder.makeLiteral(sarg2, literal.getType(), literal.getTypeName());
- return call.clone(call.type, ImmutableList.of(a, literal2));
+ // Now we've strengthened the Sarg, try to simplify again
+ return simplifySearch(
+ call.clone(call.type, ImmutableList.of(a, literal2)), unknownAs);
}
+ } else if (sarg.isPoints() && sarg.pointCount <= 1) {
+ // Expand "SEARCH(x, Sarg([point])" to "x = point"
+ // and "SEARCH(x, Sarg([])" to "false"
+ return RexUtil.expandSearch(rexBuilder, null, call);
}
}
return call;
@@ -2093,13 +2214,17 @@ public class RexSimplify {
switch (e.getType().getSqlTypeName()) {
case TIMESTAMP:
return e;
+ default:
+ break;
}
break;
+ default:
+ break;
}
final List<RexNode> reducedValues = new ArrayList<>();
final RexNode simplifiedExpr = rexBuilder.makeCast(e.getType(), operand);
executor.reduce(rexBuilder, ImmutableList.of(simplifiedExpr), reducedValues);
- return Objects.requireNonNull(Iterables.getOnlyElement(reducedValues));
+ return requireNonNull(Iterables.getOnlyElement(reducedValues));
default:
if (operand == e.getOperands().get(0)) {
return e;
@@ -2175,6 +2300,8 @@ public class RexSimplify {
return outer == TimeUnit.YEAR;
}
return outer.ordinal() <= inner.ordinal();
+ default:
+ break;
}
break;
case QUARTER:
@@ -2188,7 +2315,12 @@ public class RexSimplify {
case MILLISECOND:
case MICROSECOND:
return true;
+ default:
+ break;
}
+ break;
+ default:
+ break;
}
return false;
}
@@ -2202,7 +2334,7 @@ public class RexSimplify {
return RexUtil.removeNullabilityCast(rexBuilder.getTypeFactory(), e);
}
- private static <C extends Comparable<C>> RexNode processRange(
+ private static <C extends Comparable<C>> @Nullable RexNode processRange(
RexBuilder rexBuilder,
List<RexNode> terms,
Map<RexNode, Pair<Range<C>, List<RexNode>>> rangeTerms,
@@ -2437,6 +2569,7 @@ public class RexSimplify {
}
}
+ @SuppressWarnings("BetaApi")
private static <C extends Comparable<C>> RangeSet<C> rangeSet(SqlKind comparison, C c) {
switch (comparison) {
case EQUALS:
@@ -2458,7 +2591,7 @@ public class RexSimplify {
/** Marker interface for predicates (expressions that evaluate to BOOLEAN). */
private interface Predicate {
/** Wraps an expression in a Predicate or returns null. */
- static Predicate of(RexNode t) {
+ static @Nullable Predicate of(RexNode t) {
final Predicate p = Comparison.of(t);
if (p != null) {
return p;
@@ -2483,16 +2616,16 @@ public class RexSimplify {
final RexLiteral literal;
private Comparison(RexNode ref, SqlKind kind, RexLiteral literal) {
- this.ref = Objects.requireNonNull(ref);
- this.kind = Objects.requireNonNull(kind);
- this.literal = Objects.requireNonNull(literal);
+ this.ref = requireNonNull(ref, "ref");
+ this.kind = requireNonNull(kind, "kind");
+ this.literal = requireNonNull(literal, "literal");
}
/**
* Creates a comparison, between a {@link RexInputRef} or {@link RexFieldAccess} or
* deterministic {@link RexCall} and a literal.
*/
- static Comparison of(RexNode e) {
+ static @Nullable Comparison of(RexNode e) {
return of(
e,
node ->
@@ -2501,7 +2634,8 @@ public class RexSimplify {
}
/** Creates a comparison, or returns null. */
- static Comparison of(RexNode e, java.util.function.Predicate<RexNode> nodePredicate) {
+ static @Nullable Comparison of(
+ RexNode e, java.util.function.Predicate<RexNode> nodePredicate) {
switch (e.getKind()) {
case EQUALS:
case NOT_EQUALS:
@@ -2517,6 +2651,9 @@ public class RexSimplify {
if (nodePredicate.test(left)) {
return new Comparison(left, e.getKind(), (RexLiteral) right);
}
+ break;
+ default:
+ break;
}
switch (left.getKind()) {
case LITERAL:
@@ -2524,7 +2661,13 @@ public class RexSimplify {
return new Comparison(
right, e.getKind().reverse(), (RexLiteral) left);
}
+ break;
+ default:
+ break;
}
+ break;
+ default:
+ break;
}
return null;
}
@@ -2541,12 +2684,12 @@ public class RexSimplify {
final SqlKind kind;
private IsPredicate(RexNode ref, SqlKind kind) {
- this.ref = Objects.requireNonNull(ref);
- this.kind = Objects.requireNonNull(kind);
+ this.ref = requireNonNull(ref, "ref");
+ this.kind = requireNonNull(kind, "kind");
}
/** Creates an IS predicate, or returns null. */
- static IsPredicate of(RexNode e) {
+ static @Nullable IsPredicate of(RexNode e) {
switch (e.getKind()) {
case IS_NULL:
case IS_NOT_NULL:
@@ -2555,6 +2698,8 @@ public class RexSimplify {
return null;
}
return new IsPredicate(pA, e.getKind());
+ default:
+ break;
}
return null;
}
@@ -2606,7 +2751,7 @@ public class RexSimplify {
* @param predicates Filter condition predicates
* @return simplified conjunction of predicates for the filter, null if always false
*/
- public RexNode simplifyFilterPredicates(Iterable<? extends RexNode> predicates) {
+ public @Nullable RexNode simplifyFilterPredicates(Iterable<? extends RexNode> predicates) {
final RexNode simplifiedAnds =
withPredicateElimination(Bug.CALCITE_2401_FIXED)
.simplifyUnknownAsFalse(RexUtil.composeConjunction(rexBuilder, predicates));
@@ -2628,6 +2773,7 @@ public class RexSimplify {
* <p>Returns whether the value was found.
*/
private static <E> boolean replaceLast(List<E> list, E oldVal, E newVal) {
+ @SuppressWarnings("argument.type.incompatible")
final int index = list.lastIndexOf(oldVal);
if (index < 0) {
return false;
@@ -2641,7 +2787,6 @@ public class RexSimplify {
final Map<RexNode, RexSargBuilder> map = new HashMap<>();
private final RexBuilder rexBuilder;
private final boolean negate;
-
/**
* Count of the new terms after converting all the operands to {@code SEARCH} on a {@link
* Sarg}. It is used to decide whether the new terms are simpler.
@@ -2674,9 +2819,12 @@ public class RexSimplify {
((RexCall) e).operands.get(1),
e.getKind(),
newTerms);
- // CHANGED: we remove IS_NULL here
- // because SEARCH operator in Calcite 1.26 handles UNKNOWNs incorrectly
- // see CALCITE-4446
+ /* ----- FLINK MODIFICATION BEGIN -----
+ case IS_NULL:
+ case IS_NOT_NULL:
+ final RexNode arg = ((RexCall) e).operands.get(0);
+ return accept1(arg, e.getKind(), newTerms);
+ ----- FLINK MODIFICATION END ----- */
default:
return false;
}
@@ -2688,16 +2836,22 @@ public class RexSimplify {
case FIELD_ACCESS:
switch (right.getKind()) {
case LITERAL:
- return accept1(left, kind, (RexLiteral) right, newTerms);
+ return accept2b(left, kind, (RexLiteral) right, newTerms);
+ default:
+ break;
}
return false;
case LITERAL:
switch (right.getKind()) {
case INPUT_REF:
case FIELD_ACCESS:
- return accept1(right, kind.reverse(), (RexLiteral) left, newTerms);
+ return accept2b(right, kind.reverse(), (RexLiteral) left, newTerms);
+ default:
+ break;
}
return false;
+ default:
+ break;
}
return false;
}
@@ -2708,8 +2862,32 @@ public class RexSimplify {
}
// always returns true
- private boolean accept1(
- RexNode e, SqlKind kind, @Nonnull RexLiteral literal, List<RexNode> newTerms) {
+ private boolean accept1(RexNode e, SqlKind kind, List<RexNode> newTerms) {
+ final RexSargBuilder b =
+ map.computeIfAbsent(
+ e,
+ e2 -> addFluent(newTerms, new RexSargBuilder(e2, rexBuilder, negate)));
+ switch (negate ? kind.negate() : kind) {
+ case IS_NULL:
+ b.nullAs = b.nullAs.or(TRUE);
+ return true;
+ case IS_NOT_NULL:
+ b.nullAs = b.nullAs.or(FALSE);
+ b.addAll();
+ return true;
+ default:
+ throw new AssertionError("unexpected " + kind);
+ }
+ }
+
+ private boolean accept2b(
+ RexNode e, SqlKind kind, RexLiteral literal, List<RexNode> newTerms) {
+ if (literal.getValue() == null) {
+ // Cannot include expressions 'x > NULL' in a Sarg. Comparing to a NULL
+ // literal is a daft thing to do, because it always returns UNKNOWN. It
+ // is better handled by other simplifications.
+ return false;
+ }
final RexSargBuilder b =
map.computeIfAbsent(
e,
@@ -2717,7 +2895,7 @@ public class RexSimplify {
if (negate) {
kind = kind.negateNullSafe();
}
- final Comparable value = literal.getValueAs(Comparable.class);
+ final Comparable value = requireNonNull(literal.getValueAs(Comparable.class), "value");
switch (kind) {
case LESS_THAN:
b.addRange(Range.lessThan(value), literal.getType());
@@ -2739,12 +2917,9 @@ public class RexSimplify {
b.addRange(Range.greaterThan(value), literal.getType());
return true;
case SEARCH:
- final Sarg sarg = literal.getValueAs(Sarg.class);
+ final Sarg sarg = (Sarg) value;
b.addSarg(sarg, negate, literal.getType());
return true;
- // CHANGED: we remove IS_NULL here
- // because SEARCH operator in Calcite 1.26 handles UNKNOWNs incorrectly
- // see CALCITE-4446
default:
throw new AssertionError("unexpected " + kind);
}
@@ -2754,35 +2929,43 @@ public class RexSimplify {
boolean needToFix() {
// Fix and converts to SEARCH if:
// 1. A Sarg has complexity greater than 1;
- // 2. The terms are reduced as simpler Sarg points.
+ // 2. The terms are reduced as simpler Sarg points;
+ // 3. The terms are reduced as simpler Sarg comparison.
- // Initialize "allPoints" with "newTermsCount == 1"
- // because "newTermsCount == 1" is a precondition.
- boolean allPoints = newTermsCount == 1;
- for (RexSargBuilder builder : map.values()) {
- if (builder.complexity() > 1) {
- return true;
- }
- if (allPoints) {
- Sarg sarg = builder.build(negate);
- allPoints = sarg.isPoints();
- }
- }
- return allPoints;
+ // Ignore 'negate' just to be compatible with previous versions of this
+ // method. "build().complexity()" would be a better estimate, if we could
+ // switch to it breaking lots of plans.
+ final Collection<RexSargBuilder> builders = map.values();
+ return builders.stream().anyMatch(b -> b.build(false).complexity() > 1)
+ || newTermsCount == 1 && builders.stream().allMatch(b -> simpleSarg(b.build()));
+ }
+
+ /**
+ * Returns whether this Sarg can be expanded to more simple form, e.g. the IN call or single
+ * comparison.
+ */
+ private static boolean simpleSarg(Sarg sarg) {
+ return sarg.isPoints() || RangeSets.isOpenInterval(sarg.rangeSet);
}
/**
* If a term is a call to {@code SEARCH} on a {@link RexSargBuilder}, converts it to a
* {@code SEARCH} on a {@link Sarg}.
*/
- RexNode fix(RexBuilder rexBuilder, RexNode term) {
+ static RexNode fix(RexBuilder rexBuilder, RexNode term, RexUnknownAs unknownAs) {
if (term instanceof RexSargBuilder) {
- RexSargBuilder sargBuilder = (RexSargBuilder) term;
+ final RexSargBuilder sargBuilder = (RexSargBuilder) term;
+ final Sarg sarg = sargBuilder.build();
+ if (sarg.complexity() <= 1 && simpleSarg(sarg)) {
+ // Expand small sargs into comparisons in order to avoid plan changes
+ // and better readability.
+ return RexUtil.sargRef(
+ rexBuilder, sargBuilder.ref, sarg, term.getType(), unknownAs);
+ }
return rexBuilder.makeCall(
SqlStdOperatorTable.SEARCH,
sargBuilder.ref,
- rexBuilder.makeSearchArgumentLiteral(
- sargBuilder.build(negate), term.getType()));
+ rexBuilder.makeSearchArgumentLiteral(sarg, term.getType()));
}
return term;
}
@@ -2793,55 +2976,60 @@ public class RexSimplify {
* Sarg can be expanded as {@link SargCollector} traverses a list of OR or AND terms.
*
* <p>The {@link SargCollector#fix} method converts it to an immutable literal.
+ *
+ * <p>The {@link #nullAs} field will become {@link Sarg#nullAs}, as follows:
+ *
+ * <ul>
+ * <li>If there is at least one term that returns TRUE when the argument is NULL, then the
+ * overall value will be TRUE; failing that,
+ * <li>if there is at least one term that returns UNKNOWN when the argument is NULL, then the
+ * overall value will be UNKNOWN; failing that,
+ * <li>the value will be FALSE.
+ * </ul>
+ *
+ * <p>This is analogous to the behavior of OR in three-valued logic: {@code TRUE OR UNKNOWN OR
+ * FALSE} returns {@code TRUE}; {@code UNKNOWN OR FALSE OR UNKNOWN} returns {@code UNKNOWN};
+ * {@code FALSE OR FALSE} returns {@code FALSE}.
*/
- static class RexSargBuilder extends RexNode {
+ @SuppressWarnings("BetaApi")
+ private static class RexSargBuilder extends RexNode {
final RexNode ref;
- private final RexBuilder rexBuilder;
- private List<RelDataType> types = new ArrayList<>();
+ final RexBuilder rexBuilder;
+ final boolean negate;
+ final List<RelDataType> types = new ArrayList<>();
final RangeSet<Comparable> rangeSet = TreeRangeSet.create();
- boolean containsNull;
+ RexUnknownAs nullAs = FALSE;
RexSargBuilder(RexNode ref, RexBuilder rexBuilder, boolean negate) {
- this.ref = Objects.requireNonNull(ref);
- this.rexBuilder = Objects.requireNonNull(rexBuilder);
- this.containsNull = false;
+ this.ref = requireNonNull(ref, "ref");
+ this.rexBuilder = requireNonNull(rexBuilder, "rexBuilder");
+ this.negate = negate;
}
@Override
public String toString() {
- return "SEARCH(" + ref + ", " + rangeSet + (containsNull ? " + null)" : ")");
+ return "SEARCH("
+ + ref
+ + ", "
+ + (negate ? "NOT " : "")
+ + rangeSet
+ + "; NULL AS "
+ + nullAs
+ + ")";
}
- /**
- * Returns a rough estimate of whether it is worth converting to a Sarg.
- *
- * <p>Examples:
- *
- * <ul>
- * <li>{@code x = 1}, {@code x <> 1}, {@code x > 1} have complexity 1
- * <li>{@code x > 1 or x is null} has complexity 2
- * <li>{@code x in (2, 4, 6) or x > 20} has complexity 4
- * </ul>
- */
- int complexity() {
- int complexity = 0;
- if (rangeSet.asRanges().size() == 2
- && rangeSet.complement().asRanges().size() == 1
- && RangeSets.isPoint(
- Iterables.getOnlyElement(rangeSet.complement().asRanges()))) {
- complexity++;
- } else {
- complexity += rangeSet.asRanges().size();
- }
- if (containsNull) {
- ++complexity;
- }
- return complexity;
+ <C extends Comparable<C>> Sarg<C> build() {
+ return build(negate);
}
+ @SuppressWarnings({"rawtypes", "unchecked", "UnstableApiUsage"})
<C extends Comparable<C>> Sarg<C> build(boolean negate) {
- final RangeSet rangeSet = negate ? this.rangeSet.complement() : this.rangeSet;
- return Sarg.of(containsNull, rangeSet);
+ final RangeSet<C> r = (RangeSet) this.rangeSet;
+ if (negate) {
+ return Sarg.of(nullAs.negate(), r.complement());
+ } else {
+ return Sarg.of(nullAs, r);
+ }
}
@Override
@@ -2851,7 +3039,7 @@ public class RexSimplify {
return ref.getType();
}
final List<RelDataType> distinctTypes = Util.distinctList(this.types);
- return Objects.requireNonNull(
+ return requireNonNull(
rexBuilder.typeFactory.leastRestrictive(distinctTypes),
() -> "Can't find leastRestrictive type among " + distinctTypes);
}
@@ -2867,7 +3055,7 @@ public class RexSimplify {
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
throw new UnsupportedOperationException();
}
@@ -2876,15 +3064,40 @@ public class RexSimplify {
throw new UnsupportedOperationException();
}
+ void addAll() {
+ rangeSet.add(Range.all());
+ }
+
void addRange(Range<Comparable> range, RelDataType type) {
types.add(type);
rangeSet.add(range);
+ nullAs = nullAs.or(UNKNOWN);
}
+ @SuppressWarnings({"UnstableApiUsage", "rawtypes", "unchecked"})
void addSarg(Sarg sarg, boolean negate, RelDataType type) {
+ final RangeSet r;
+ final RexUnknownAs nullAs;
+ if (negate) {
+ r = sarg.rangeSet.complement();
+ nullAs = sarg.nullAs.negate();
+ } else {
+ r = sarg.rangeSet;
+ nullAs = sarg.nullAs;
+ }
types.add(type);
- rangeSet.addAll(negate ? sarg.rangeSet.complement() : sarg.rangeSet);
- containsNull |= sarg.containsNull;
+ rangeSet.addAll(r);
+ switch (nullAs) {
+ case TRUE:
+ this.nullAs = this.nullAs.or(TRUE);
+ break;
+ case FALSE:
+ this.nullAs = this.nullAs.or(FALSE);
+ break;
+ case UNKNOWN:
+ this.nullAs = this.nullAs.or(UNKNOWN);
+ break;
+ }
}
}
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlMatchRecognize.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlMatchRecognize.java
deleted file mode 100644
index e20675b0d93..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlMatchRecognize.java
+++ /dev/null
@@ -1,428 +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;
-
-import com.google.common.base.Preconditions;
-import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.util.SqlBasicVisitor;
-import org.apache.calcite.sql.util.SqlVisitor;
-import org.apache.calcite.sql.validate.SqlValidator;
-import org.apache.calcite.sql.validate.SqlValidatorScope;
-import org.apache.calcite.util.ImmutableNullableList;
-
-import javax.annotation.Nonnull;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * SqlNode for MATCH_RECOGNIZE clause.
- *
- * <p>The class was copied over because of CALCITE-4390. The changed lines are: 110-112
- */
-public class SqlMatchRecognize extends SqlCall {
- public static final int OPERAND_TABLE_REF = 0;
- public static final int OPERAND_PATTERN = 1;
- public static final int OPERAND_STRICT_START = 2;
- public static final int OPERAND_STRICT_END = 3;
- public static final int OPERAND_PATTERN_DEFINES = 4;
- public static final int OPERAND_MEASURES = 5;
- public static final int OPERAND_AFTER = 6;
- public static final int OPERAND_SUBSET = 7;
- public static final int OPERAND_ROWS_PER_MATCH = 8;
- public static final int OPERAND_PARTITION_BY = 9;
- public static final int OPERAND_ORDER_BY = 10;
- public static final int OPERAND_INTERVAL = 11;
-
- public static final SqlPrefixOperator SKIP_TO_FIRST =
- new SqlPrefixOperator("SKIP TO FIRST", SqlKind.SKIP_TO_FIRST, 20, null, null, null);
-
- public static final SqlPrefixOperator SKIP_TO_LAST =
- new SqlPrefixOperator("SKIP TO LAST", SqlKind.SKIP_TO_LAST, 20, null, null, null);
-
- // ~ Instance fields -------------------------------------------
-
- private SqlNode tableRef;
- private SqlNode pattern;
- private SqlLiteral strictStart;
- private SqlLiteral strictEnd;
- private SqlNodeList patternDefList;
- private SqlNodeList measureList;
- private SqlNode after;
- private SqlNodeList subsetList;
- private SqlLiteral rowsPerMatch;
- private SqlNodeList partitionList;
- private SqlNodeList orderList;
- private SqlLiteral interval;
-
- /** Creates a SqlMatchRecognize. */
- public SqlMatchRecognize(
- SqlParserPos pos,
- SqlNode tableRef,
- SqlNode pattern,
- SqlLiteral strictStart,
- SqlLiteral strictEnd,
- SqlNodeList patternDefList,
- SqlNodeList measureList,
- SqlNode after,
- SqlNodeList subsetList,
- SqlLiteral rowsPerMatch,
- SqlNodeList partitionList,
- SqlNodeList orderList,
- SqlLiteral interval) {
- super(pos);
- this.tableRef = Objects.requireNonNull(tableRef);
- this.pattern = Objects.requireNonNull(pattern);
- this.strictStart = strictStart;
- this.strictEnd = strictEnd;
- this.patternDefList = Objects.requireNonNull(patternDefList);
- Preconditions.checkArgument(patternDefList.size() > 0);
- this.measureList = Objects.requireNonNull(measureList);
- this.after = after;
- this.subsetList = subsetList;
- Preconditions.checkArgument(
- rowsPerMatch == null || rowsPerMatch.value instanceof RowsPerMatchOption);
- this.rowsPerMatch = rowsPerMatch;
- this.partitionList = Objects.requireNonNull(partitionList);
- this.orderList = Objects.requireNonNull(orderList);
- this.interval = interval;
- }
-
- // ~ Methods
-
- @Override
- public SqlOperator getOperator() {
- return SqlMatchRecognizeOperator.INSTANCE;
- }
-
- @Override
- public SqlKind getKind() {
- return SqlKind.MATCH_RECOGNIZE;
- }
-
- @Override
- public List<SqlNode> getOperandList() {
- // This method was changed to fix CALCITE-4390
- return ImmutableNullableList.of(
- tableRef,
- pattern,
- strictStart,
- strictEnd,
- patternDefList,
- measureList,
- after,
- subsetList,
- rowsPerMatch,
- partitionList,
- orderList,
- interval);
- }
-
- @Override
- public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
- getOperator().unparse(writer, this, 0, 0);
- }
-
- @Override
- public void validate(SqlValidator validator, SqlValidatorScope scope) {
- validator.validateMatchRecognize(this);
- }
-
- @Override
- public void setOperand(int i, SqlNode operand) {
- switch (i) {
- case OPERAND_TABLE_REF:
- tableRef = Objects.requireNonNull(operand);
- break;
- case OPERAND_PATTERN:
- pattern = operand;
- break;
- case OPERAND_STRICT_START:
- strictStart = (SqlLiteral) operand;
- break;
- case OPERAND_STRICT_END:
- strictEnd = (SqlLiteral) operand;
- break;
- case OPERAND_PATTERN_DEFINES:
- patternDefList = Objects.requireNonNull((SqlNodeList) operand);
- Preconditions.checkArgument(patternDefList.size() > 0);
- break;
- case OPERAND_MEASURES:
- measureList = Objects.requireNonNull((SqlNodeList) operand);
- break;
- case OPERAND_AFTER:
- after = operand;
- break;
- case OPERAND_SUBSET:
- subsetList = (SqlNodeList) operand;
- break;
- case OPERAND_ROWS_PER_MATCH:
- rowsPerMatch = (SqlLiteral) operand;
- Preconditions.checkArgument(
- rowsPerMatch == null || rowsPerMatch.value instanceof RowsPerMatchOption);
- break;
- case OPERAND_PARTITION_BY:
- partitionList = (SqlNodeList) operand;
- break;
- case OPERAND_ORDER_BY:
- orderList = (SqlNodeList) operand;
- break;
- case OPERAND_INTERVAL:
- interval = (SqlLiteral) operand;
- break;
- default:
- throw new AssertionError(i);
- }
- }
-
- @Nonnull
- public SqlNode getTableRef() {
- return tableRef;
- }
-
- public SqlNode getPattern() {
- return pattern;
- }
-
- public SqlLiteral getStrictStart() {
- return strictStart;
- }
-
- public SqlLiteral getStrictEnd() {
- return strictEnd;
- }
-
- @Nonnull
- public SqlNodeList getPatternDefList() {
- return patternDefList;
- }
-
- @Nonnull
- public SqlNodeList getMeasureList() {
- return measureList;
- }
-
- public SqlNode getAfter() {
- return after;
- }
-
- public SqlNodeList getSubsetList() {
- return subsetList;
- }
-
- public SqlLiteral getRowsPerMatch() {
- return rowsPerMatch;
- }
-
- public SqlNodeList getPartitionList() {
- return partitionList;
- }
-
- public SqlNodeList getOrderList() {
- return orderList;
- }
-
- public SqlLiteral getInterval() {
- return interval;
- }
-
- /** Options for {@code ROWS PER MATCH}. */
- public enum RowsPerMatchOption {
- ONE_ROW("ONE ROW PER MATCH"),
- ALL_ROWS("ALL ROWS PER MATCH");
-
- private final String sql;
-
- RowsPerMatchOption(String sql) {
- this.sql = sql;
- }
-
- @Override
- public String toString() {
- return sql;
- }
-
- public SqlLiteral symbol(SqlParserPos pos) {
- return SqlLiteral.createSymbol(this, pos);
- }
- }
-
- /** Options for {@code AFTER MATCH} clause. */
- public enum AfterOption implements Symbolizable {
- SKIP_TO_NEXT_ROW("SKIP TO NEXT ROW"),
- SKIP_PAST_LAST_ROW("SKIP PAST LAST ROW");
-
- private final String sql;
-
- AfterOption(String sql) {
- this.sql = sql;
- }
-
- @Override
- public String toString() {
- return sql;
- }
- }
-
- /** An operator describing a MATCH_RECOGNIZE specification. */
- public static class SqlMatchRecognizeOperator extends SqlOperator {
- public static final SqlMatchRecognizeOperator INSTANCE = new SqlMatchRecognizeOperator();
-
- private SqlMatchRecognizeOperator() {
- super("MATCH_RECOGNIZE", SqlKind.MATCH_RECOGNIZE, 2, true, null, null, null);
- }
-
- @Override
- public SqlSyntax getSyntax() {
- return SqlSyntax.SPECIAL;
- }
-
- @Override
- public SqlCall createCall(
- SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) {
- assert functionQualifier == null;
- assert operands.length == 12;
-
- return new SqlMatchRecognize(
- pos,
- operands[0],
- operands[1],
- (SqlLiteral) operands[2],
- (SqlLiteral) operands[3],
- (SqlNodeList) operands[4],
- (SqlNodeList) operands[5],
- operands[6],
- (SqlNodeList) operands[7],
- (SqlLiteral) operands[8],
- (SqlNodeList) operands[9],
- (SqlNodeList) operands[10],
- (SqlLiteral) operands[11]);
- }
-
- @Override
- public <R> void acceptCall(
- SqlVisitor<R> visitor,
- SqlCall call,
- boolean onlyExpressions,
- SqlBasicVisitor.ArgHandler<R> argHandler) {
- if (onlyExpressions) {
- List<SqlNode> operands = call.getOperandList();
- for (int i = 0; i < operands.size(); i++) {
- SqlNode operand = operands.get(i);
- if (operand == null) {
- continue;
- }
- argHandler.visitChild(visitor, call, i, operand);
- }
- } else {
- super.acceptCall(visitor, call, onlyExpressions, argHandler);
- }
- }
-
- @Override
- public void validateCall(
- SqlCall call,
- SqlValidator validator,
- SqlValidatorScope scope,
- SqlValidatorScope operandScope) {
- validator.validateMatchRecognize(call);
- }
-
- @Override
- public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
- final SqlMatchRecognize pattern = (SqlMatchRecognize) call;
-
- pattern.tableRef.unparse(writer, 0, 0);
- final SqlWriter.Frame mrFrame = writer.startFunCall("MATCH_RECOGNIZE");
-
- if (pattern.partitionList != null && pattern.partitionList.size() > 0) {
- writer.newlineAndIndent();
- writer.sep("PARTITION BY");
- final SqlWriter.Frame partitionFrame = writer.startList("", "");
- pattern.partitionList.unparse(writer, 0, 0);
- writer.endList(partitionFrame);
- }
-
- if (pattern.orderList != null && pattern.orderList.size() > 0) {
- writer.newlineAndIndent();
- writer.sep("ORDER BY");
- writer.list(
- SqlWriter.FrameTypeEnum.ORDER_BY_LIST, SqlWriter.COMMA, pattern.orderList);
- }
-
- if (pattern.measureList != null && pattern.measureList.size() > 0) {
- writer.newlineAndIndent();
- writer.sep("MEASURES");
- final SqlWriter.Frame measureFrame = writer.startList("", "");
- pattern.measureList.unparse(writer, 0, 0);
- writer.endList(measureFrame);
- }
-
- if (pattern.rowsPerMatch != null) {
- writer.newlineAndIndent();
- pattern.rowsPerMatch.unparse(writer, 0, 0);
- }
-
- if (pattern.after != null) {
- writer.newlineAndIndent();
- writer.sep("AFTER MATCH");
- pattern.after.unparse(writer, 0, 0);
- }
-
- writer.newlineAndIndent();
- writer.sep("PATTERN");
-
- SqlWriter.Frame patternFrame = writer.startList("(", ")");
- if (pattern.strictStart.booleanValue()) {
- writer.sep("^");
- }
- pattern.pattern.unparse(writer, 0, 0);
- if (pattern.strictEnd.booleanValue()) {
- writer.sep("$");
- }
- writer.endList(patternFrame);
- if (pattern.interval != null) {
- writer.sep("WITHIN");
- pattern.interval.unparse(writer, 0, 0);
- }
-
- if (pattern.subsetList != null && pattern.subsetList.size() > 0) {
- writer.newlineAndIndent();
- writer.sep("SUBSET");
- SqlWriter.Frame subsetFrame = writer.startList("", "");
- pattern.subsetList.unparse(writer, 0, 0);
- writer.endList(subsetFrame);
- }
-
- writer.newlineAndIndent();
- writer.sep("DEFINE");
-
- final SqlWriter.Frame patternDefFrame = writer.startList("", "");
- final SqlNodeList newDefineList = new SqlNodeList(SqlParserPos.ZERO);
- for (SqlNode node : pattern.getPatternDefList()) {
- final SqlCall call2 = (SqlCall) node;
- // swap the position of alias position in AS operator
- newDefineList.add(
- call2.getOperator()
- .createCall(SqlParserPos.ZERO, call2.operand(1), call2.operand(0)));
- }
- newDefineList.unparse(writer, 0, 0);
- writer.endList(patternDefFrame);
- writer.endList(mrFrame);
- }
- }
-}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlTableRef.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlTableRef.java
deleted file mode 100644
index a9cb3af1302..00000000000
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql/SqlTableRef.java
+++ /dev/null
@@ -1,80 +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;
-
-import com.google.common.collect.ImmutableList;
-import org.apache.calcite.sql.parser.SqlParserPos;
-
-import java.util.List;
-
-/**
- * A <code>SqlTableRef</code> is a node of a parse tree which represents a table reference.
- *
- * <p>It can be attached with a sql hint statement, see {@link SqlHint} for details.
- *
- * <p>The class was copied over because of CALCITE-4406. The changed lines are: 43-49
- */
-public class SqlTableRef extends SqlCall {
-
- // ~ Instance fields --------------------------------------------------------
-
- private final SqlIdentifier tableName;
- private final SqlNodeList hints;
-
- // ~ Static fields/initializers ---------------------------------------------
-
- private static final SqlOperator OPERATOR =
- new SqlSpecialOperator("TABLE_REF", SqlKind.TABLE_REF) {
- @Override
- public SqlCall createCall(
- SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) {
- return new SqlTableRef(
- pos, (SqlIdentifier) operands[0], (SqlNodeList) operands[1]);
- }
- };
-
- // ~ Constructors -----------------------------------------------------------
-
- public SqlTableRef(SqlParserPos pos, SqlIdentifier tableName, SqlNodeList hints) {
- super(pos);
- this.tableName = tableName;
- this.hints = hints;
- }
-
- // ~ Methods ----------------------------------------------------------------
-
- @Override
- public SqlOperator getOperator() {
- return OPERATOR;
- }
-
- @Override
- public List<SqlNode> getOperandList() {
- return ImmutableList.of(tableName, hints);
- }
-
- @Override
- public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
- tableName.unparse(writer, leftPrec, rightPrec);
- if (this.hints != null && this.hints.size() > 0) {
- writer.newlineAndIndent();
- writer.keyword("/*+");
- this.hints.unparse(writer, 0, 0);
- writer.keyword("*/");
- }
- }
-}
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 ed88a182990..fcf03ea12ab 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
@@ -1,13 +1,12 @@
/*
- * 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
+ * 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
+ * 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,
@@ -15,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.calcite.sql.validate;
import com.google.common.annotations.VisibleForTesting;
@@ -24,7 +22,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.apache.calcite.linq4j.Ord;
-import org.apache.calcite.linq4j.function.Function2;
import org.apache.calcite.linq4j.function.Functions;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelOptUtil;
@@ -80,6 +77,7 @@ import org.apache.calcite.sql.SqlSelectKeyword;
import org.apache.calcite.sql.SqlSnapshot;
import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.SqlTableFunction;
+import org.apache.calcite.sql.SqlUnpivot;
import org.apache.calcite.sql.SqlUnresolvedFunction;
import org.apache.calcite.sql.SqlUpdate;
import org.apache.calcite.sql.SqlUtil;
@@ -113,11 +111,13 @@ import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Static;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
+import org.apiguardian.api.API;
+import org.checkerframework.checker.nullness.qual.KeyFor;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.checker.nullness.qual.PolyNull;
+import org.checkerframework.dataflow.qual.Pure;
import org.slf4j.Logger;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.AbstractList;
@@ -137,22 +137,29 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
+import static java.util.Objects.requireNonNull;
+import static org.apache.calcite.linq4j.Nullness.castNonNull;
import static org.apache.calcite.sql.SqlUtil.stripAs;
+import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset;
+import static org.apache.calcite.sql.type.NonNullableAccessors.getCollation;
+import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getCondition;
+import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getTable;
import static org.apache.calcite.util.Static.RESOURCE;
/**
* Default implementation of {@link SqlValidator}, the class was copied over because of
* CALCITE-4554.
*
- * <p>Lines 4697 ~ 4716, Flink enables TIMESTAMP and TIMESTAMP_LTZ for system time period
- * specification type.
+ * <p>Lines 5009 ~ 5022, Flink enables TIMESTAMP and TIMESTAMP_LTZ for system time period
+ * specification type at {@link org.apache.calcite.sql.validate.SqlValidatorImpl#validateSnapshot}
*
- * <p>Lines 5034 ~ 5038, Flink enables TIMESTAMP and TIMESTAMP_LTZ for first orderBy column in
- * matchRecognize.
+ * <p>Lines 5366 ~ 5372, Flink enables TIMESTAMP and TIMESTAMP_LTZ for first orderBy column in
+ * matchRecognize at {@link SqlValidatorImpl#validateMatchRecognize}.
*/
public class SqlValidatorImpl implements SqlValidatorWithHints {
// ~ Static fields/initializers ---------------------------------------------
@@ -186,19 +193,20 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* Maps {@link SqlNode query node} objects to the {@link SqlValidatorScope} scope created from
* them.
*/
- protected final Map<SqlNode, SqlValidatorScope> scopes = new IdentityHashMap<>();
+ protected final IdentityHashMap<SqlNode, SqlValidatorScope> scopes = new IdentityHashMap<>();
/** Maps a {@link SqlSelect} and a clause to the scope used by that clause. */
private final Map<IdPair<SqlSelect, Clause>, SqlValidatorScope> clauseScopes = new HashMap<>();
/** The name-resolution scope of a LATERAL TABLE clause. */
- private TableScope tableScope = null;
+ private @Nullable TableScope tableScope = null;
/**
* Maps a {@link SqlNode node} to the {@link SqlValidatorNamespace namespace} which describes
* what columns they contain.
*/
- protected final Map<SqlNode, SqlValidatorNamespace> namespaces = new IdentityHashMap<>();
+ protected final IdentityHashMap<SqlNode, SqlValidatorNamespace> namespaces =
+ new IdentityHashMap<>();
/**
* Set of select expressions used as cursor definitions. In standard SQL, only the top-level
@@ -225,7 +233,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* Map of derived RelDataType for each node. This is an IdentityHashMap since in some cases
* (such as null literals) we need to discriminate by instance.
*/
- private final Map<SqlNode, RelDataType> nodeToTypeMap = new IdentityHashMap<>();
+ private final IdentityHashMap<SqlNode, RelDataType> nodeToTypeMap = new IdentityHashMap<>();
+
+ /** Provides the data for {@link #getValidatedOperandTypes(SqlCall)}. */
+ public final IdentityHashMap<SqlCall, List<RelDataType>> callToOperandTypesMap =
+ new IdentityHashMap<>();
private final AggFinder aggFinder;
private final AggFinder aggOrOverFinder;
@@ -237,7 +249,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private final Map<SqlNode, SqlNode> originalExprs = new HashMap<>();
- private SqlNode top;
+ private @Nullable SqlNode top;
// TODO jvs 11-Dec-2008: make this local to performUnconditionalRewrites
// if it's OK to expand the signature of that method.
@@ -266,10 +278,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlValidatorCatalogReader catalogReader,
RelDataTypeFactory typeFactory,
Config config) {
- this.opTab = Objects.requireNonNull(opTab);
- this.catalogReader = Objects.requireNonNull(catalogReader);
- this.typeFactory = Objects.requireNonNull(typeFactory);
- this.config = Objects.requireNonNull(config);
+ this.opTab = requireNonNull(opTab, "opTab");
+ this.catalogReader = requireNonNull(catalogReader, "catalogReader");
+ this.typeFactory = requireNonNull(typeFactory, "typeFactory");
+ this.config = requireNonNull(config, "config");
unknownType = typeFactory.createUnknownType();
booleanType = typeFactory.createSqlType(SqlTypeName.BOOLEAN);
@@ -280,7 +292,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
overFinder = new AggFinder(opTab, true, false, false, aggOrOverFinder, nameMatcher);
groupFinder = new AggFinder(opTab, false, false, true, null, nameMatcher);
aggOrOverOrGroupFinder = new AggFinder(opTab, true, true, true, null, nameMatcher);
- this.typeCoercion = config.typeCoercionFactory().create(typeFactory, this);
+ @SuppressWarnings("argument.type.incompatible")
+ TypeCoercion typeCoercion = config.typeCoercionFactory().create(typeFactory, this);
+ this.typeCoercion = typeCoercion;
if (config.typeCoercionRules() != null) {
SqlTypeCoercionRule.THREAD_PROVIDERS.set(config.typeCoercionRules());
}
@@ -292,22 +306,30 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return config.sqlConformance();
}
+ @Pure
+ @Override
public SqlValidatorCatalogReader getCatalogReader() {
return catalogReader;
}
+ @Pure
+ @Override
public SqlOperatorTable getOperatorTable() {
return opTab;
}
+ @Pure
+ @Override
public RelDataTypeFactory getTypeFactory() {
return typeFactory;
}
+ @Override
public RelDataType getUnknownType() {
return unknownType;
}
+ @Override
public SqlNodeList expandStar(
SqlNodeList selectList, SqlSelect select, boolean includeSystemVars) {
final List<SqlNode> list = new ArrayList<>();
@@ -324,17 +346,18 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
types,
includeSystemVars);
}
- getRawSelectScope(select).setExpandedSelectList(list);
+ getRawSelectScopeNonNull(select).setExpandedSelectList(list);
return new SqlNodeList(list, SqlParserPos.ZERO);
}
// implement SqlValidator
+ @Override
public void declareCursor(SqlSelect select, SqlValidatorScope parentScope) {
cursorSet.add(select);
// add the cursor to a map that maps the cursor to its select based on
// the position of the cursor relative to other cursors in that call
- FunctionParamInfo funcParamInfo = functionCallStack.peek();
+ FunctionParamInfo funcParamInfo = requireNonNull(functionCallStack.peek(), "functionCall");
Map<Integer, SqlSelect> cursorMap = funcParamInfo.cursorPosToSelectMap;
int numCursors = cursorMap.size();
cursorMap.put(numCursors, select);
@@ -350,19 +373,22 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
// implement SqlValidator
+ @Override
public void pushFunctionCall() {
FunctionParamInfo funcInfo = new FunctionParamInfo();
functionCallStack.push(funcInfo);
}
// implement SqlValidator
+ @Override
public void popFunctionCall() {
functionCallStack.pop();
}
// implement SqlValidator
- public String getParentCursor(String columnListParamName) {
- FunctionParamInfo funcParamInfo = functionCallStack.peek();
+ @Override
+ public @Nullable String getParentCursor(String columnListParamName) {
+ FunctionParamInfo funcParamInfo = requireNonNull(functionCallStack.peek(), "functionCall");
Map<String, String> parentCursorMap = funcParamInfo.columnListParamToParentCursorMap;
return parentCursorMap.get(columnListParamName);
}
@@ -396,13 +422,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// parentheses-free functions such as LOCALTIME into explicit function
// calls.
SqlNode expanded = expandSelectExpr(selectItem, scope, select);
- final String alias = deriveAlias(selectItem, aliases.size());
+ final String alias = deriveAliasNonNull(selectItem, aliases.size());
// If expansion has altered the natural alias, supply an explicit 'AS'.
final SqlValidatorScope selectScope = getSelectScope(select);
if (expanded != selectItem) {
- String newAlias = deriveAlias(expanded, aliases.size());
- if (!newAlias.equals(alias)) {
+ String newAlias = deriveAliasNonNull(expanded, aliases.size());
+ if (!Objects.equals(newAlias, alias)) {
expanded =
SqlStdOperatorTable.AS.createCall(
selectItem.getParserPosition(),
@@ -425,16 +451,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
private static SqlNode expandExprFromJoin(
- SqlJoin join, SqlIdentifier identifier, SelectScope scope) {
+ SqlJoin join, SqlIdentifier identifier, @Nullable SelectScope scope) {
if (join.getConditionType() != JoinConditionType.USING) {
return identifier;
}
- for (SqlNode node : (SqlNodeList) join.getCondition()) {
- final String name = ((SqlIdentifier) node).getSimple();
+ for (String name : SqlIdentifier.simpleNames((SqlNodeList) getCondition(join))) {
if (identifier.getSimple().equals(name)) {
final List<SqlNode> qualifiedNode = new ArrayList<>();
- for (ScopeChild child : scope.children) {
+ for (ScopeChild child : requireNonNull(scope, "scope").children) {
if (child.namespace.getRowType().getFieldNames().indexOf(name) >= 0) {
final SqlIdentifier exp =
new SqlIdentifier(
@@ -471,13 +496,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* Returns the set of field names in the join condition specified by USING or implicitly by
* NATURAL, de-duplicated and in order.
*/
- public List<String> usingNames(SqlJoin join) {
+ public @Nullable List<String> usingNames(SqlJoin join) {
switch (join.getConditionType()) {
case USING:
final ImmutableList.Builder<String> list = ImmutableList.builder();
final Set<String> names = catalogReader.nameMatcher().createSet();
- for (SqlNode node : (SqlNodeList) join.getCondition()) {
- final String name = ((SqlIdentifier) node).getSimple();
+ for (String name : SqlIdentifier.simpleNames((SqlNodeList) getCondition(join))) {
if (names.add(name)) {
list.add(name);
}
@@ -490,6 +514,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return SqlValidatorUtil.deriveNaturalJoinColumnList(
catalogReader.nameMatcher(), t0, t1);
}
+ break;
+ default:
+ break;
}
return null;
}
@@ -497,7 +524,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private static SqlNode expandCommonColumn(
SqlSelect sqlSelect,
SqlNode selectItem,
- SelectScope scope,
+ @Nullable SelectScope scope,
SqlValidatorImpl validator) {
if (!(selectItem instanceof SqlIdentifier)) {
return selectItem;
@@ -520,18 +547,22 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
private static void validateQualifiedCommonColumn(
- SqlJoin join, SqlIdentifier identifier, SelectScope scope, SqlValidatorImpl validator) {
+ SqlJoin join,
+ SqlIdentifier identifier,
+ @Nullable SelectScope scope,
+ SqlValidatorImpl validator) {
List<String> names = validator.usingNames(join);
if (names == null) {
// Not USING or NATURAL.
return;
}
+ requireNonNull(scope, "scope");
// First we should make sure that the first component is the table name.
// Then check whether the qualified identifier contains common column.
for (ScopeChild child : scope.children) {
- if (child.name.equals(identifier.getComponent(0).toString())) {
- if (names.indexOf(identifier.getComponent(1).toString()) >= 0) {
+ if (Objects.equals(child.name, identifier.getComponent(0).toString())) {
+ if (names.contains(identifier.getComponent(1).toString())) {
throw validator.newValidationError(
identifier,
RESOURCE.disallowsQualifyingCommonColumn(identifier.toString()));
@@ -580,9 +611,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
addToSelectList(
selectItems, aliases, fields, exp, scope, includeSystemVars);
} else {
- final SqlNode from = child.namespace.getNode();
- final SqlValidatorNamespace fromNs = getNamespace(from, scope);
- assert fromNs != null;
+ final SqlNode from = SqlNonNullableAccessors.getNode(child);
+ final SqlValidatorNamespace fromNs = getNamespaceOrThrow(from, scope);
final RelDataType rowType = fromNs.getRowType();
for (RelDataTypeField field : rowType.getFieldList()) {
String columnName = field.getName();
@@ -622,7 +652,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// If NATURAL JOIN or USING is present, move key fields to the front of
// the list, per standard SQL. Disabled if there are dynamic fields.
if (!hasDynamicStruct || Bug.CALCITE_2400_FIXED) {
- new Permute(scope.getNode().getFrom(), 0).permute(selectItems, fields);
+ SqlNode from =
+ requireNonNull(
+ scope.getNode().getFrom(),
+ () -> "getFrom for " + scope.getNode());
+ new Permute(from, 0).permute(selectItems, fields);
}
return true;
@@ -698,6 +732,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return false;
}
+ @Override
public SqlNode validate(SqlNode topNode) {
SqlValidatorScope scope = new EmptyScope(this);
scope = new CatalogScope(scope, ImmutableList.of("CATALOG"));
@@ -707,6 +742,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return topNode2;
}
+ @Override
public List<SqlMoniker> lookupHints(SqlNode topNode, SqlParserPos pos) {
SqlValidatorScope scope = new EmptyScope(this);
SqlNode outermostNode = performUnconditionalRewrites(topNode, false);
@@ -723,7 +759,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return ImmutableList.copyOf(hintList);
}
- public SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos) {
+ @Override
+ public @Nullable SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos) {
final String posString = pos.toString();
IdInfo info = idPositions.get(posString);
if (info != null) {
@@ -764,15 +801,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
private void lookupFromHints(
- SqlNode node,
- SqlValidatorScope scope,
+ @Nullable SqlNode node,
+ @Nullable SqlValidatorScope scope,
SqlParserPos pos,
Collection<SqlMoniker> hintList) {
if (node == null) {
// This can happen in cases like "select * _suggest_", so from clause is absent
return;
}
- final SqlValidatorNamespace ns = getNamespace(node);
+ final SqlValidatorNamespace ns = getNamespaceOrThrow(node);
if (ns.isWrapperFor(IdentifierNamespace.class)) {
IdentifierNamespace idNs = ns.unwrap(IdentifierNamespace.class);
final SqlIdentifier id = idNs.getId();
@@ -802,7 +839,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void lookupJoinHints(
SqlJoin join,
- SqlValidatorScope scope,
+ @Nullable SqlValidatorScope scope,
SqlParserPos pos,
Collection<SqlMoniker> hintList) {
SqlNode left = join.getLeft();
@@ -817,10 +854,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return;
}
final JoinConditionType conditionType = join.getConditionType();
- final SqlValidatorScope joinScope = scopes.get(join);
switch (conditionType) {
case ON:
- condition.findValidOptions(this, joinScope, pos, hintList);
+ requireNonNull(condition, () -> "join.getCondition() for " + join)
+ .findValidOptions(this, getScopeOrThrow(join), pos, hintList);
return;
default:
@@ -935,6 +972,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public SqlNode validateParameterizedExpression(
SqlNode topNode, final Map<String, RelDataType> nameToTypeMap) {
SqlValidatorScope scope = new ParameterScope(this, nameToTypeMap);
@@ -959,8 +997,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return outermostNode;
}
- public void validateQuery(SqlNode node, SqlValidatorScope scope, RelDataType targetRowType) {
- final SqlValidatorNamespace ns = getNamespace(node, scope);
+ @Override
+ public void validateQuery(
+ SqlNode node, @Nullable SqlValidatorScope scope, RelDataType targetRowType) {
+ final SqlValidatorNamespace ns = getNamespaceOrThrow(node, scope);
if (node.getKind() == SqlKind.TABLESAMPLE) {
List<SqlNode> operands = ((SqlCall) node).getOperandList();
SqlSampleSpec sampleSpec = SqlLiteral.sampleValue(operands.get(1));
@@ -976,7 +1016,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
switch (node.getKind()) {
case EXTEND:
// Until we have a dedicated namespace for EXTEND
- deriveType(scope, node);
+ deriveType(requireNonNull(scope, "scope"), node);
+ break;
+ default:
+ break;
}
if (node == top) {
validateModality(node);
@@ -995,8 +1038,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
protected void validateNamespace(
final SqlValidatorNamespace namespace, RelDataType targetRowType) {
namespace.validate(targetRowType);
- if (namespace.getNode() != null) {
- setValidatedNodeType(namespace.getNode(), namespace.getType());
+ SqlNode node = namespace.getNode();
+ if (node != null) {
+ setValidatedNodeType(node, namespace.getType());
}
}
@@ -1005,57 +1049,82 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return new EmptyScope(this);
}
+ private SqlValidatorScope getScope(SqlSelect select, Clause clause) {
+ return requireNonNull(
+ clauseScopes.get(IdPair.of(select, clause)),
+ () -> "no " + clause + " scope for " + select);
+ }
+
public SqlValidatorScope getCursorScope(SqlSelect select) {
- return clauseScopes.get(IdPair.of(select, Clause.CURSOR));
+ return getScope(select, Clause.CURSOR);
}
+ @Override
public SqlValidatorScope getWhereScope(SqlSelect select) {
- return clauseScopes.get(IdPair.of(select, Clause.WHERE));
+ return getScope(select, Clause.WHERE);
}
+ @Override
public SqlValidatorScope getSelectScope(SqlSelect select) {
- return clauseScopes.get(IdPair.of(select, Clause.SELECT));
+ return getScope(select, Clause.SELECT);
}
- public SelectScope getRawSelectScope(SqlSelect select) {
- SqlValidatorScope scope = getSelectScope(select);
+ @Override
+ public @Nullable SelectScope getRawSelectScope(SqlSelect select) {
+ SqlValidatorScope scope = clauseScopes.get(IdPair.of(select, Clause.SELECT));
if (scope instanceof AggregatingSelectScope) {
scope = ((AggregatingSelectScope) scope).getParent();
}
return (SelectScope) scope;
}
+ private SelectScope getRawSelectScopeNonNull(SqlSelect select) {
+ return requireNonNull(getRawSelectScope(select), () -> "getRawSelectScope for " + select);
+ }
+
+ @Override
public SqlValidatorScope getHavingScope(SqlSelect select) {
// Yes, it's the same as getSelectScope
- return clauseScopes.get(IdPair.of(select, Clause.SELECT));
+ return getScope(select, Clause.SELECT);
}
+ @Override
public SqlValidatorScope getGroupScope(SqlSelect select) {
// Yes, it's the same as getWhereScope
- return clauseScopes.get(IdPair.of(select, Clause.GROUP_BY));
+ return getScope(select, Clause.WHERE);
}
- public SqlValidatorScope getFromScope(SqlSelect select) {
+ @Override
+ public @Nullable SqlValidatorScope getFromScope(SqlSelect select) {
return scopes.get(select);
}
+ @Override
public SqlValidatorScope getOrderScope(SqlSelect select) {
- return clauseScopes.get(IdPair.of(select, Clause.ORDER));
+ return getScope(select, Clause.ORDER);
}
+ @Override
public SqlValidatorScope getMatchRecognizeScope(SqlMatchRecognize node) {
- return scopes.get(node);
+ return getScopeOrThrow(node);
}
- public SqlValidatorScope getJoinScope(SqlNode node) {
+ @Override
+ public @Nullable SqlValidatorScope getJoinScope(SqlNode node) {
return scopes.get(stripAs(node));
}
+ @Override
public SqlValidatorScope getOverScope(SqlNode node) {
- return scopes.get(node);
+ return getScopeOrThrow(node);
+ }
+
+ private SqlValidatorScope getScopeOrThrow(SqlNode node) {
+ return requireNonNull(scopes.get(node), () -> "scope for " + node);
}
- private SqlValidatorNamespace getNamespace(SqlNode node, SqlValidatorScope scope) {
+ private @Nullable SqlValidatorNamespace getNamespace(
+ SqlNode node, @Nullable SqlValidatorScope scope) {
if (node instanceof SqlIdentifier && scope instanceof DelegatingScope) {
final SqlIdentifier id = (SqlIdentifier) node;
final DelegatingScope idScope = (DelegatingScope) ((DelegatingScope) scope).getParent();
@@ -1080,18 +1149,24 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case TABLE_REF:
case EXTEND:
return getNamespace(nested, scope);
+ default:
+ break;
}
break;
+ default:
+ break;
}
}
return getNamespace(node);
}
- private SqlValidatorNamespace getNamespace(SqlIdentifier id, DelegatingScope scope) {
+ private @Nullable SqlValidatorNamespace getNamespace(
+ SqlIdentifier id, @Nullable DelegatingScope scope) {
if (id.isSimple()) {
final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
final SqlValidatorScope.ResolvedImpl resolved = new SqlValidatorScope.ResolvedImpl();
- scope.resolve(id.names, nameMatcher, false, resolved);
+ requireNonNull(scope, () -> "scope needed to lookup " + id)
+ .resolve(id.names, nameMatcher, false, resolved);
if (resolved.count() == 1) {
return resolved.only().namespace;
}
@@ -1099,7 +1174,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return getNamespace(id);
}
- public SqlValidatorNamespace getNamespace(SqlNode node) {
+ @Override
+ public @Nullable SqlValidatorNamespace getNamespace(SqlNode node) {
switch (node.getKind()) {
case AS:
@@ -1121,7 +1197,47 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
- private void handleOffsetFetch(SqlNode offset, SqlNode fetch) {
+ /**
+ * Namespace for the given node.
+ *
+ * @param node node to compute the namespace for
+ * @return namespace for the given node, never null
+ * @see #getNamespace(SqlNode)
+ */
+ @API(since = "1.27", status = API.Status.INTERNAL)
+ SqlValidatorNamespace getNamespaceOrThrow(SqlNode node) {
+ return requireNonNull(getNamespace(node), () -> "namespace for " + node);
+ }
+
+ /**
+ * Namespace for the given node.
+ *
+ * @param node node to compute the namespace for
+ * @param scope namespace scope
+ * @return namespace for the given node, never null
+ * @see #getNamespace(SqlNode)
+ */
+ @API(since = "1.27", status = API.Status.INTERNAL)
+ SqlValidatorNamespace getNamespaceOrThrow(SqlNode node, @Nullable SqlValidatorScope scope) {
+ return requireNonNull(
+ getNamespace(node, scope), () -> "namespace for " + node + ", scope " + scope);
+ }
+
+ /**
+ * Namespace for the given node.
+ *
+ * @param id identifier to resolve
+ * @param scope namespace scope
+ * @return namespace for the given node, never null
+ * @see #getNamespace(SqlIdentifier, DelegatingScope)
+ */
+ @API(since = "1.26", status = API.Status.INTERNAL)
+ SqlValidatorNamespace getNamespaceOrThrow(SqlIdentifier id, @Nullable DelegatingScope scope) {
+ return requireNonNull(
+ getNamespace(id, scope), () -> "namespace for " + id + ", scope " + scope);
+ }
+
+ private void handleOffsetFetch(@Nullable SqlNode offset, @Nullable SqlNode fetch) {
if (offset instanceof SqlDynamicParam) {
setValidatedNodeType(offset, typeFactory.createSqlType(SqlTypeName.INTEGER));
}
@@ -1135,17 +1251,18 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* the expression tree into a standard form so that the rest of the validation logic can be
* simpler.
*
+ * <p>Returns null if and only if the original expression is null.
+ *
* @param node expression to be rewritten
* @param underFrom whether node appears directly under a FROM clause
- * @return rewritten expression
+ * @return rewritten expression, or null if the original expression is null
*/
- protected SqlNode performUnconditionalRewrites(SqlNode node, boolean underFrom) {
+ protected @PolyNull SqlNode performUnconditionalRewrites(
+ @PolyNull SqlNode node, boolean underFrom) {
if (node == null) {
return null;
}
- SqlNode newOperand;
-
// first transform operands and invoke generic call rewrite
if (node instanceof SqlCall) {
if (node instanceof SqlMerge) {
@@ -1166,7 +1283,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
} else {
childUnderFrom = false;
}
- newOperand = performUnconditionalRewrites(operand, childUnderFrom);
+ SqlNode newOperand = performUnconditionalRewrites(operand, childUnderFrom);
if (newOperand != null && newOperand != operand) {
call.setOperand(i, newOperand);
}
@@ -1194,12 +1311,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
node = call.getOperator().rewriteCall(this, call);
}
} else if (node instanceof SqlNodeList) {
- SqlNodeList list = (SqlNodeList) node;
- for (int i = 0, count = list.size(); i < count; i++) {
+ final SqlNodeList list = (SqlNodeList) node;
+ for (int i = 0; i < list.size(); i++) {
SqlNode operand = list.get(i);
- newOperand = performUnconditionalRewrites(operand, false);
+ SqlNode newOperand = performUnconditionalRewrites(operand, false);
if (newOperand != null) {
- list.getList().set(i, newOperand);
+ list.set(i, newOperand);
}
}
}
@@ -1267,13 +1384,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
final SqlNodeList orderList;
- if (getInnerSelect(node) != null && isAggregate(getInnerSelect(node))) {
+ SqlSelect innerSelect = getInnerSelect(node);
+ if (innerSelect != null && isAggregate(innerSelect)) {
orderList = SqlNode.clone(orderBy.orderList);
// We assume that ORDER BY item does not have ASC etc.
// We assume that ORDER BY item is present in SELECT list.
for (int i = 0; i < orderList.size(); i++) {
SqlNode sqlNode = orderList.get(i);
- SqlNodeList selectList2 = getInnerSelect(node).getSelectList();
+ SqlNodeList selectList2 =
+ SqlNonNullableAccessors.getSelectList(innerSelect);
for (Ord<SqlNode> sel : Ord.zip(selectList2)) {
if (stripAs(sel.e).equalsDeep(sqlNode, Litmus.IGNORE)) {
orderList.set(
@@ -1356,11 +1475,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
rewriteMerge(call);
break;
}
+ default:
+ break;
}
return node;
}
- private SqlSelect getInnerSelect(SqlNode node) {
+ private static @Nullable SqlSelect getInnerSelect(SqlNode node) {
for (; ; ) {
if (node instanceof SqlSelect) {
return (SqlSelect) node;
@@ -1374,7 +1495,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
- private void rewriteMerge(SqlMerge call) {
+ private static void rewriteMerge(SqlMerge call) {
SqlNodeList selectList;
SqlUpdate updateStmt = call.getUpdateCall();
if (updateStmt != null) {
@@ -1382,7 +1503,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// from the update statement's source since it's the same as
// what we want for the select list of the merge source -- '*'
// followed by the update set expressions
- selectList = SqlNode.clone(updateStmt.getSourceSelect().getSelectList());
+ SqlSelect sourceSelect = SqlNonNullableAccessors.getSourceSelect(updateStmt);
+ selectList = SqlNode.clone(SqlNonNullableAccessors.getSelectList(sourceSelect));
} else {
// otherwise, just use select *
selectList = new SqlNodeList(SqlParserPos.ZERO);
@@ -1457,12 +1579,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private SqlNode rewriteUpdateToMerge(SqlUpdate updateCall, SqlNode selfJoinSrcExpr) {
// Make sure target has an alias.
- if (updateCall.getAlias() == null) {
- updateCall.setAlias(new SqlIdentifier(UPDATE_TGT_ALIAS, SqlParserPos.ZERO));
+ SqlIdentifier updateAlias = updateCall.getAlias();
+ if (updateAlias == null) {
+ updateAlias = new SqlIdentifier(UPDATE_TGT_ALIAS, SqlParserPos.ZERO);
+ updateCall.setAlias(updateAlias);
}
SqlNode selfJoinTgtExpr =
- getSelfJoinExprForUpdate(
- updateCall.getTargetTable(), updateCall.getAlias().getSimple());
+ getSelfJoinExprForUpdate(updateCall.getTargetTable(), updateAlias.getSimple());
assert selfJoinTgtExpr != null;
// Create join condition between source and target exprs,
@@ -1488,7 +1611,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Note that we anonymize the source rather than the
// target because downstream, the optimizer rules
// don't want to see any projection on top of the target.
- IdentifierNamespace ns = new IdentifierNamespace(this, target, null, null);
+ IdentifierNamespace ns = new IdentifierNamespace(this, target, null, castNonNull(null));
RelDataType rowType = ns.getRowType();
SqlNode source = updateCall.getTargetTable().clone(SqlParserPos.ZERO);
final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
@@ -1538,7 +1661,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* been anonymized to "SYS$ANONx", where x is the 1-based column number.
* @return expression for unique identifier, or null to prevent conversion
*/
- protected SqlNode getSelfJoinExprForUpdate(SqlNode table, String alias) {
+ protected @Nullable SqlNode getSelfJoinExprForUpdate(SqlNode table, String alias) {
return null;
}
@@ -1561,8 +1684,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
++ordinal;
}
SqlNode sourceTable = call.getTargetTable();
- if (call.getAlias() != null) {
- sourceTable = SqlValidatorUtil.addAlias(sourceTable, call.getAlias().getSimple());
+ SqlIdentifier alias = call.getAlias();
+ if (alias != null) {
+ sourceTable = SqlValidatorUtil.addAlias(sourceTable, alias.getSimple());
}
return new SqlSelect(
SqlParserPos.ZERO,
@@ -1590,8 +1714,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
SqlNode sourceTable = call.getTargetTable();
- if (call.getAlias() != null) {
- sourceTable = SqlValidatorUtil.addAlias(sourceTable, call.getAlias().getSimple());
+ SqlIdentifier alias = call.getAlias();
+ if (alias != null) {
+ sourceTable = SqlValidatorUtil.addAlias(sourceTable, alias.getSimple());
}
return new SqlSelect(
SqlParserPos.ZERO,
@@ -1611,6 +1736,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
/**
* Returns null if there is no common type. E.g. if the rows have a different number of columns.
*/
+ @Nullable
RelDataType getTableConstructorRowType(SqlCall values, SqlValidatorScope scope) {
final List<SqlNode> rows = values.getOperandList();
assert rows.size() >= 1;
@@ -1624,7 +1750,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final List<String> aliasList = new ArrayList<>();
final List<RelDataType> typeList = new ArrayList<>();
for (Ord<SqlNode> column : Ord.zip(rowConstructor.getOperandList())) {
- final String alias = deriveAlias(column.e, column.i);
+ final String alias = deriveAliasNonNull(column.e, column.i);
aliasList.add(alias);
final RelDataType type = deriveType(scope, column.e);
typeList.add(type);
@@ -1639,16 +1765,21 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return typeFactory.leastRestrictive(rowTypes);
}
+ @Override
public RelDataType getValidatedNodeType(SqlNode node) {
RelDataType type = getValidatedNodeTypeIfKnown(node);
if (type == null) {
+ if (node.getKind() == SqlKind.IDENTIFIER) {
+ throw newValidationError(node, RESOURCE.unknownIdentifier(node.toString()));
+ }
throw Util.needToImplement(node);
} else {
return type;
}
}
- public RelDataType getValidatedNodeTypeIfKnown(SqlNode node) {
+ @Override
+ public @Nullable RelDataType getValidatedNodeTypeIfKnown(SqlNode node) {
final RelDataType type = nodeToTypeMap.get(node);
if (type != null) {
return type;
@@ -1667,6 +1798,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return null;
}
+ @Override
+ public @Nullable List<RelDataType> getValidatedOperandTypes(SqlCall call) {
+ return callToOperandTypesMap.get(call);
+ }
+
/**
* Saves the type of a {@link SqlNode}, now that it has been validated.
*
@@ -1676,9 +1812,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @param node A SQL parse tree node, never null
* @param type Its type; must not be null
*/
+ @Override
public final void setValidatedNodeType(SqlNode node, RelDataType type) {
- Objects.requireNonNull(type);
- Objects.requireNonNull(node);
+ requireNonNull(type, "type");
+ requireNonNull(node, "node");
if (type.equals(unknownType)) {
// don't set anything until we know what it is, and don't overwrite
// a known type with the unknown type
@@ -1687,12 +1824,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
nodeToTypeMap.put(node, type);
}
+ @Override
public void removeValidatedNodeType(SqlNode node) {
nodeToTypeMap.remove(node);
}
- @Nullable
- public SqlCall makeNullaryCall(SqlIdentifier id) {
+ @Override
+ public @Nullable SqlCall makeNullaryCall(SqlIdentifier id) {
if (id.names.size() == 1 && !id.isComponentQuoted(0)) {
final List<SqlOperator> list = new ArrayList<>();
opTab.lookupOperatorOverloads(
@@ -1711,9 +1849,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return null;
}
+ @Override
public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) {
- Objects.requireNonNull(scope);
- Objects.requireNonNull(expr);
+ requireNonNull(scope, "scope");
+ requireNonNull(expr, "expr");
// if we already know the type, no need to re-derive
RelDataType type = nodeToTypeMap.get(expr);
@@ -1734,14 +1873,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
RelDataType deriveTypeImpl(SqlValidatorScope scope, SqlNode operand) {
DeriveTypeVisitor v = new DeriveTypeVisitor(scope);
final RelDataType type = operand.accept(v);
- return Objects.requireNonNull(scope.nullifyType(operand, type));
+ return requireNonNull(scope.nullifyType(operand, type));
}
+ @Override
public RelDataType deriveConstructorType(
SqlValidatorScope scope,
SqlCall call,
SqlFunction unresolvedConstructor,
- SqlFunction resolvedConstructor,
+ @Nullable SqlFunction resolvedConstructor,
List<RelDataType> argTypes) {
SqlIdentifier sqlIdentifier = unresolvedConstructor.getSqlIdentifier();
assert sqlIdentifier != null;
@@ -1773,7 +1913,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
((SqlBasicCall) call)
.setOperator(
new SqlFunction(
- type.getSqlIdentifier(),
+ requireNonNull(
+ type.getSqlIdentifier(),
+ () -> "sqlIdentifier of " + type),
ReturnTypes.explicit(type),
null,
null,
@@ -1784,11 +1926,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return type;
}
+ @Override
public CalciteException handleUnresolvedFunction(
SqlCall call,
- SqlFunction unresolvedFunction,
+ SqlOperator unresolvedFunction,
List<RelDataType> argTypes,
- List<String> argNames) {
+ @Nullable List<String> argNames) {
// For builtins, we can give a better error message
final List<SqlOperator> overloads = new ArrayList<>();
opTab.lookupOperatorOverloads(
@@ -1807,20 +1950,24 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
- AssignableOperandTypeChecker typeChecking =
- new AssignableOperandTypeChecker(argTypes, argNames);
- String signature =
- typeChecking.getAllowedSignatures(unresolvedFunction, unresolvedFunction.getName());
+ final String signature;
+ if (unresolvedFunction instanceof SqlFunction) {
+ final SqlOperandTypeChecker typeChecking =
+ new AssignableOperandTypeChecker(argTypes, argNames);
+ signature =
+ typeChecking.getAllowedSignatures(
+ unresolvedFunction, unresolvedFunction.getName());
+ } else {
+ signature = unresolvedFunction.getName();
+ }
throw newValidationError(call, RESOURCE.validatorUnknownFunction(signature));
}
protected void inferUnknownTypes(
- @Nonnull RelDataType inferredType,
- @Nonnull SqlValidatorScope scope,
- @Nonnull SqlNode node) {
- Objects.requireNonNull(inferredType);
- Objects.requireNonNull(scope);
- Objects.requireNonNull(node);
+ RelDataType inferredType, SqlValidatorScope scope, SqlNode node) {
+ requireNonNull(inferredType, "inferredType");
+ requireNonNull(scope, "scope");
+ requireNonNull(node, "node");
final SqlValidatorScope newScope = scopes.get(node);
if (newScope != null) {
scope = newScope;
@@ -1847,8 +1994,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
newInferredType =
typeFactory.createTypeWithCharsetAndCollation(
newInferredType,
- inferredType.getCharset(),
- inferredType.getCollation());
+ getCharset(inferredType),
+ getCollation(inferredType));
}
setValidatedNodeType(node, newInferredType);
} else if (node instanceof SqlNodeList) {
@@ -1877,18 +2024,20 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final RelDataType whenType =
caseCall.getValueOperand() == null ? booleanType : unknownType;
- for (SqlNode sqlNode : caseCall.getWhenOperands().getList()) {
+ for (SqlNode sqlNode : caseCall.getWhenOperands()) {
inferUnknownTypes(whenType, scope, sqlNode);
}
RelDataType returnType = deriveType(scope, node);
- for (SqlNode sqlNode : caseCall.getThenOperands().getList()) {
+ for (SqlNode sqlNode : caseCall.getThenOperands()) {
inferUnknownTypes(returnType, scope, sqlNode);
}
- if (!SqlUtil.isNullLiteral(caseCall.getElseOperand(), false)) {
- inferUnknownTypes(returnType, scope, caseCall.getElseOperand());
+ SqlNode elseOperand =
+ requireNonNull(caseCall.getElseOperand(), () -> "elseOperand for " + caseCall);
+ if (!SqlUtil.isNullLiteral(elseOperand, false)) {
+ inferUnknownTypes(returnType, scope, elseOperand);
} else {
- setValidatedNodeType(caseCall.getElseOperand(), returnType);
+ setValidatedNodeType(elseOperand, returnType);
}
} else if (node.getKind() == SqlKind.AS) {
// For AS operator, only infer the operand not the alias
@@ -1936,10 +2085,17 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
list.add(exp);
}
- public String deriveAlias(SqlNode node, int ordinal) {
+ @Override
+ public @Nullable String deriveAlias(SqlNode node, int ordinal) {
return SqlValidatorUtil.getAlias(node, ordinal);
}
+ private String deriveAliasNonNull(SqlNode node, int ordinal) {
+ return requireNonNull(
+ deriveAlias(node, ordinal),
+ () -> "non-null alias expected for node = " + node + ", ordinal = " + ordinal);
+ }
+
protected boolean shouldAllowIntermediateOrderBy() {
return true;
}
@@ -1949,7 +2105,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlValidatorScope usingScope,
SqlMatchRecognize call,
SqlNode enclosingNode,
- String alias,
+ @Nullable String alias,
boolean forceNullable) {
final MatchRecognizeNamespace matchRecognizeNamespace =
@@ -1985,27 +2141,55 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void registerPivot(
SqlValidatorScope parentScope,
SqlValidatorScope usingScope,
- SqlPivot call,
+ SqlPivot pivot,
SqlNode enclosingNode,
- String alias,
+ @Nullable String alias,
boolean forceNullable) {
- final PivotNamespace namespace = createPivotNameSpace(call, enclosingNode);
+ final PivotNamespace namespace = createPivotNameSpace(pivot, enclosingNode);
registerNamespace(usingScope, alias, namespace, forceNullable);
- final SqlValidatorScope scope = new PivotScope(parentScope, call);
+ final SqlValidatorScope scope = new PivotScope(parentScope, pivot);
+ scopes.put(pivot, scope);
+
+ // parse input query
+ SqlNode expr = pivot.query;
+ SqlNode newExpr =
+ registerFrom(
+ parentScope, scope, true, expr, expr, null, null, forceNullable, false);
+ if (expr != newExpr) {
+ pivot.setOperand(0, newExpr);
+ }
+ }
+
+ protected PivotNamespace createPivotNameSpace(SqlPivot call, SqlNode enclosingNode) {
+ return new PivotNamespace(this, call, enclosingNode);
+ }
+
+ private void registerUnpivot(
+ SqlValidatorScope parentScope,
+ SqlValidatorScope usingScope,
+ SqlUnpivot call,
+ SqlNode enclosingNode,
+ @Nullable String alias,
+ boolean forceNullable) {
+ final UnpivotNamespace namespace = createUnpivotNameSpace(call, enclosingNode);
+ registerNamespace(usingScope, alias, namespace, forceNullable);
+
+ final SqlValidatorScope scope = new UnpivotScope(parentScope, call);
scopes.put(call, scope);
// parse input query
SqlNode expr = call.query;
SqlNode newExpr =
- registerFrom(scope, scope, true, expr, expr, null, null, forceNullable, false);
+ registerFrom(
+ parentScope, scope, true, expr, expr, null, null, forceNullable, false);
if (expr != newExpr) {
call.setOperand(0, newExpr);
}
}
- protected PivotNamespace createPivotNameSpace(SqlPivot call, SqlNode enclosingNode) {
- return new PivotNamespace(this, call, enclosingNode);
+ protected UnpivotNamespace createUnpivotNameSpace(SqlUnpivot call, SqlNode enclosingNode) {
+ return new UnpivotNamespace(this, call, enclosingNode);
}
/**
@@ -2018,12 +2202,18 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @param forceNullable Whether to force the type of namespace to be nullable
*/
protected void registerNamespace(
- SqlValidatorScope usingScope,
- String alias,
+ @Nullable SqlValidatorScope usingScope,
+ @Nullable String alias,
SqlValidatorNamespace ns,
boolean forceNullable) {
- namespaces.put(ns.getNode(), ns);
+ namespaces.put(requireNonNull(ns.getNode(), () -> "ns.getNode() for " + ns), ns);
if (usingScope != null) {
+ assert alias != null
+ : "Registering namespace "
+ + ns
+ + ", into scope "
+ + usingScope
+ + ", so alias must not be null";
usingScope.addChild(ns, alias, forceNullable);
}
}
@@ -2059,8 +2249,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
boolean register,
final SqlNode node,
SqlNode enclosingNode,
- String alias,
- SqlNodeList extendList,
+ @Nullable String alias,
+ @Nullable SqlNodeList extendList,
boolean forceNullable,
final boolean lateral) {
final SqlKind kind = node.getKind();
@@ -2076,7 +2266,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case OVER:
alias = deriveAlias(node, -1);
if (alias == null) {
- alias = deriveAlias(node, nextGeneratedId++);
+ alias = deriveAliasNonNull(node, nextGeneratedId++);
}
if (config.identifierExpansion()) {
newNode = SqlValidatorUtil.addAlias(node, alias);
@@ -2092,11 +2282,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case OTHER_FUNCTION:
case COLLECTION_TABLE:
case PIVOT:
+ case UNPIVOT:
case MATCH_RECOGNIZE:
// give this anonymous construct a name since later
// query processing stages rely on it
- alias = deriveAlias(node, nextGeneratedId++);
+ alias = deriveAliasNonNull(node, nextGeneratedId++);
if (config.identifierExpansion()) {
// Since we're expanding identifiers, we should make the
// aliases explicit too, otherwise the expanded query
@@ -2105,6 +2296,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
newNode = SqlValidatorUtil.addAlias(node, alias);
}
break;
+ default:
+ break;
}
}
@@ -2131,10 +2324,17 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case AS:
call = (SqlCall) node;
if (alias == null) {
- alias = call.operand(1).toString();
+ alias = String.valueOf(call.operand(1));
}
- final boolean needAlias = call.operandCount() > 2;
expr = call.operand(0);
+ final boolean needAlias =
+ call.operandCount() > 2
+ || expr.getKind() == SqlKind.VALUES
+ || expr.getKind() == SqlKind.UNNEST
+ && (((SqlCall) expr).operand(0).getKind()
+ == SqlKind.ARRAY_VALUE_CONSTRUCTOR
+ || ((SqlCall) expr).operand(0).getKind()
+ == SqlKind.MULTISET_VALUE_CONSTRUCTOR);
newExpr =
registerFrom(
parentScope,
@@ -2181,6 +2381,16 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
forceNullable);
return node;
+ case UNPIVOT:
+ registerUnpivot(
+ parentScope,
+ usingScope,
+ (SqlUnpivot) node,
+ enclosingNode,
+ alias,
+ forceNullable);
+ return node;
+
case TABLESAMPLE:
call = (SqlCall) node;
expr = call.operand(0);
@@ -2219,6 +2429,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
forceLeftNullable = true;
forceRightNullable = true;
break;
+ default:
+ break;
}
final SqlNode newLeft =
registerFrom(
@@ -2261,7 +2473,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (tableScope == null) {
tableScope = new TableScope(parentScope, node);
}
- tableScope.addChild(newNs, alias, forceNullable);
+ tableScope.addChild(newNs, requireNonNull(alias, "alias"), forceNullable);
if (extendList != null && extendList.size() != 0) {
return enclosingNode;
}
@@ -2335,7 +2547,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case WITH:
case OTHER_FUNCTION:
if (alias == null) {
- alias = deriveAlias(node, nextGeneratedId++);
+ alias = deriveAliasNonNull(node, nextGeneratedId++);
}
registerQuery(
parentScope,
@@ -2475,10 +2687,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
*/
private void registerQuery(
SqlValidatorScope parentScope,
- SqlValidatorScope usingScope,
+ @Nullable SqlValidatorScope usingScope,
SqlNode node,
SqlNode enclosingNode,
- String alias,
+ @Nullable String alias,
boolean forceNullable) {
Preconditions.checkArgument(usingScope == null || alias != null);
registerQuery(parentScope, usingScope, node, enclosingNode, alias, forceNullable, true);
@@ -2496,14 +2708,14 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
*/
private void registerQuery(
SqlValidatorScope parentScope,
- SqlValidatorScope usingScope,
+ @Nullable SqlValidatorScope usingScope,
SqlNode node,
SqlNode enclosingNode,
- String alias,
+ @Nullable String alias,
boolean forceNullable,
boolean checkUpdate) {
- Objects.requireNonNull(node);
- Objects.requireNonNull(enclosingNode);
+ requireNonNull(node, "node");
+ requireNonNull(enclosingNode, "enclosingNode");
Preconditions.checkArgument(usingScope == null || alias != null);
SqlCall call;
@@ -2560,7 +2772,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
registerSubQueries(groupByScope, select.getGroup());
}
registerOperandSubQueries(aggScope, select, SqlSelect.HAVING_OPERAND);
- registerSubQueries(aggScope, select.getSelectList());
+ registerSubQueries(aggScope, SqlNonNullableAccessors.getSelectList(select));
final SqlNodeList orderList = select.getOrderList();
if (orderList != null) {
// If the query is 'SELECT DISTINCT', restrict the columns
@@ -2647,7 +2859,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
registerQuery(
parentScope,
usingScope,
- deleteCall.getSourceSelect(),
+ SqlNonNullableAccessors.getSourceSelect(deleteCall),
enclosingNode,
null,
false);
@@ -2664,7 +2876,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
registerQuery(
parentScope,
usingScope,
- updateCall.getSourceSelect(),
+ SqlNonNullableAccessors.getSourceSelect(updateCall),
enclosingNode,
null,
false);
@@ -2679,7 +2891,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
registerQuery(
parentScope,
usingScope,
- mergeCall.getSourceSelect(),
+ SqlNonNullableAccessors.getSourceSelect(mergeCall),
enclosingNode,
null,
false);
@@ -2688,24 +2900,22 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// or the target table, so set its parent scope to the merge's
// source select; when validating the update, skip the feature
// validation check
- if (mergeCall.getUpdateCall() != null) {
+ SqlUpdate mergeUpdateCall = mergeCall.getUpdateCall();
+ if (mergeUpdateCall != null) {
registerQuery(
- clauseScopes.get(IdPair.of(mergeCall.getSourceSelect(), Clause.WHERE)),
+ getScope(
+ SqlNonNullableAccessors.getSourceSelect(mergeCall),
+ Clause.WHERE),
null,
- mergeCall.getUpdateCall(),
+ mergeUpdateCall,
enclosingNode,
null,
false,
false);
}
- if (mergeCall.getInsertCall() != null) {
- registerQuery(
- parentScope,
- null,
- mergeCall.getInsertCall(),
- enclosingNode,
- null,
- false);
+ SqlInsert mergeInsertCall = mergeCall.getInsertCall();
+ if (mergeInsertCall != null) {
+ registerQuery(parentScope, null, mergeInsertCall, enclosingNode, null, false);
}
break;
@@ -2732,7 +2942,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
CollectScope cs = new CollectScope(parentScope, usingScope, call);
final CollectNamespace tableConstructorNs =
new CollectNamespace(call, cs, enclosingNode);
- final String alias2 = deriveAlias(node, nextGeneratedId++);
+ final String alias2 = deriveAliasNonNull(node, nextGeneratedId++);
registerNamespace(usingScope, alias2, tableConstructorNs, forceNullable);
operands = call.getOperandList();
for (int i = 0; i < operands.size(); i++) {
@@ -2747,10 +2957,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void registerSetop(
SqlValidatorScope parentScope,
- SqlValidatorScope usingScope,
+ @Nullable SqlValidatorScope usingScope,
SqlNode node,
SqlNode enclosingNode,
- String alias,
+ @Nullable String alias,
boolean forceNullable) {
SqlCall call = (SqlCall) node;
final SetopNamespace setopNamespace = createSetopNamespace(call, enclosingNode);
@@ -2765,10 +2975,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void registerWith(
SqlValidatorScope parentScope,
- SqlValidatorScope usingScope,
+ @Nullable SqlValidatorScope usingScope,
SqlWith with,
SqlNode enclosingNode,
- String alias,
+ @Nullable String alias,
boolean forceNullable,
boolean checkUpdate) {
final WithNamespace withNamespace = new WithNamespace(this, with, enclosingNode);
@@ -2789,12 +2999,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
registerQuery(scope, null, with.body, enclosingNode, alias, forceNullable, checkUpdate);
}
+ @Override
public boolean isAggregate(SqlSelect select) {
if (getAggregate(select) != null) {
return true;
}
// Also when nested window aggregates are present
- for (SqlCall call : overFinder.findAll(select.getSelectList())) {
+ for (SqlCall call : overFinder.findAll(SqlNonNullableAccessors.getSelectList(select))) {
assert call.getKind() == SqlKind.OVER;
if (isNestedAggregateWindow(call.operand(0))) {
return true;
@@ -2823,7 +3034,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* <p>The node is useful context for error messages, but you cannot assume that the node is the
* only aggregate function.
*/
- protected SqlNode getAggregate(SqlSelect select) {
+ protected @Nullable SqlNode getAggregate(SqlSelect select) {
SqlNode node = select.getGroup();
if (node != null) {
return node;
@@ -2836,7 +3047,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
/** If there is at least one call to an aggregate function, returns the first. */
- private SqlNode getAgg(SqlSelect select) {
+ private @Nullable SqlNode getAgg(SqlSelect select) {
final SelectScope selectScope = getRawSelectScope(select);
if (selectScope != null) {
final List<SqlNode> selectList = selectScope.getExpandedSelectList();
@@ -2844,7 +3055,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return aggFinder.findAgg(selectList);
}
}
- return aggFinder.findAgg(select.getSelectList());
+ return aggFinder.findAgg(SqlNonNullableAccessors.getSelectList(select));
}
@Deprecated
@@ -2858,10 +3069,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case MULTISET_VALUE_CONSTRUCTOR:
validateFeature(RESOURCE.sQLFeature_S271(), node.getParserPosition());
break;
+ default:
+ break;
}
}
- private void registerSubQueries(SqlValidatorScope parentScope, SqlNode node) {
+ private void registerSubQueries(SqlValidatorScope parentScope, @Nullable SqlNode node) {
if (node == null) {
return;
}
@@ -2917,6 +3130,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
registerSubQueries(parentScope, operand);
}
+ @Override
public void validateIdentifier(SqlIdentifier id, SqlValidatorScope scope) {
final SqlQualified fqId = scope.fullyQualify(id);
if (this.config.columnReferenceExpansion()) {
@@ -2928,6 +3142,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public void validateLiteral(SqlLiteral literal) {
switch (literal.getTypeName()) {
case DECIMAL:
@@ -2940,7 +3155,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
//
// jhyde 2006/12/21: I think the limits should be baked into the
// type system, not dependent on the calculator implementation.
- BigDecimal bd = (BigDecimal) literal.getValue();
+ BigDecimal bd = literal.getValueAs(BigDecimal.class);
BigInteger unscaled = bd.unscaledValue();
long longValue = unscaled.longValue();
if (!BigInteger.valueOf(longValue).equals(unscaled)) {
@@ -2955,7 +3170,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
break;
case BINARY:
- final BitString bitString = (BitString) literal.getValue();
+ final BitString bitString = literal.getValueAs(BitString.class);
if ((bitString.getBitCount() % 8) != 0) {
throw newValidationError(literal, RESOURCE.binaryLiteralOdd());
}
@@ -3009,7 +3224,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
private void validateLiteralAsDouble(SqlLiteral literal) {
- BigDecimal bd = (BigDecimal) literal.getValue();
+ BigDecimal bd = literal.getValueAs(BigDecimal.class);
double d = bd.doubleValue();
if (Double.isInfinite(d) || Double.isNaN(d)) {
// overflow
@@ -3020,6 +3235,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// REVIEW jvs 4-Aug-2004: what about underflow?
}
+ @Override
public void validateIntervalQualifier(SqlIntervalQualifier qualifier) {
assert qualifier != null;
boolean startPrecisionOutOfRange = false;
@@ -3063,7 +3279,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @param scope Scope
*/
protected void validateFrom(SqlNode node, RelDataType targetRowType, SqlValidatorScope scope) {
- Objects.requireNonNull(targetRowType);
+ requireNonNull(targetRowType, "targetRowType");
switch (node.getKind()) {
case AS:
case TABLE_REF:
@@ -3088,7 +3304,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Validate the namespace representation of the node, just in case the
// validation did not occur implicitly.
- getNamespace(node, scope).validate(targetRowType);
+ getNamespaceOrThrow(node, scope).validate(targetRowType);
}
protected void validateOver(SqlCall call, SqlValidatorScope scope) {
@@ -3110,7 +3326,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (namespace != null) {
SqlValidatorTable sqlValidatorTable = namespace.getTable();
if (sqlValidatorTable != null) {
- Table table = sqlValidatorTable.unwrap(Table.class);
+ Table table = sqlValidatorTable.table();
String column = Util.last(identifier.names);
if (table.isRolledUp(column)) {
@@ -3128,7 +3344,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
boolean natural = join.isNatural();
final JoinType joinType = join.getJoinType();
final JoinConditionType conditionType = join.getConditionType();
- final SqlValidatorScope joinScope = scopes.get(join);
+ final SqlValidatorScope joinScope = getScopeOrThrow(join); // getJoinScope?
validateFrom(left, unknownType, joinScope);
validateFrom(right, unknownType, joinScope);
@@ -3138,15 +3354,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
Preconditions.checkArgument(condition == null);
break;
case ON:
- Preconditions.checkArgument(condition != null);
+ requireNonNull(condition, "join.getCondition()");
SqlNode expandedCondition = expand(condition, joinScope);
join.setOperand(5, expandedCondition);
- condition = join.getCondition();
+ condition = getCondition(join);
validateWhereOrOn(joinScope, condition, "ON");
checkRollUp(null, join, condition, joinScope, "ON");
break;
case USING:
- SqlNodeList list = (SqlNodeList) condition;
+ SqlNodeList list = (SqlNodeList) requireNonNull(condition, "join.getCondition()");
// Parser ensures that using clause is not empty.
Preconditions.checkArgument(list.size() > 0, "Empty USING clause");
@@ -3178,8 +3394,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Join on fields that occur exactly once on each side. Ignore
// fields that occur more than once on either side.
- final RelDataType leftRowType = getNamespace(left).getRowType();
- final RelDataType rightRowType = getNamespace(right).getRowType();
+ final RelDataType leftRowType = getNamespaceOrThrow(left).getRowType();
+ final RelDataType rightRowType = getNamespaceOrThrow(right).getRowType();
final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
List<String> naturalColumnNames =
SqlValidatorUtil.deriveNaturalJoinColumnList(
@@ -3187,8 +3403,24 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Check compatibility of the chosen columns.
for (String name : naturalColumnNames) {
- final RelDataType leftColType = nameMatcher.field(leftRowType, name).getType();
- final RelDataType rightColType = nameMatcher.field(rightRowType, name).getType();
+ final RelDataType leftColType =
+ requireNonNull(
+ nameMatcher.field(leftRowType, name),
+ () ->
+ "unable to find left field "
+ + name
+ + " in "
+ + leftRowType)
+ .getType();
+ final RelDataType rightColType =
+ requireNonNull(
+ nameMatcher.field(rightRowType, name),
+ () ->
+ "unable to find right field "
+ + name
+ + " in "
+ + rightRowType)
+ .getType();
if (!SqlTypeUtil.isComparable(leftColType, rightColType)) {
throw newValidationError(
join,
@@ -3258,7 +3490,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private RelDataType validateUsingCol(SqlIdentifier id, SqlNode leftOrRight) {
if (id.names.size() == 1) {
String name = id.names.get(0);
- final SqlValidatorNamespace namespace = getNamespace(leftOrRight);
+ final SqlValidatorNamespace namespace = getNamespaceOrThrow(leftOrRight);
final RelDataType rowType = namespace.getRowType();
final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
final RelDataTypeField field = nameMatcher.field(rowType, name);
@@ -3281,20 +3513,19 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
protected void validateSelect(SqlSelect select, RelDataType targetRowType) {
assert targetRowType != null;
// Namespace is either a select namespace or a wrapper around one.
- final SelectNamespace ns = getNamespace(select).unwrap(SelectNamespace.class);
+ final SelectNamespace ns = getNamespaceOrThrow(select).unwrap(SelectNamespace.class);
// Its rowtype is null, meaning it hasn't been validated yet.
// This is important, because we need to take the targetRowType into
// account.
assert ns.rowType == null;
- if (select.isDistinct()) {
- validateFeature(
- RESOURCE.sQLFeature_E051_01(),
- select.getModifierNode(SqlSelectKeyword.DISTINCT).getParserPosition());
+ SqlNode distinctNode = select.getModifierNode(SqlSelectKeyword.DISTINCT);
+ if (distinctNode != null) {
+ validateFeature(RESOURCE.sQLFeature_E051_01(), distinctNode.getParserPosition());
}
- final SqlNodeList selectItems = select.getSelectList();
+ final SqlNodeList selectItems = SqlNonNullableAccessors.getSelectList(select);
RelDataType fromType = unknownType;
if (selectItems.size() == 1) {
final SqlNode selectItem = selectItems.get(0);
@@ -3312,19 +3543,25 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
// Make sure that items in FROM clause have distinct aliases.
- final SelectScope fromScope = (SelectScope) getFromScope(select);
- List<String> names = fromScope.getChildNames();
+ final SelectScope fromScope =
+ (SelectScope) requireNonNull(getFromScope(select), () -> "fromScope for " + select);
+ List<@Nullable String> names = fromScope.getChildNames();
if (!catalogReader.nameMatcher().isCaseSensitive()) {
+ //noinspection RedundantTypeArguments
names =
names.stream()
- .map(s -> s.toUpperCase(Locale.ROOT))
+ .<@Nullable String>map(
+ s -> s == null ? null : s.toUpperCase(Locale.ROOT))
.collect(Collectors.toList());
}
final int duplicateAliasOrdinal = Util.firstDuplicate(names);
if (duplicateAliasOrdinal >= 0) {
final ScopeChild child = fromScope.children.get(duplicateAliasOrdinal);
throw newValidationError(
- child.namespace.getEnclosingNode(), RESOURCE.fromAliasDuplicate(child.name));
+ requireNonNull(
+ child.namespace.getEnclosingNode(),
+ () -> "enclosingNode of namespace of " + child.name),
+ RESOURCE.fromAliasDuplicate(child.name));
}
if (select.getFrom() == null) {
@@ -3364,7 +3601,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void checkRollUpInSelectList(SqlSelect select) {
SqlValidatorScope scope = getSelectScope(select);
- for (SqlNode item : select.getSelectList()) {
+ for (SqlNode item : SqlNonNullableAccessors.getSelectList(select)) {
checkRollUp(null, select, item, scope);
}
}
@@ -3387,7 +3624,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
- private void checkRollUpInWindow(SqlWindow window, SqlValidatorScope scope) {
+ private void checkRollUpInWindow(@Nullable SqlWindow window, SqlValidatorScope scope) {
if (window != null) {
for (SqlNode node : window.getPartitionList()) {
checkRollUp(null, window, node, scope, "PARTITION BY");
@@ -3405,7 +3642,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
- private SqlNode stripDot(SqlNode node) {
+ private static @Nullable SqlNode stripDot(@Nullable SqlNode node) {
if (node != null && node.getKind() == SqlKind.DOT) {
return stripDot(((SqlCall) node).operand(0));
}
@@ -3413,42 +3650,47 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
private void checkRollUp(
- SqlNode grandParent,
- SqlNode parent,
- SqlNode current,
+ @Nullable SqlNode grandParent,
+ @Nullable SqlNode parent,
+ @Nullable SqlNode current,
SqlValidatorScope scope,
- String optionalClause) {
+ @Nullable String optionalClause) {
current = stripAs(current);
if (current instanceof SqlCall && !(current instanceof SqlSelect)) {
// Validate OVER separately
checkRollUpInWindow(getWindowInOver(current), scope);
current = stripOver(current);
- List<SqlNode> children = ((SqlCall) stripAs(stripDot(current))).getOperandList();
+ SqlNode stripDot = requireNonNull(stripDot(current), "stripDot(current)");
+ List<? extends @Nullable SqlNode> children =
+ ((SqlCall) stripAs(stripDot)).getOperandList();
for (SqlNode child : children) {
checkRollUp(parent, current, child, scope, optionalClause);
}
} else if (current instanceof SqlIdentifier) {
SqlIdentifier id = (SqlIdentifier) current;
if (!id.isStar() && isRolledUpColumn(id, scope)) {
- if (!isAggregation(parent.getKind())
+ if (!isAggregation(requireNonNull(parent, "parent").getKind())
|| !isRolledUpColumnAllowedInAgg(
id, scope, (SqlCall) parent, grandParent)) {
String context =
optionalClause != null ? optionalClause : parent.getKind().toString();
throw newValidationError(
- id, RESOURCE.rolledUpNotAllowed(deriveAlias(id, 0), context));
+ id, RESOURCE.rolledUpNotAllowed(deriveAliasNonNull(id, 0), context));
}
}
}
}
private void checkRollUp(
- SqlNode grandParent, SqlNode parent, SqlNode current, SqlValidatorScope scope) {
+ @Nullable SqlNode grandParent,
+ SqlNode parent,
+ @Nullable SqlNode current,
+ SqlValidatorScope scope) {
checkRollUp(grandParent, parent, current, scope, null);
}
- private SqlWindow getWindowInOver(SqlNode over) {
+ private static @Nullable SqlWindow getWindowInOver(SqlNode over) {
if (over.getKind() == SqlKind.OVER) {
SqlNode window = ((SqlCall) over).getOperandList().get(1);
if (window instanceof SqlWindow) {
@@ -3469,7 +3711,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
- private Pair<String, String> findTableColumnPair(
+ private @Nullable Pair<String, String> findTableColumnPair(
SqlIdentifier identifier, SqlValidatorScope scope) {
final SqlCall call = makeNullaryCall(identifier);
if (call != null) {
@@ -3487,7 +3729,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Returns true iff the given column is valid inside the given aggCall.
private boolean isRolledUpColumnAllowedInAgg(
- SqlIdentifier identifier, SqlValidatorScope scope, SqlCall aggCall, SqlNode parent) {
+ SqlIdentifier identifier,
+ SqlValidatorScope scope,
+ SqlCall aggCall,
+ @Nullable SqlNode parent) {
Pair<String, String> pair = findTableColumnPair(identifier, scope);
if (pair == null) {
@@ -3496,15 +3741,24 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
String columnName = pair.right;
- SqlValidatorTable sqlValidatorTable = scope.fullyQualify(identifier).namespace.getTable();
- if (sqlValidatorTable != null) {
- Table table = sqlValidatorTable.unwrap(Table.class);
+ Table table = resolveTable(identifier, scope);
+ if (table != null) {
return table.rolledUpColumnValidInsideAgg(
columnName, aggCall, parent, catalogReader.getConfig());
}
return true;
}
+ private static @Nullable Table resolveTable(SqlIdentifier identifier, SqlValidatorScope scope) {
+ SqlQualified fullyQualified = scope.fullyQualify(identifier);
+ assert fullyQualified.namespace != null : "namespace must not be null in " + fullyQualified;
+ SqlValidatorTable sqlValidatorTable = fullyQualified.namespace.getTable();
+ if (sqlValidatorTable != null) {
+ return sqlValidatorTable.table();
+ }
+ return null;
+ }
+
// Returns true iff the given column is actually rolled up.
private boolean isRolledUpColumn(SqlIdentifier identifier, SqlValidatorScope scope) {
Pair<String, String> pair = findTableColumnPair(identifier, scope);
@@ -3515,15 +3769,14 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
String columnName = pair.right;
- SqlValidatorTable sqlValidatorTable = scope.fullyQualify(identifier).namespace.getTable();
- if (sqlValidatorTable != null) {
- Table table = sqlValidatorTable.unwrap(Table.class);
+ Table table = resolveTable(identifier, scope);
+ if (table != null) {
return table.isRolledUp(columnName);
}
return false;
}
- private boolean shouldCheckForRollUp(SqlNode from) {
+ private static boolean shouldCheckForRollUp(@Nullable SqlNode from) {
if (from != null) {
SqlKind kind = stripAs(from).getKind();
return kind != SqlKind.VALUES && kind != SqlKind.SELECT;
@@ -3544,6 +3797,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
switch (modality) {
case STREAM:
throw newValidationError(query, Static.RESOURCE.cannotStreamValues());
+ default:
+ break;
}
} else {
assert query.isA(SqlKind.SET_QUERY);
@@ -3559,7 +3814,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
/** Return the intended modality of a SELECT or set-op. */
- private SqlModality deduceModality(SqlNode query) {
+ private static SqlModality deduceModality(SqlNode query) {
if (query instanceof SqlSelect) {
SqlSelect select = (SqlSelect) query;
return select.getModifierNode(SqlSelectKeyword.STREAM) != null
@@ -3574,8 +3829,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public boolean validateModality(SqlSelect select, SqlModality modality, boolean fail) {
- final SelectScope scope = getRawSelectScope(select);
+ final SelectScope scope = getRawSelectScopeNonNull(select);
switch (modality) {
case STREAM:
@@ -3583,9 +3839,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
for (ScopeChild child : scope.children) {
if (!child.namespace.supportsModality(modality)) {
if (fail) {
+ SqlNode node = SqlNonNullableAccessors.getNode(child);
throw newValidationError(
- child.namespace.getNode(),
- Static.RESOURCE.cannotConvertToStream(child.name));
+ node, Static.RESOURCE.cannotConvertToStream(child.name));
} else {
return false;
}
@@ -3616,9 +3872,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
for (ScopeChild child : scope.children) {
if (!child.namespace.supportsModality(modality)) {
if (fail) {
+ SqlNode node = SqlNonNullableAccessors.getNode(child);
throw newValidationError(
- child.namespace.getNode(),
- Static.RESOURCE.cannotConvertToRelation(child.name));
+ node, Static.RESOURCE.cannotConvertToRelation(child.name));
} else {
return false;
}
@@ -3641,6 +3897,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return false;
}
}
+ break;
+ default:
+ break;
}
}
@@ -3657,20 +3916,25 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return false;
}
}
+ break;
+ default:
+ break;
}
}
return true;
}
/** Returns whether the prefix is sorted. */
- private boolean hasSortedPrefix(SelectScope scope, SqlNodeList orderList) {
+ private static boolean hasSortedPrefix(SelectScope scope, SqlNodeList orderList) {
return isSortCompatible(scope, orderList.get(0), false);
}
- private boolean isSortCompatible(SelectScope scope, SqlNode node, boolean descending) {
+ private static boolean isSortCompatible(SelectScope scope, SqlNode node, boolean descending) {
switch (node.getKind()) {
case DESCENDING:
return isSortCompatible(scope, ((SqlCall) node).getOperandList().get(0), true);
+ default:
+ break;
}
final SqlMonotonicity monotonicity = scope.getMonotonicity(node);
switch (monotonicity) {
@@ -3685,21 +3949,22 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @SuppressWarnings({"unchecked", "rawtypes"})
protected void validateWindowClause(SqlSelect select) {
final SqlNodeList windowList = select.getWindowList();
- @SuppressWarnings("unchecked")
- final List<SqlWindow> windows = (List) windowList.getList();
- if (windows.isEmpty()) {
+ if (windowList.isEmpty()) {
return;
}
- final SelectScope windowScope = (SelectScope) getFromScope(select);
- assert windowScope != null;
+ final SelectScope windowScope =
+ (SelectScope) requireNonNull(getFromScope(select), () -> "fromScope for " + select);
// 1. ensure window names are simple
// 2. ensure they are unique within this scope
- for (SqlWindow window : windows) {
- SqlIdentifier declName = window.getDeclName();
+ for (SqlWindow window : (List<SqlWindow>) (List) windowList) {
+ SqlIdentifier declName =
+ requireNonNull(
+ window.getDeclName(), () -> "window.getDeclName() for " + window);
if (!declName.isSimple()) {
throw newValidationError(declName, RESOURCE.windowNameMustBeSimple());
}
@@ -3713,17 +3978,17 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// 7.10 rule 2
// Check for pairs of windows which are equivalent.
- for (int i = 0; i < windows.size(); i++) {
- SqlNode window1 = windows.get(i);
- for (int j = i + 1; j < windows.size(); j++) {
- SqlNode window2 = windows.get(j);
+ for (int i = 0; i < windowList.size(); i++) {
+ SqlNode window1 = windowList.get(i);
+ for (int j = i + 1; j < windowList.size(); j++) {
+ SqlNode window2 = windowList.get(j);
if (window1.equalsDeep(window2, Litmus.IGNORE)) {
throw newValidationError(window2, RESOURCE.dupWindowSpec());
}
}
}
- for (SqlWindow window : windows) {
+ for (SqlWindow window : (List<SqlWindow>) (List) windowList) {
final SqlNodeList expandedOrderList =
(SqlNodeList) expand(window.getOrderList(), windowScope);
window.setOrderList(expandedOrderList);
@@ -3739,20 +4004,22 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
windowList.validate(this, windowScope);
}
+ @Override
public void validateWith(SqlWith with, SqlValidatorScope scope) {
- final SqlValidatorNamespace namespace = getNamespace(with);
+ final SqlValidatorNamespace namespace = getNamespaceOrThrow(with);
validateNamespace(namespace, unknownType);
}
+ @Override
public void validateWithItem(SqlWithItem withItem) {
- if (withItem.columnList != null) {
+ SqlNodeList columnList = withItem.columnList;
+ if (columnList != null) {
final RelDataType rowType = getValidatedNodeType(withItem.query);
final int fieldCount = rowType.getFieldCount();
- if (withItem.columnList.size() != fieldCount) {
- throw newValidationError(withItem.columnList, RESOURCE.columnCountMismatch());
+ if (columnList.size() != fieldCount) {
+ throw newValidationError(columnList, RESOURCE.columnCountMismatch());
}
- SqlValidatorUtil.checkIdentifierListForDuplicates(
- withItem.columnList.getList(), validationErrorFunction);
+ SqlValidatorUtil.checkIdentifierListForDuplicates(columnList, validationErrorFunction);
} else {
// Luckily, field names have not been make unique yet.
final List<String> fieldNames = getValidatedNodeType(withItem.query).getFieldNames();
@@ -3764,6 +4031,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public void validateSequenceValue(SqlValidatorScope scope, SqlIdentifier id) {
// Resolve identifier as a table.
final SqlValidatorScope.ResolvedImpl resolved = new SqlValidatorScope.ResolvedImpl();
@@ -3775,30 +4043,36 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// We've found a table. But is it a sequence?
final SqlValidatorNamespace ns = resolved.only().namespace;
if (ns instanceof TableNamespace) {
- final Table table = ns.getTable().unwrap(Table.class);
+ final Table table = getTable(ns).table();
switch (table.getJdbcTableType()) {
case SEQUENCE:
case TEMPORARY_SEQUENCE:
return;
+ default:
+ break;
}
}
throw newValidationError(id, RESOURCE.notASequence(id.toString()));
}
- public SqlValidatorScope getWithScope(SqlNode withItem) {
+ @Override
+ public @Nullable SqlValidatorScope getWithScope(SqlNode withItem) {
assert withItem.getKind() == SqlKind.WITH_ITEM;
return scopes.get(withItem);
}
+ @Override
public TypeCoercion getTypeCoercion() {
assert config.typeCoercionEnabled();
return this.typeCoercion;
}
+ @Override
public Config config() {
return this.config;
}
+ @Override
public SqlValidator transform(UnaryOperator<Config> transform) {
this.config = transform.apply(this.config);
return this;
@@ -3823,7 +4097,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
final SqlValidatorScope orderScope = getOrderScope(select);
- Objects.requireNonNull(orderScope);
+ requireNonNull(orderScope, "orderScope");
List<SqlNode> expandList = new ArrayList<>();
for (SqlNode orderItem : orderList) {
@@ -3879,12 +4153,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
RESOURCE.sQLConformance_OrderByDesc(), orderItem.getParserPosition());
validateOrderItem(select, ((SqlCall) orderItem).operand(0));
return;
+ default:
+ break;
}
final SqlValidatorScope orderScope = getOrderScope(select);
validateExpr(orderItem, orderScope);
}
+ @Override
public SqlNode expandOrderExpr(SqlSelect select, SqlNode orderExpr) {
final SqlNode newSqlNode = new OrderExpressionExpander(select, orderExpr).go();
if (newSqlNode != orderExpr) {
@@ -3960,7 +4237,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void validateGroupItem(
SqlValidatorScope groupScope,
- AggregatingSelectScope aggregatingScope,
+ @Nullable AggregatingSelectScope aggregatingScope,
SqlNode groupItem) {
switch (groupItem.getKind()) {
case GROUPING_SETS:
@@ -3979,7 +4256,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void validateGroupingSets(
SqlValidatorScope groupScope,
- AggregatingSelectScope aggregatingScope,
+ @Nullable AggregatingSelectScope aggregatingScope,
SqlCall groupItem) {
for (SqlNode node : groupItem.getOperandList()) {
validateGroupItem(groupScope, aggregatingScope, node);
@@ -4077,7 +4354,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (config.identifierExpansion()) {
select.setSelectList(newSelectList);
}
- getRawSelectScope(select).setExpandedSelectList(expandedSelectItems);
+ getRawSelectScopeNonNull(select).setExpandedSelectList(expandedSelectItems);
// TODO: when SELECT appears as a value sub-query, should be using
// something other than unknownType for targetRowType
@@ -4133,7 +4410,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
Set<String> aliasList,
List<Map.Entry<String, RelDataType>> fieldList) {
// A scalar sub-query only has one output column.
- if (1 != selectItem.getSelectList().size()) {
+ if (1 != SqlNonNullableAccessors.getSelectList(selectItem).size()) {
throw newValidationError(selectItem, RESOURCE.onlyScalarSubQueryAllowed());
}
@@ -4141,7 +4418,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
expandedSelectItems.add(selectItem);
// Get or generate alias and add to list.
- final String alias = deriveAlias(selectItem, aliasList.size());
+ final String alias = deriveAliasNonNull(selectItem, aliasList.size());
aliasList.add(alias);
final SelectScope scope = (SelectScope) getWhereScope(parentSelect);
@@ -4169,7 +4446,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @return Rowtype
*/
protected RelDataType createTargetRowType(
- SqlValidatorTable table, SqlNodeList targetColumnList, boolean append) {
+ SqlValidatorTable table, @Nullable SqlNodeList targetColumnList, boolean append) {
RelDataType baseRowType = table.getRowType();
if (targetColumnList == null) {
return baseRowType;
@@ -4202,8 +4479,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return typeFactory.createStructType(fields);
}
+ @Override
public void validateInsert(SqlInsert insert) {
- final SqlValidatorNamespace targetNamespace = getNamespace(insert);
+ final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(insert);
validateNamespace(targetNamespace, unknownType);
final RelOptTable relOptTable =
SqlValidatorUtil.getRelOptTable(
@@ -4213,8 +4491,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
null);
final SqlValidatorTable table =
relOptTable == null
- ? targetNamespace.getTable()
- : relOptTable.unwrap(SqlValidatorTable.class);
+ ? getTable(targetNamespace)
+ : relOptTable.unwrapOrThrow(SqlValidatorTable.class);
// INSERT has an optional column name list. If present then
// reduce the rowtype to the columns specified. If not present
@@ -4236,13 +4514,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// from validateSelect above). It would be better if that information
// were used here so that we never saw any untyped nulls during
// checkTypeAssignment.
- final RelDataType sourceRowType = getNamespace(source).getRowType();
+ final RelDataType sourceRowType = getNamespaceOrThrow(source).getRowType();
final RelDataType logicalTargetRowType = getLogicalTargetRowType(targetRowType, insert);
setValidatedNodeType(insert, logicalTargetRowType);
final RelDataType logicalSourceRowType = getLogicalSourceRowType(sourceRowType, insert);
final List<ColumnStrategy> strategies =
- table.unwrap(RelOptTable.class).getColumnStrategies();
+ table.unwrapOrThrow(RelOptTable.class).getColumnStrategies();
final RelDataType realTargetRowType =
typeFactory.createStructType(
@@ -4288,7 +4566,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final ModifiableViewTable modifiableViewTable =
validatorTable.unwrap(ModifiableViewTable.class);
if (modifiableViewTable != null && source instanceof SqlCall) {
- final Table table = modifiableViewTable.unwrap(Table.class);
+ final Table table = modifiableViewTable.getTable();
final RelDataType tableRowType = table.getRowType(typeFactory);
final List<RelDataTypeField> tableFields = tableRowType.getFieldList();
@@ -4302,15 +4580,19 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// Determine columns (indexed to the underlying table) that need
// to be validated against the view constraint.
+ @SuppressWarnings("RedundantCast")
final ImmutableBitSet targetColumns =
- ImmutableBitSet.of(tableIndexToTargetField.keySet());
- final ImmutableBitSet constrainedColumns = ImmutableBitSet.of(projectMap.keySet());
- final ImmutableBitSet constrainedTargetColumns =
- targetColumns.intersect(constrainedColumns);
+ ImmutableBitSet.of((Iterable<Integer>) tableIndexToTargetField.keySet());
+ @SuppressWarnings("RedundantCast")
+ final ImmutableBitSet constrainedColumns =
+ ImmutableBitSet.of((Iterable<Integer>) projectMap.keySet());
+ @SuppressWarnings("assignment.type.incompatible")
+ List<@KeyFor({"tableIndexToTargetField", "projectMap"}) Integer>
+ constrainedTargetColumns = targetColumns.intersect(constrainedColumns).asList();
// Validate insert values against the view constraint.
final List<SqlNode> values = ((SqlCall) source).getOperandList();
- for (final int colIndex : constrainedTargetColumns.asList()) {
+ for (final int colIndex : constrainedTargetColumns) {
final String colName = tableFields.get(colIndex).getName();
final RelDataTypeField targetField = tableIndexToTargetField.get(colIndex);
for (SqlNode row : values) {
@@ -4340,7 +4622,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final ModifiableViewTable modifiableViewTable =
validatorTable.unwrap(ModifiableViewTable.class);
if (modifiableViewTable != null) {
- final Table table = modifiableViewTable.unwrap(Table.class);
+ final Table table = modifiableViewTable.getTable();
final RelDataType tableRowType = table.getRowType(typeFactory);
final Map<Integer, RexNode> projectMap =
@@ -4350,23 +4632,26 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlValidatorUtil.mapNameToIndex(tableRowType.getFieldList());
// Validate update values against the view constraint.
- final List<SqlNode> targets = update.getTargetColumnList().getList();
- final List<SqlNode> sources = update.getSourceExpressionList().getList();
- for (final Pair<SqlNode, SqlNode> column : Pair.zip(targets, sources)) {
- final String columnName = ((SqlIdentifier) column.left).getSimple();
- final Integer columnIndex = nameToIndex.get(columnName);
- if (projectMap.containsKey(columnIndex)) {
- final RexNode columnConstraint = projectMap.get(columnIndex);
- final ValidationError validationError =
- new ValidationError(
- column.right,
- RESOURCE.viewConstraintNotSatisfied(
- columnName,
- Util.last(validatorTable.getQualifiedName())));
- RelOptUtil.validateValueAgainstConstraint(
- column.right, columnConstraint, validationError);
- }
- }
+ final List<String> targetNames =
+ SqlIdentifier.simpleNames(update.getTargetColumnList());
+ final List<SqlNode> sources = update.getSourceExpressionList();
+ Pair.forEach(
+ targetNames,
+ sources,
+ (columnName, expr) -> {
+ final Integer columnIndex = nameToIndex.get(columnName);
+ if (projectMap.containsKey(columnIndex)) {
+ final RexNode columnConstraint = projectMap.get(columnIndex);
+ final ValidationError validationError =
+ new ValidationError(
+ expr,
+ RESOURCE.viewConstraintNotSatisfied(
+ columnName,
+ Util.last(validatorTable.getQualifiedName())));
+ RelOptUtil.validateValueAgainstConstraint(
+ expr, columnConstraint, validationError);
+ }
+ });
}
}
@@ -4424,12 +4709,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
throw newValidationError(
node, RESOURCE.insertIntoAlwaysGenerated(field.getName()));
}
+ break;
+ default:
+ break;
}
}
}
/** Returns whether a query uses {@code DEFAULT} to populate a given column. */
- private boolean isValuesWithDefault(SqlNode source, int column) {
+ private static boolean isValuesWithDefault(SqlNode source, int column) {
switch (source.getKind()) {
case VALUES:
for (SqlNode operand : ((SqlCall) source).getOperandList()) {
@@ -4438,16 +4726,20 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
return true;
+ default:
+ break;
}
return false;
}
- private boolean isRowWithDefault(SqlNode operand, int column) {
+ private static boolean isRowWithDefault(SqlNode operand, int column) {
switch (operand.getKind()) {
case ROW:
final SqlCall row = (SqlCall) operand;
return row.getOperandList().size() >= column
&& row.getOperandList().get(column).getKind() == SqlKind.DEFAULT;
+ default:
+ break;
}
return false;
}
@@ -4457,14 +4749,14 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
&& this.config.sqlConformance().isInsertSubsetColumnsAllowed()) {
// Target an implicit subset of columns.
final SqlNode source = insert.getSource();
- final RelDataType sourceRowType = getNamespace(source).getRowType();
+ final RelDataType sourceRowType = getNamespaceOrThrow(source).getRowType();
final RelDataType logicalSourceRowType = getLogicalSourceRowType(sourceRowType, insert);
final RelDataType implicitTargetRowType =
typeFactory.createStructType(
targetRowType
.getFieldList()
.subList(0, logicalSourceRowType.getFieldCount()));
- final SqlValidatorNamespace targetNamespace = getNamespace(insert);
+ final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(insert);
validateNamespace(targetNamespace, implicitTargetRowType);
return implicitTargetRowType;
} else {
@@ -4492,7 +4784,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @param query The query
*/
protected void checkTypeAssignment(
- SqlValidatorScope sourceScope,
+ @Nullable SqlValidatorScope sourceScope,
SqlValidatorTable table,
RelDataType sourceRowType,
RelDataType targetRowType,
@@ -4565,7 +4857,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @param sourceCount Number of expressions
* @return Ordinal'th expression, never null
*/
- private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) {
+ private static SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) {
if (query instanceof SqlInsert) {
SqlInsert insert = (SqlInsert) query;
if (insert.getTargetColumnList() != null) {
@@ -4578,12 +4870,14 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (update.getSourceExpressionList() != null) {
return update.getSourceExpressionList().get(ordinal);
} else {
- return getNthExpr(update.getSourceSelect(), ordinal, sourceCount);
+ return getNthExpr(
+ SqlNonNullableAccessors.getSourceSelect(update), ordinal, sourceCount);
}
} else if (query instanceof SqlSelect) {
SqlSelect select = (SqlSelect) query;
- if (select.getSelectList().size() == sourceCount) {
- return select.getSelectList().get(ordinal);
+ SqlNodeList selectList = SqlNonNullableAccessors.getSelectList(select);
+ if (selectList.size() == sourceCount) {
+ return selectList.get(ordinal);
} else {
return query; // give up
}
@@ -4592,35 +4886,37 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public void validateDelete(SqlDelete call) {
- final SqlSelect sqlSelect = call.getSourceSelect();
+ final SqlSelect sqlSelect = SqlNonNullableAccessors.getSourceSelect(call);
validateSelect(sqlSelect, unknownType);
- final SqlValidatorNamespace targetNamespace = getNamespace(call);
+ final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(call);
validateNamespace(targetNamespace, unknownType);
final SqlValidatorTable table = targetNamespace.getTable();
validateAccess(call.getTargetTable(), table, SqlAccessEnum.DELETE);
}
+ @Override
public void validateUpdate(SqlUpdate call) {
- final SqlValidatorNamespace targetNamespace = getNamespace(call);
+ final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(call);
validateNamespace(targetNamespace, unknownType);
final RelOptTable relOptTable =
SqlValidatorUtil.getRelOptTable(
targetNamespace,
- catalogReader.unwrap(Prepare.CatalogReader.class),
+ castNonNull(catalogReader.unwrap(Prepare.CatalogReader.class)),
null,
null);
final SqlValidatorTable table =
relOptTable == null
- ? targetNamespace.getTable()
- : relOptTable.unwrap(SqlValidatorTable.class);
+ ? getTable(targetNamespace)
+ : relOptTable.unwrapOrThrow(SqlValidatorTable.class);
final RelDataType targetRowType =
createTargetRowType(table, call.getTargetColumnList(), true);
- final SqlSelect select = call.getSourceSelect();
+ final SqlSelect select = SqlNonNullableAccessors.getSourceSelect(call);
validateSelect(select, targetRowType);
final RelDataType sourceRowType = getValidatedNodeType(select);
@@ -4631,8 +4927,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
validateAccess(call.getTargetTable(), table, SqlAccessEnum.UPDATE);
}
+ @Override
public void validateMerge(SqlMerge call) {
- SqlSelect sqlSelect = call.getSourceSelect();
+ SqlSelect sqlSelect = SqlNonNullableAccessors.getSourceSelect(call);
// REVIEW zfong 5/25/06 - Does an actual type have to be passed into
// validateSelect()?
@@ -4646,7 +4943,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// since validateSelect() would bail.
// Let's use the update/insert targetRowType when available.
IdentifierNamespace targetNamespace =
- (IdentifierNamespace) getNamespace(call.getTargetTable());
+ (IdentifierNamespace) getNamespaceOrThrow(call.getTargetTable());
validateNamespace(targetNamespace, unknownType);
SqlValidatorTable table = targetNamespace.getTable();
@@ -4654,22 +4951,26 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
RelDataType targetRowType = unknownType;
- if (call.getUpdateCall() != null) {
- targetRowType =
- createTargetRowType(table, call.getUpdateCall().getTargetColumnList(), true);
+ SqlUpdate updateCall = call.getUpdateCall();
+ if (updateCall != null) {
+ requireNonNull(table, () -> "ns.getTable() for " + targetNamespace);
+ targetRowType = createTargetRowType(table, updateCall.getTargetColumnList(), true);
}
- if (call.getInsertCall() != null) {
- targetRowType =
- createTargetRowType(table, call.getInsertCall().getTargetColumnList(), false);
+ SqlInsert insertCall = call.getInsertCall();
+ if (insertCall != null) {
+ requireNonNull(table, () -> "ns.getTable() for " + targetNamespace);
+ targetRowType = createTargetRowType(table, insertCall.getTargetColumnList(), false);
}
validateSelect(sqlSelect, targetRowType);
- if (call.getUpdateCall() != null) {
- validateUpdate(call.getUpdateCall());
+ SqlUpdate updateCallAfterValidate = call.getUpdateCall();
+ if (updateCallAfterValidate != null) {
+ validateUpdate(updateCallAfterValidate);
}
- if (call.getInsertCall() != null) {
- validateInsert(call.getInsertCall());
+ SqlInsert insertCallAfterValidate = call.getInsertCall();
+ if (insertCallAfterValidate != null) {
+ validateInsert(insertCallAfterValidate);
}
}
@@ -4680,7 +4981,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @param requiredAccess Access requested on table
*/
private void validateAccess(
- SqlNode node, SqlValidatorTable table, SqlAccessEnum requiredAccess) {
+ SqlNode node, @Nullable SqlValidatorTable table, SqlAccessEnum requiredAccess) {
if (table != null) {
SqlAccessType access = table.getAllowedAccess();
if (!access.allowsAccess(requiredAccess)) {
@@ -4699,11 +5000,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* @param scope Validator scope to derive type
* @param ns The namespace to lookup table
*/
- private void validateSnapshot(SqlNode node, SqlValidatorScope scope, SqlValidatorNamespace ns) {
+ private void validateSnapshot(
+ SqlNode node, @Nullable SqlValidatorScope scope, SqlValidatorNamespace ns) {
if (node.getKind() == SqlKind.SNAPSHOT) {
SqlSnapshot snapshot = (SqlSnapshot) node;
SqlNode period = snapshot.getPeriod();
- RelDataType dataType = deriveType(scope, period);
+ RelDataType dataType = deriveType(requireNonNull(scope, "scope"), period);
+ // ----- FLINK MODIFICATION BEGIN -----
if (!(dataType.getSqlTypeName() == SqlTypeName.TIMESTAMP
|| dataType.getSqlTypeName() == SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)) {
throw newValidationError(
@@ -4716,9 +5019,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// table to operate on. This will be rechecked later in the planner rules.
return;
}
- assert ns.getTable() != null;
- if (!ns.getTable().isTemporal()) {
- List<String> qualifiedName = ns.getTable().getQualifiedName();
+ // ----- FLINK MODIFICATION END -----
+ SqlValidatorTable table = getTable(ns);
+ if (!table.isTemporal()) {
+ List<String> qualifiedName = table.getQualifiedName();
String tableName = qualifiedName.get(qualifiedName.size() - 1);
throw newValidationError(
snapshot.getTableRef(), Static.RESOURCE.notTemporalTable(tableName));
@@ -4800,11 +5104,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final RelDataType type =
typeFactory.leastRestrictive(
new AbstractList<RelDataType>() {
+ @Override
public RelDataType get(int row) {
SqlCall thisRow = (SqlCall) operands.get(row);
return deriveType(scope, thisRow.operand(c));
}
+ @Override
public int size() {
return rowCount;
}
@@ -4819,8 +5125,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public void validateDataType(SqlDataTypeSpec dataType) {}
+ @Override
public void validateDynamicParam(SqlDynamicParam dynamicParam) {}
/**
@@ -4837,6 +5145,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
this.validatorException = validatorException;
}
+ @Override
public CalciteContextException get() {
return newValidationError(sqlNode, validatorException);
}
@@ -4847,7 +5156,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* determined when the function is applied.
*/
class ValidationErrorFunction
- implements Function2<
+ implements BiFunction<
SqlNode, Resources.ExInst<SqlValidatorException>, CalciteContextException> {
@Override
public CalciteContextException apply(
@@ -4860,6 +5169,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return validationErrorFunction;
}
+ @Override
public CalciteContextException newValidationError(
SqlNode node, Resources.ExInst<SqlValidatorException> e) {
assert node != null;
@@ -4879,6 +5189,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return window;
}
+ @Override
public SqlWindow resolveWindow(SqlNode windowOrRef, SqlValidatorScope scope) {
SqlWindow window;
if (windowOrRef instanceof SqlIdentifier) {
@@ -4915,6 +5226,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
originalExprs.putIfAbsent(expr, original);
}
+ @Nullable
SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) {
final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
final RelDataTypeField field = nameMatcher.field(rowType, name);
@@ -4924,7 +5236,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return new FieldNamespace(this, field.getType());
}
- public void validateWindow(SqlNode windowOrId, SqlValidatorScope scope, SqlCall call) {
+ @Override
+ public void validateWindow(
+ SqlNode windowOrId, SqlValidatorScope scope, @Nullable SqlCall call) {
// Enable nested aggregates with window aggregates (OVER operator)
inWindow = true;
@@ -4942,13 +5256,14 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
throw Util.unexpected(windowOrId.getKind());
}
+ requireNonNull(call, () -> "call is null when validating windowOrId " + windowOrId);
assert targetWindow.getWindowCall() == null;
targetWindow.setWindowCall(call);
targetWindow.validate(this, scope);
targetWindow.setWindowCall(null);
call.validate(this, scope);
- validateAggregateParams(call, null, null, scope);
+ validateAggregateParams(call, null, null, null, scope);
// Disable nested aggregates post validation
inWindow = false;
@@ -4960,7 +5275,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final MatchRecognizeScope scope =
(MatchRecognizeScope) getMatchRecognizeScope(matchRecognize);
- final MatchRecognizeNamespace ns = getNamespace(call).unwrap(MatchRecognizeNamespace.class);
+ final MatchRecognizeNamespace ns =
+ getNamespaceOrThrow(call).unwrap(MatchRecognizeNamespace.class);
assert ns.rowType == null;
// rows per match
@@ -4990,9 +5306,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
node.validate(this, scope);
SqlIdentifier identifier;
if (node instanceof SqlBasicCall) {
- identifier = (SqlIdentifier) ((SqlBasicCall) node).getOperands()[0];
+ identifier = (SqlIdentifier) ((SqlBasicCall) node).operand(0);
} else {
- identifier = (SqlIdentifier) node;
+ identifier =
+ (SqlIdentifier)
+ requireNonNull(
+ node,
+ () -> "order by field is null. All fields: " + orderBy);
}
if (allRows) {
@@ -5006,7 +5326,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
if (allRows) {
- final SqlValidatorNamespace sqlNs = getNamespace(matchRecognize.getTableRef());
+ final SqlValidatorNamespace sqlNs = getNamespaceOrThrow(matchRecognize.getTableRef());
final RelDataType inputDataType = sqlNs.getRowType();
for (RelDataTypeField fs : inputDataType.getFieldList()) {
if (!typeBuilder.nameExists(fs.getName())) {
@@ -5024,26 +5344,32 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (interval != null) {
interval.validate(this, scope);
if (((SqlIntervalLiteral) interval).signum() < 0) {
+ String intervalValue = interval.toValue();
throw newValidationError(
- interval, RESOURCE.intervalMustBeNonNegative(interval.toValue()));
+ interval,
+ RESOURCE.intervalMustBeNonNegative(
+ intervalValue != null ? intervalValue : interval.toString()));
}
if (orderBy == null || orderBy.size() == 0) {
throw newValidationError(interval, RESOURCE.cannotUseWithinWithoutOrderBy());
}
- SqlNode firstOrderByColumn = orderBy.getList().get(0);
+ SqlNode firstOrderByColumn = orderBy.get(0);
SqlIdentifier identifier;
if (firstOrderByColumn instanceof SqlBasicCall) {
- identifier = (SqlIdentifier) ((SqlBasicCall) firstOrderByColumn).getOperands()[0];
+ identifier = ((SqlBasicCall) firstOrderByColumn).operand(0);
} else {
- identifier = (SqlIdentifier) firstOrderByColumn;
+ identifier =
+ (SqlIdentifier) requireNonNull(firstOrderByColumn, "firstOrderByColumn");
}
RelDataType firstOrderByColumnType = deriveType(scope, identifier);
+ // ----- FLINK MODIFICATION BEGIN -----
if (!(firstOrderByColumnType.getSqlTypeName() == SqlTypeName.TIMESTAMP
|| firstOrderByColumnType.getSqlTypeName()
== SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)) {
throw newValidationError(interval, RESOURCE.firstColumnOfOrderByMustBeTimestamp());
}
+ // ----- FLINK MODIFICATION END -----
SqlNode expand = expand(interval, scope);
RelDataType type = deriveType(scope, expand);
@@ -5092,7 +5418,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final RelDataType rowType = typeBuilder.build();
if (matchRecognize.getMeasureList().size() == 0) {
- ns.setType(getNamespace(matchRecognize.getTableRef()).getRowType());
+ ns.setType(getNamespaceOrThrow(matchRecognize.getTableRef()).getRowType());
} else {
ns.setType(rowType);
}
@@ -5107,7 +5433,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
for (SqlNode measure : measures) {
assert measure instanceof SqlCall;
- final String alias = deriveAlias(measure, aliases.size());
+ final String alias = deriveAliasNonNull(measure, aliases.size());
aliases.add(alias);
SqlNode expand = expand(measure, scope);
@@ -5157,7 +5483,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
private void validateDefinitions(SqlMatchRecognize mr, MatchRecognizeScope scope) {
final Set<String> aliases = catalogReader.nameMatcher().createSet();
- for (SqlNode item : mr.getPatternDefList().getList()) {
+ for (SqlNode item : mr.getPatternDefList()) {
final String alias = alias(item);
if (!aliases.add(alias)) {
throw newValidationError(item, Static.RESOURCE.patternVarAlreadyDefined(alias));
@@ -5166,7 +5492,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
final List<SqlNode> sqlNodes = new ArrayList<>();
- for (SqlNode item : mr.getPatternDefList().getList()) {
+ for (SqlNode item : mr.getPatternDefList()) {
final String alias = alias(item);
SqlNode expand = expand(item, scope);
expand = navigationInDefine(expand, alias);
@@ -5198,10 +5524,19 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
mr.setOperand(SqlMatchRecognize.OPERAND_PATTERN_DEFINES, list);
}
+ /** Returns the alias of a "expr AS alias" expression. */
+ private static String alias(SqlNode item) {
+ assert item instanceof SqlCall;
+ assert item.getKind() == SqlKind.AS;
+ final SqlIdentifier identifier = ((SqlCall) item).operand(1);
+ return identifier.getSimple();
+ }
+
public void validatePivot(SqlPivot pivot) {
- final PivotScope scope = (PivotScope) getJoinScope(pivot);
+ final PivotScope scope =
+ (PivotScope) requireNonNull(getJoinScope(pivot), () -> "joinScope for " + pivot);
- final PivotNamespace ns = getNamespace(pivot).unwrap(PivotNamespace.class);
+ final PivotNamespace ns = getNamespaceOrThrow(pivot).unwrap(PivotNamespace.class);
assert ns.rowType == null;
// Given
@@ -5215,7 +5550,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// an aggregate or as an axis.
// Aggregates, e.g. "PIVOT (sum(x) AS sum_x, count(*) AS c)"
- final List<Pair<String, RelDataType>> aggNames = new ArrayList<>();
+ final List<Pair<@Nullable String, RelDataType>> aggNames = new ArrayList<>();
pivot.forEachAgg(
(alias, call) -> {
call.validate(this, scope);
@@ -5290,12 +5625,143 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
ns.setType(rowType);
}
- /** Returns the alias of a "expr AS alias" expression. */
- private static String alias(SqlNode item) {
- assert item instanceof SqlCall;
- assert item.getKind() == SqlKind.AS;
- final SqlIdentifier identifier = ((SqlCall) item).operand(1);
- return identifier.getSimple();
+ public void validateUnpivot(SqlUnpivot unpivot) {
+ final UnpivotScope scope =
+ (UnpivotScope) requireNonNull(getJoinScope(unpivot), () -> "scope for " + unpivot);
+
+ final UnpivotNamespace ns = getNamespaceOrThrow(unpivot).unwrap(UnpivotNamespace.class);
+ assert ns.rowType == null;
+
+ // Given
+ // query UNPIVOT ((measure1, ..., measureM)
+ // FOR (axis1, ..., axisN)
+ // IN ((c11, ..., c1M) AS (value11, ..., value1N),
+ // (c21, ..., c2M) AS (value21, ..., value2N), ...)
+ // the type is
+ // k1, ... kN, axis1, ..., axisN, measure1, ..., measureM
+ // where k1, ... kN are columns that are not referenced as an argument to
+ // an aggregate or as an axis.
+
+ // First, And make sure that each
+ final int measureCount = unpivot.measureList.size();
+ final int axisCount = unpivot.axisList.size();
+ unpivot.forEachNameValues(
+ (nodeList, valueList) -> {
+ // Make sure that each (ci1, ... ciM) list has the same arity as
+ // (measure1, ..., measureM).
+ if (nodeList.size() != measureCount) {
+ throw newValidationError(
+ nodeList,
+ RESOURCE.unpivotValueArityMismatch(nodeList.size(), measureCount));
+ }
+
+ // Make sure that each (vi1, ... viN) list has the same arity as
+ // (axis1, ..., axisN).
+ if (valueList != null && valueList.size() != axisCount) {
+ throw newValidationError(
+ valueList,
+ RESOURCE.unpivotValueArityMismatch(valueList.size(), axisCount));
+ }
+
+ // Make sure that each IN expression is a valid column from the input.
+ nodeList.forEach(node -> deriveType(scope, node));
+ });
+
+ // What columns from the input are not referenced by a column in the IN
+ // list?
+ final SqlValidatorNamespace inputNs = Objects.requireNonNull(getNamespace(unpivot.query));
+ final Set<String> unusedColumnNames = catalogReader.nameMatcher().createSet();
+ unusedColumnNames.addAll(inputNs.getRowType().getFieldNames());
+ unusedColumnNames.removeAll(unpivot.usedColumnNames());
+
+ // What columns will be present in the output row type?
+ final Set<String> columnNames = catalogReader.nameMatcher().createSet();
+ columnNames.addAll(unusedColumnNames);
+
+ // Gather the name and type of each measure.
+ final List<Pair<String, RelDataType>> measureNameTypes = new ArrayList<>();
+ Ord.forEach(
+ unpivot.measureList,
+ (measure, i) -> {
+ final String measureName = ((SqlIdentifier) measure).getSimple();
+ final List<RelDataType> types = new ArrayList<>();
+ final List<SqlNode> nodes = new ArrayList<>();
+ unpivot.forEachNameValues(
+ (nodeList, valueList) -> {
+ final SqlNode alias = nodeList.get(i);
+ nodes.add(alias);
+ types.add(deriveType(scope, alias));
+ });
+ final RelDataType type0 = typeFactory.leastRestrictive(types);
+ if (type0 == null) {
+ throw newValidationError(
+ nodes.get(0), RESOURCE.unpivotCannotDeriveMeasureType(measureName));
+ }
+ final RelDataType type =
+ typeFactory.createTypeWithNullability(
+ type0, unpivot.includeNulls || unpivot.measureList.size() > 1);
+ setValidatedNodeType(measure, type);
+ if (!columnNames.add(measureName)) {
+ throw newValidationError(measure, RESOURCE.unpivotDuplicate(measureName));
+ }
+ measureNameTypes.add(Pair.of(measureName, type));
+ });
+
+ // Gather the name and type of each axis.
+ // Consider
+ // FOR (job, deptno)
+ // IN (a AS ('CLERK', 10),
+ // b AS ('ANALYST', 20))
+ // There are two axes, (job, deptno), and so each value list ('CLERK', 10),
+ // ('ANALYST', 20) must have arity two.
+ //
+ // The type of 'job' is derived as the least restrictive type of the values
+ // ('CLERK', 'ANALYST'), namely VARCHAR(7). The derived type of 'deptno' is
+ // the type of values (10, 20), namely INTEGER.
+ final List<Pair<String, RelDataType>> axisNameTypes = new ArrayList<>();
+ Ord.forEach(
+ unpivot.axisList,
+ (axis, i) -> {
+ final String axisName = ((SqlIdentifier) axis).getSimple();
+ final List<RelDataType> types = new ArrayList<>();
+ unpivot.forEachNameValues(
+ (aliasList, valueList) ->
+ types.add(
+ valueList == null
+ ? typeFactory.createSqlType(
+ SqlTypeName.VARCHAR,
+ SqlUnpivot.aliasValue(aliasList)
+ .length())
+ : deriveType(scope, valueList.get(i))));
+ final RelDataType type = typeFactory.leastRestrictive(types);
+ if (type == null) {
+ throw newValidationError(
+ axis, RESOURCE.unpivotCannotDeriveAxisType(axisName));
+ }
+ setValidatedNodeType(axis, type);
+ if (!columnNames.add(axisName)) {
+ throw newValidationError(axis, RESOURCE.unpivotDuplicate(axisName));
+ }
+ axisNameTypes.add(Pair.of(axisName, type));
+ });
+
+ // Columns that have been seen as arguments to aggregates or as axes
+ // do not appear in the output.
+ final RelDataTypeFactory.Builder typeBuilder = typeFactory.builder();
+ scope.getChild()
+ .getRowType()
+ .getFieldList()
+ .forEach(
+ field -> {
+ if (unusedColumnNames.contains(field.getName())) {
+ typeBuilder.add(field);
+ }
+ });
+ typeBuilder.addAll(axisNameTypes);
+ typeBuilder.addAll(measureNameTypes);
+
+ final RelDataType rowType = typeBuilder.build();
+ ns.setType(rowType);
}
/**
@@ -5310,8 +5776,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return node;
}
+ @Override
public void validateAggregateParams(
- SqlCall aggCall, SqlNode filter, SqlNodeList orderList, SqlValidatorScope scope) {
+ SqlCall aggCall,
+ @Nullable SqlNode filter,
+ @Nullable SqlNodeList distinctList,
+ @Nullable SqlNodeList orderList,
+ SqlValidatorScope scope) {
// For "agg(expr)", expr cannot itself contain aggregate function
// invocations. For example, "SUM(2 * MAX(x))" is illegal; when
// we see it, we'll report the error for the SUM (not the MAX).
@@ -5345,6 +5816,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
throw newValidationError(filter, RESOURCE.aggregateInFilterIllegal());
}
}
+ if (distinctList != null) {
+ for (SqlNode param : distinctList) {
+ if (a.findAgg(param) != null) {
+ throw newValidationError(aggCall, RESOURCE.aggregateInWithinDistinctIllegal());
+ }
+ }
+ }
if (orderList != null) {
for (SqlNode param : orderList) {
if (a.findAgg(param) != null) {
@@ -5366,7 +5844,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case IGNORED:
// rewrite the order list to empty
if (orderList != null) {
- orderList.getList().clear();
+ orderList.clear();
}
break;
case FORBIDDEN:
@@ -5380,6 +5858,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public void validateCall(SqlCall call, SqlValidatorScope scope) {
final SqlOperator operator = call.getOperator();
if ((call.operandCount() == 0)
@@ -5389,7 +5868,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// For example, "LOCALTIME()" is illegal. (It should be
// "LOCALTIME", which would have been handled as a
// SqlIdentifier.)
- throw handleUnresolvedFunction(call, (SqlFunction) operator, ImmutableList.of(), null);
+ throw handleUnresolvedFunction(call, operator, ImmutableList.of(), null);
}
SqlValidatorScope operandScope = scope.getOperandScope(call);
@@ -5419,16 +5898,17 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
public SqlNode expandSelectExpr(SqlNode expr, SelectScope scope, SqlSelect select) {
final Expander expander = new SelectExpander(this, scope, select);
- final SqlNode newExpr = expr.accept(expander);
+ final SqlNode newExpr = expander.go(expr);
if (expr != newExpr) {
setOriginal(newExpr, expr);
}
return newExpr;
}
+ @Override
public SqlNode expand(SqlNode expr, SqlValidatorScope scope) {
final Expander expander = new Expander(this, scope);
- SqlNode newExpr = expr.accept(expander);
+ SqlNode newExpr = expander.go(expr);
if (expr != newExpr) {
setOriginal(newExpr, expr);
}
@@ -5438,18 +5918,20 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
public SqlNode expandGroupByOrHavingExpr(
SqlNode expr, SqlValidatorScope scope, SqlSelect select, boolean havingExpression) {
final Expander expander = new ExtendedExpander(this, scope, select, expr, havingExpression);
- SqlNode newExpr = expr.accept(expander);
+ SqlNode newExpr = expander.go(expr);
if (expr != newExpr) {
setOriginal(newExpr, expr);
}
return newExpr;
}
+ @Override
public boolean isSystemField(RelDataTypeField field) {
return false;
}
- public List<List<String>> getFieldOrigins(SqlNode sqlQuery) {
+ @Override
+ public List<@Nullable List<String>> getFieldOrigins(SqlNode sqlQuery) {
if (sqlQuery instanceof SqlExplain) {
return Collections.emptyList();
}
@@ -5458,22 +5940,25 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
if (!sqlQuery.isA(SqlKind.QUERY)) {
return Collections.nCopies(fieldCount, null);
}
- final List<List<String>> list = new ArrayList<>();
+ final List<@Nullable List<String>> list = new ArrayList<>();
for (int i = 0; i < fieldCount; i++) {
list.add(getFieldOrigin(sqlQuery, i));
}
return ImmutableNullableList.copyOf(list);
}
- private List<String> getFieldOrigin(SqlNode sqlQuery, int i) {
+ private @Nullable List<String> getFieldOrigin(SqlNode sqlQuery, int i) {
if (sqlQuery instanceof SqlSelect) {
SqlSelect sqlSelect = (SqlSelect) sqlQuery;
- final SelectScope scope = getRawSelectScope(sqlSelect);
- final List<SqlNode> selectList = scope.getExpandedSelectList();
+ final SelectScope scope = getRawSelectScopeNonNull(sqlSelect);
+ final List<SqlNode> selectList =
+ requireNonNull(
+ scope.getExpandedSelectList(), () -> "expandedSelectList for " + scope);
final SqlNode selectItem = stripAs(selectList.get(i));
if (selectItem instanceof SqlIdentifier) {
final SqlQualified qualified = scope.fullyQualify((SqlIdentifier) selectItem);
- SqlValidatorNamespace namespace = qualified.namespace;
+ SqlValidatorNamespace namespace =
+ requireNonNull(qualified.namespace, () -> "namespace for " + qualified);
final SqlValidatorTable table = namespace.getTable();
if (table == null) {
return null;
@@ -5496,6 +5981,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
+ @Override
public RelDataType getParameterRowType(SqlNode sqlQuery) {
// NOTE: We assume that bind variables occur in depth-first tree
// traversal in the same order that they occurred in the SQL text.
@@ -5530,6 +6016,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
});
}
+ @Override
public void validateColumnListParams(
SqlFunction function, List<RelDataType> argTypes, List<SqlNode> operands) {
throw new UnsupportedOperationException();
@@ -5583,10 +6070,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlNode enclosingNode,
SqlValidatorScope parentScope) {
super(validator, node.getTargetTable(), enclosingNode, parentScope);
- this.node = Objects.requireNonNull(node);
+ this.node = requireNonNull(node, "node");
}
- public SqlInsert getNode() {
+ @Override
+ public @Nullable SqlNode getNode() {
return node;
}
}
@@ -5601,10 +6089,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlNode enclosingNode,
SqlValidatorScope parentScope) {
super(validator, node.getTargetTable(), enclosingNode, parentScope);
- this.node = Objects.requireNonNull(node);
+ this.node = requireNonNull(node, "node");
}
- public SqlUpdate getNode() {
+ @Override
+ public @Nullable SqlNode getNode() {
return node;
}
}
@@ -5619,10 +6108,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlNode enclosingNode,
SqlValidatorScope parentScope) {
super(validator, node.getTargetTable(), enclosingNode, parentScope);
- this.node = Objects.requireNonNull(node);
+ this.node = requireNonNull(node, "node");
}
- public SqlDelete getNode() {
+ @Override
+ public @Nullable SqlNode getNode() {
return node;
}
}
@@ -5637,10 +6127,11 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlNode enclosingNode,
SqlValidatorScope parentScope) {
super(validator, node.getTargetTable(), enclosingNode, parentScope);
- this.node = Objects.requireNonNull(node);
+ this.node = requireNonNull(node, "node");
}
- public SqlMerge getNode() {
+ @Override
+ public @Nullable SqlNode getNode() {
return node;
}
}
@@ -5707,15 +6198,18 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
this.scope = scope;
}
+ @Override
public RelDataType visit(SqlLiteral literal) {
return literal.createSqlType(typeFactory);
}
+ @Override
public RelDataType visit(SqlCall call) {
final SqlOperator operator = call.getOperator();
return operator.deriveType(SqlValidatorImpl.this, scope, call);
}
+ @Override
public RelDataType visit(SqlNodeList nodeList) {
// Operand is of a type that we can't derive a type for. If the
// operand is of a peculiar type, such as a SqlNodeList, then you
@@ -5724,6 +6218,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
throw Util.needToImplement(nodeList);
}
+ @Override
public RelDataType visit(SqlIdentifier id) {
// First check for builtin functions which don't have parentheses,
// like "LOCALTIME".
@@ -5802,6 +6297,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return type;
}
+ @Override
public RelDataType visit(SqlDataTypeSpec dataType) {
// Q. How can a data type have a type?
// A. When it appears in an expression. (Say as the 2nd arg to the
@@ -5810,10 +6306,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return dataType.deriveType(SqlValidatorImpl.this);
}
+ @Override
public RelDataType visit(SqlDynamicParam param) {
return unknownType;
}
+ @Override
public RelDataType visit(SqlIntervalQualifier intervalQualifier) {
return typeFactory.createSqlIntervalType(intervalQualifier);
}
@@ -5828,8 +6326,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
this.validator = validator;
}
+ public SqlNode go(SqlNode root) {
+ return requireNonNull(root.accept(this), () -> this + " returned null for " + root);
+ }
+
@Override
- public SqlNode visit(SqlIdentifier id) {
+ public @Nullable SqlNode visit(SqlIdentifier id) {
// First check for builtin functions which don't have
// parentheses, like "LOCALTIME".
final SqlCall call = validator.makeNullaryCall(id);
@@ -5850,10 +6352,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case NEXT_VALUE:
case WITH:
return call;
+ default:
+ break;
}
// Only visits arguments which are expressions. We don't want to
// qualify non-expressions such as 'x' in 'empno * 5 AS x'.
- ArgHandler<SqlNode> argHandler = new CallCopyingArgHandler(call, false);
+ CallCopyingArgHandler argHandler = new CallCopyingArgHandler(call, false);
call.getOperator().acceptCall(this, call, true, argHandler);
final SqlNode result = argHandler.result();
validator.setOriginal(result, call);
@@ -5888,14 +6392,16 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
super(getOrderScope(select));
this.select = select;
this.root = root;
- this.aliasList = getNamespace(select).getRowType().getFieldNames();
+ this.aliasList = getNamespaceOrThrow(select).getRowType().getFieldNames();
}
public SqlNode go() {
- return root.accept(this);
+ return requireNonNull(
+ root.accept(this), () -> "OrderExpressionExpander returned null for " + root);
}
- public SqlNode visit(SqlLiteral literal) {
+ @Override
+ public @Nullable SqlNode visit(SqlLiteral literal) {
// Ordinal markers, e.g. 'select a, b from t order by 2'.
// Only recognize them if they are the whole expression,
// and if the dialect permits.
@@ -5915,6 +6421,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return nthSelectItem(ordinal, literal.getParserPosition());
}
break;
+ default:
+ break;
}
}
@@ -5927,7 +6435,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
// version of each expression -- select lists and identifiers -- in
// the validator.
- SqlNodeList expandedSelectList = expandStar(select.getSelectList(), select, false);
+ SqlNodeList expandedSelectList =
+ expandStar(SqlNonNullableAccessors.getSelectList(select), select, false);
SqlNode expr = expandedSelectList.get(ordinal);
expr = stripAs(expr);
if (expr instanceof SqlIdentifier) {
@@ -5939,11 +6448,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return expr.clone(pos);
}
+ @Override
public SqlNode visit(SqlIdentifier id) {
// Aliases, e.g. 'select a as x, b from t order by x'.
if (id.isSimple() && config.sqlConformance().isSortByAlias()) {
String alias = id.getSimple();
- final SqlValidatorNamespace selectNs = getNamespace(select);
+ final SqlValidatorNamespace selectNs = getNamespaceOrThrow(select);
final RelDataType rowType = selectNs.getRowTypeSansSystemColumns();
final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
RelDataTypeField field = nameMatcher.field(rowType, alias);
@@ -5956,7 +6466,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return getScope().fullyQualify(id).identifier;
}
- protected SqlNode visitScoped(SqlCall call) {
+ @Override
+ protected @Nullable SqlNode visitScoped(SqlCall call) {
// Don't attempt to expand sub-queries. We haven't implemented
// these yet.
if (call instanceof SqlSelect) {
@@ -5979,7 +6490,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
@Override
- public SqlNode visit(SqlIdentifier id) {
+ public @Nullable SqlNode visit(SqlIdentifier id) {
final SqlNode node =
expandCommonColumn(select, id, (SelectScope) getScope(), validator);
if (node != id) {
@@ -6012,7 +6523,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
@Override
- public SqlNode visit(SqlIdentifier id) {
+ public @Nullable SqlNode visit(SqlIdentifier id) {
if (id.isSimple()
&& (havingExpr
? validator.config().sqlConformance().isHavingAlias()
@@ -6021,7 +6532,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
SqlNode expr = null;
final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher();
int n = 0;
- for (SqlNode s : select.getSelectList()) {
+ for (SqlNode s : SqlNonNullableAccessors.getSelectList(select)) {
final String alias = SqlValidatorUtil.getAlias(s, -1);
if (alias != null && nameMatcher.matches(alias, name)) {
expr = s;
@@ -6055,7 +6566,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
return super.visit(id);
}
- public SqlNode visit(SqlLiteral literal) {
+ @Override
+ public @Nullable SqlNode visit(SqlLiteral literal) {
if (havingExpr || !validator.config().sqlConformance().isGroupByOrdinal()) {
return super.visit(literal);
}
@@ -6074,6 +6586,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
}
break;
+ default:
+ break;
}
if (isOrdinalLiteral) {
switch (literal.getTypeName()) {
@@ -6081,16 +6595,22 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
case DOUBLE:
final int intValue = literal.intValue(false);
if (intValue >= 0) {
- if (intValue < 1 || intValue > select.getSelectList().size()) {
+ if (intValue < 1
+ || intValue
+ > SqlNonNullableAccessors.getSelectList(select)
+ .size()) {
throw validator.newValidationError(
literal, RESOURCE.orderByOrdinalOutOfRange());
}
// SQL ordinals are 1-based, but Sort's are 0-based
int ordinal = intValue - 1;
- return SqlUtil.stripAs(select.getSelectList().get(ordinal));
+ return SqlUtil.stripAs(
+ SqlNonNullableAccessors.getSelectList(select).get(ordinal));
}
break;
+ default:
+ break;
}
}
@@ -6132,7 +6652,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
/** Modify the nodes in navigation function such as FIRST, LAST, PREV AND NEXT. */
private static class NavigationModifier extends SqlShuttle {
public SqlNode go(SqlNode node) {
- return node.accept(this);
+ return requireNonNull(
+ node.accept(this), () -> "NavigationModifier returned for " + node);
}
}
@@ -6147,23 +6668,23 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
* </ul>
*/
private static class NavigationExpander extends NavigationModifier {
- final SqlOperator op;
- final SqlNode offset;
+ final @Nullable SqlOperator op;
+ final @Nullable SqlNode offset;
NavigationExpander() {
this(null, null);
}
- NavigationExpander(SqlOperator operator, SqlNode offset) {
+ NavigationExpander(@Nullable SqlOperator operator, @Nullable SqlNode offset) {
this.offset = offset;
this.op = operator;
}
@Override
- public SqlNode visit(SqlCall call) {
+ public @Nullable SqlNode visit(SqlCall call) {
SqlKind kind = call.getKind();
List<SqlNode> operands = call.getOperandList();
- List<SqlNode> newOperands = new ArrayList<>();
+ List<@Nullable SqlNode> newOperands = new ArrayList<>();
if (call.getFunctionQuantifier() != null
&& call.getFunctionQuantifier().getValue() == SqlSelectKeyword.DISTINCT) {
@@ -6251,7 +6772,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
@Override
- public SqlNode visit(SqlCall call) {
+ public @Nullable SqlNode visit(SqlCall call) {
SqlKind kind = call.getKind();
if (isLogicalNavigation(kind) || isAggregation(kind) || isRunningOrFinal(kind)) {
return call;
@@ -6266,6 +6787,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
? call
: SqlStdOperatorTable.LAST.createCall(SqlParserPos.ZERO, operands);
}
+ break;
+ default:
+ break;
}
return super.visit(call);
}
@@ -6286,7 +6810,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
/** Validates that within one navigation function, the pattern var is the same. */
- private class PatternValidator extends SqlBasicVisitor<Set<String>> {
+ private class PatternValidator extends SqlBasicVisitor<@Nullable Set<String>> {
private final boolean isMeasure;
int firstLastCount;
int prevNextCount;
@@ -6352,12 +6876,14 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
for (SqlNode node : operands) {
if (node != null) {
vars.addAll(
- node.accept(
- new PatternValidator(
- isMeasure,
- firstLastCount,
- prevNextCount,
- aggregateCount)));
+ requireNonNull(
+ node.accept(
+ new PatternValidator(
+ isMeasure,
+ firstLastCount,
+ prevNextCount,
+ aggregateCount)),
+ () -> "node.accept(PatternValidator) for node " + node));
}
}
@@ -6488,7 +7014,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
}
private RelDataTypeField field(String name) {
- return catalogReader.nameMatcher().field(rowType, name);
+ RelDataTypeField field = catalogReader.nameMatcher().field(rowType, name);
+ assert field != null : "field " + name + " was not found in " + rowType;
+ return field;
}
/** Moves fields according to the permutation. */
@@ -6514,8 +7042,16 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
final RelDataType type1 = field1.getValue();
// output is nullable only if both inputs are
final boolean nullable = type.isNullable() && type1.isNullable();
+ RelDataType currentType = type;
final RelDataType type2 =
- SqlTypeUtil.leastRestrictiveForComparison(typeFactory, type, type1);
+ requireNonNull(
+ SqlTypeUtil.leastRestrictiveForComparison(
+ typeFactory, type, type1),
+ () ->
+ "leastRestrictiveForComparison for types "
+ + currentType
+ + " and "
+ + type1);
selectItem =
SqlStdOperatorTable.AS.createCall(
SqlParserPos.ZERO,
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
index cf2d4de2c65..192e338e1c4 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -34,11 +34,13 @@ import org.apache.calcite.linq4j.function.Function2;
import org.apache.calcite.plan.Context;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCostImpl;
+import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelRule;
import org.apache.calcite.plan.hep.HepPlanner;
import org.apache.calcite.plan.hep.HepProgram;
+import org.apache.calcite.plan.hep.HepProgramBuilder;
import org.apache.calcite.plan.hep.HepRelVertex;
import org.apache.calcite.rel.BiRel;
import org.apache.calcite.rel.RelCollation;
@@ -102,10 +104,9 @@ import org.apache.calcite.util.ReflectiveVisitor;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.mapping.Mappings;
import org.apache.calcite.util.trace.CalciteTrace;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
-import javax.annotation.Nonnull;
-
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
@@ -117,11 +118,23 @@ import java.util.Map;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.Set;
-import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
-/** Copied to fix calcite issues. */
+import static java.util.Objects.requireNonNull;
+import static org.apache.calcite.linq4j.Nullness.castNonNull;
+
+/**
+ * Copied to fix calcite issues. FLINK modifications are at lines
+ *
+ * <ol>
+ * <li>Was changed within FLINK-29280, FLINK-28682: Line 224 ~ 234
+ * <li>Should be removed after fix of FLINK-29540: Line 296 ~ 302
+ * <li>Should be removed after fix of FLINK-29540: Line 314 ~ 320
+ * <li>Was changed within FLINK-21592: Line 1954 ~ 1962, Should be removed after update to Calcite
+ * 1.28.0 as it is fixed at CALCITE-4773
+ * </ol>
+ */
public class RelDecorrelator implements ReflectiveVisitor {
// ~ Static fields/initializers ---------------------------------------------
@@ -129,27 +142,29 @@ public class RelDecorrelator implements ReflectiveVisitor {
// ~ Instance fields --------------------------------------------------------
- private final RelBuilder relBuilder;
+ protected final RelBuilder relBuilder;
// map built during translation
protected CorelMap cm;
- private final ReflectUtil.MethodDispatcher<Frame> dispatcher =
- ReflectUtil.createMethodDispatcher(Frame.class, this, "decorrelateRel", RelNode.class);
+ @SuppressWarnings("method.invocation.invalid")
+ protected final ReflectUtil.MethodDispatcher<@Nullable Frame> dispatcher =
+ ReflectUtil.<RelNode, @Nullable Frame>createMethodDispatcher(
+ Frame.class, getVisitor(), "decorrelateRel", RelNode.class, boolean.class);
// The rel which is being visited
- private RelNode currentRel;
+ protected @Nullable RelNode currentRel;
- private final Context context;
+ protected final Context context;
/**
* Built during decorrelation, of rel to all the newly created correlated variables in its
* output, and to map old input positions to new input positions. This is from the view point of
* the parent rel of a new rel.
*/
- private final Map<RelNode, Frame> map = new HashMap<>();
+ protected final Map<RelNode, Frame> map = new HashMap<>();
- private final HashSet<Correlate> generatedCorRels = new HashSet<>();
+ protected final HashSet<Correlate> generatedCorRels = new HashSet<>();
// ~ Constructors -----------------------------------------------------------
@@ -221,7 +236,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
return newRootRel;
}
- private void setCurrent(RelNode root, Correlate corRel) {
+ private void setCurrent(@Nullable RelNode root, @Nullable Correlate corRel) {
currentRel = corRel;
if (corRel != null) {
cm = new CorelMapBuilder().build(Util.first(root, corRel));
@@ -278,20 +293,42 @@ public class RelDecorrelator implements ReflectiveVisitor {
FilterCorrelateRule.Config.DEFAULT
.withRelBuilderFactory(f)
.toRule())
+ /* ----- FLINK MODIFICATION BEGIN -----
+ This is commented as a workaround for https://issues.apache.org/jira/browse/FLINK-29540
+ .addRuleInstance(
+ FilterFlattenCorrelatedConditionRule.Config.DEFAULT
+ .withRelBuilderFactory(f)
+ .toRule())
+ ----- FLINK MODIFICATION END -----*/
.build();
HepPlanner planner = createPlanner(program);
planner.setRoot(root);
root = planner.findBestExp();
-
+ if (SQL2REL_LOGGER.isDebugEnabled()) {
+ SQL2REL_LOGGER.debug(
+ "Plan before extracting correlated computations:\n"
+ + RelOptUtil.toString(root));
+ }
+ /* ----- FLINK MODIFICATION BEGIN -----
+ This is commented as a workaround for https://issues.apache.org/jira/browse/FLINK-29540
+ root = root.accept(new CorrelateProjectExtractor(f));
+ // Necessary to update cm (CorrelMap) since CorrelateProjectExtractor above may modify the
+ // plan
+ this.cm = new CorelMapBuilder().build(root);
+ ----- FLINK MODIFICATION END ----- */
+ if (SQL2REL_LOGGER.isDebugEnabled()) {
+ SQL2REL_LOGGER.debug(
+ "Plan after extracting correlated computations:\n" + RelOptUtil.toString(root));
+ }
// Perform decorrelation.
map.clear();
- final Frame frame = getInvoke(root, null);
+ final Frame frame = getInvoke(root, false, null);
if (frame != null) {
// has been rewritten; apply rules post-decorrelation
- final HepProgram program2 =
+ final HepProgramBuilder builder =
HepProgram.builder()
.addRuleInstance(
CoreRules.FILTER_INTO_JOIN
@@ -302,8 +339,11 @@ public class RelDecorrelator implements ReflectiveVisitor {
CoreRules.JOIN_CONDITION_PUSH
.config
.withRelBuilderFactory(f)
- .toRule())
- .build();
+ .toRule());
+ if (!getPostDecorrelateRules().isEmpty()) {
+ builder.addRuleCollection(getPostDecorrelateRules());
+ }
+ final HepProgram program2 = builder.build();
final HepPlanner planner2 = createPlanner(program2);
final RelNode newRoot = frame.r;
@@ -314,7 +354,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
return root;
}
- private Function2<RelNode, RelNode, Void> createCopyHook() {
+ private Function2<RelNode, RelNode, @Nullable Void> createCopyHook() {
return (oldNode, newNode) -> {
if (cm.mapRefRelToCorRef.containsKey(oldNode)) {
cm.mapRefRelToCorRef.putAll(newNode, cm.mapRefRelToCorRef.get(oldNode));
@@ -396,14 +436,14 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
/** Fallback if none of the other {@code decorrelateRel} methods match. */
- public Frame decorrelateRel(RelNode rel) {
+ public @Nullable Frame decorrelateRel(RelNode rel, boolean isCorVarDefined) {
RelNode newRel = rel.copy(rel.getTraitSet(), rel.getInputs());
if (rel.getInputs().size() > 0) {
List<RelNode> oldInputs = rel.getInputs();
List<RelNode> newInputs = new ArrayList<>();
for (int i = 0; i < oldInputs.size(); ++i) {
- final Frame frame = getInvoke(oldInputs.get(i), rel);
+ final Frame frame = getInvoke(oldInputs.get(i), isCorVarDefined, rel);
if (frame == null || !frame.corDefOutputs.isEmpty()) {
// if input is not rewritten, or if it produces correlated
// variables, terminate rewrite
@@ -427,7 +467,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
ImmutableSortedMap.of());
}
- public Frame decorrelateRel(Sort rel) {
+ public @Nullable Frame decorrelateRel(Sort rel, boolean isCorVarDefined) {
//
// Rewrite logic:
//
@@ -444,15 +484,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
// need to call propagateExpr.
final RelNode oldInput = rel.getInput();
- final Frame frame = getInvoke(oldInput, rel);
+ final Frame frame = getInvoke(oldInput, isCorVarDefined, rel);
if (frame == null) {
// If input has not been rewritten, do not rewrite this rel.
return null;
}
- // BEGIN FLINK MODIFICATION
- // Reason: to de-correlate sort rel when its parent is not a correlate
- // Should be removed after CALCITE-4333 is fixed
final RelNode newInput = frame.r;
Mappings.TargetMapping mapping =
@@ -466,7 +503,6 @@ public class RelDecorrelator implements ReflectiveVisitor {
final int offset = rel.offset == null ? -1 : RexLiteral.intValue(rel.offset);
final int fetch = rel.fetch == null ? -1 : RexLiteral.intValue(rel.fetch);
- // END FLINK MODIFICATION
final RelNode newSort =
relBuilder
@@ -478,16 +514,16 @@ public class RelDecorrelator implements ReflectiveVisitor {
return register(rel, newSort, frame.oldToNewOutputs, frame.corDefOutputs);
}
- public Frame decorrelateRel(Values rel) {
+ public @Nullable Frame decorrelateRel(Values rel, boolean isCorVarDefined) {
// There are no inputs, so rel does not need to be changed.
return null;
}
- public Frame decorrelateRel(LogicalAggregate rel) {
- return decorrelateRel((Aggregate) rel);
+ public @Nullable Frame decorrelateRel(LogicalAggregate rel, boolean isCorVarDefined) {
+ return decorrelateRel((Aggregate) rel, isCorVarDefined);
}
- public Frame decorrelateRel(Aggregate rel) {
+ public @Nullable Frame decorrelateRel(Aggregate rel, boolean isCorVarDefined) {
//
// Rewrite logic:
//
@@ -501,7 +537,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
assert !cm.mapRefRelToCorRef.containsKey(rel);
final RelNode oldInput = rel.getInput();
- final Frame frame = getInvoke(oldInput, rel);
+ final Frame frame = getInvoke(oldInput, isCorVarDefined, rel);
if (frame == null) {
// If input has not been rewritten, do not rewrite this rel.
return null;
@@ -542,7 +578,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
newPos++;
}
- final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
+ final NavigableMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
if (!frame.corDefOutputs.isEmpty()) {
// If input produces correlated variables, move them to the front,
// right after any existing GROUP BY fields.
@@ -594,10 +630,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
// newInput
Map<Integer, Integer> combinedMap = new HashMap<>();
- for (Integer oldInputPos : frame.oldToNewOutputs.keySet()) {
+ for (Map.Entry<Integer, Integer> entry : frame.oldToNewOutputs.entrySet()) {
combinedMap.put(
- oldInputPos,
- mapNewInputToProjOutputs.get(frame.oldToNewOutputs.get(oldInputPos)));
+ entry.getKey(),
+ requireNonNull(
+ mapNewInputToProjOutputs.get(entry.getValue()),
+ () -> "mapNewInputToProjOutputs.get(" + entry.getValue() + ")"));
}
register(oldInput, newProject, combinedMap, corDefOutputs);
@@ -634,12 +672,16 @@ public class RelDecorrelator implements ReflectiveVisitor {
// output position mapping can be used to derive the new positions
// for the argument.
for (int oldPos : oldAggArgs) {
- aggArgs.add(combinedMap.get(oldPos));
+ aggArgs.add(
+ requireNonNull(
+ combinedMap.get(oldPos), () -> "combinedMap.get(" + oldPos + ")"));
}
final int filterArg =
oldAggCall.filterArg < 0
? oldAggCall.filterArg
- : combinedMap.get(oldAggCall.filterArg);
+ : requireNonNull(
+ combinedMap.get(oldAggCall.filterArg),
+ () -> "combinedMap.get(" + oldAggCall.filterArg + ")");
newAggCalls.add(
oldAggCall.adaptTo(
@@ -691,39 +733,32 @@ public class RelDecorrelator implements ReflectiveVisitor {
private static void shiftMapping(Map<Integer, Integer> mapping, int startIndex, int offset) {
for (Map.Entry<Integer, Integer> entry : mapping.entrySet()) {
if (entry.getValue() >= startIndex) {
- mapping.put(entry.getKey(), entry.getValue() + offset);
- } else {
- mapping.put(entry.getKey(), entry.getValue());
+ entry.setValue(entry.getValue() + offset);
}
}
}
- public Frame getInvoke(RelNode r, RelNode parent) {
- final Frame frame = dispatcher.invoke(r);
- // BEGIN FLINK MODIFICATION
- // Reason: to de-correlate sort rel when its parent is not a correlate
- // Should be removed after CALCITE-4333 is fixed
- if (frame != null && parent instanceof Correlate && r instanceof Sort) {
- Sort sort = (Sort) r;
+ public @Nullable Frame getInvoke(RelNode r, boolean isCorVarDefined, @Nullable RelNode parent) {
+ final Frame frame = dispatcher.invoke(r, isCorVarDefined);
+ currentRel = parent;
+ if (frame != null && isCorVarDefined && r instanceof Sort) {
+ final Sort sort = (Sort) r;
// Can not decorrelate if the sort has per-correlate-key attributes like
// offset or fetch limit, because these attributes scope would change to
// global after decorrelation. They should take effect within the scope
// of the correlation key actually.
if (sort.offset != null || sort.fetch != null) {
- currentRel = parent;
return null;
}
}
- // END FLINK MODIFICATION
if (frame != null) {
map.put(r, frame);
}
- currentRel = parent;
return frame;
}
/** Returns a literal output field, or null if it is not literal. */
- private static RexLiteral projectedLiteral(RelNode rel, int i) {
+ private static @Nullable RexLiteral projectedLiteral(RelNode rel, int i) {
if (rel instanceof Project) {
final Project project = (Project) rel;
final RexNode node = project.getProjects().get(i);
@@ -734,11 +769,11 @@ public class RelDecorrelator implements ReflectiveVisitor {
return null;
}
- public Frame decorrelateRel(LogicalProject rel) {
- return decorrelateRel((Project) rel);
+ public @Nullable Frame decorrelateRel(LogicalProject rel, boolean isCorVarDefined) {
+ return decorrelateRel((Project) rel, isCorVarDefined);
}
- public Frame decorrelateRel(Project rel) {
+ public @Nullable Frame decorrelateRel(Project rel, boolean isCorVarDefined) {
//
// Rewrite logic:
//
@@ -746,7 +781,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
//
final RelNode oldInput = rel.getInput();
- Frame frame = getInvoke(oldInput, rel);
+ Frame frame = getInvoke(oldInput, isCorVarDefined, rel);
if (frame == null) {
// If input has not been rewritten, do not rewrite this rel.
return null;
@@ -771,13 +806,17 @@ public class RelDecorrelator implements ReflectiveVisitor {
projects.add(
newPos,
Pair.of(
- decorrelateExpr(currentRel, map, cm, oldProjects.get(newPos)),
+ decorrelateExpr(
+ requireNonNull(currentRel, "currentRel"),
+ map,
+ cm,
+ oldProjects.get(newPos)),
relOutput.get(newPos).getName()));
mapOldToNewOutputs.put(newPos, newPos);
}
// Project any correlated variables the input wants to pass along.
- final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
+ final NavigableMap<CorDef, Integer> corDefOutputs = new TreeMap<>();
for (Map.Entry<CorDef, Integer> entry : frame.corDefOutputs.entrySet()) {
projects.add(RexInputRef.of2(entry.getValue(), frame.r.getRowType().getFieldList()));
corDefOutputs.put(entry.getKey(), newPos);
@@ -803,10 +842,10 @@ public class RelDecorrelator implements ReflectiveVisitor {
* @param corDefOutputs output positions for the correlated variables generated
* @return RelNode the root of the resultant RelNode tree
*/
- private RelNode createValueGenerator(
+ private @Nullable RelNode createValueGenerator(
Iterable<CorRef> correlations,
int valueGenFieldOffset,
- SortedMap<CorDef, Integer> corDefOutputs) {
+ NavigableMap<CorDef, Integer> corDefOutputs) {
final Map<RelNode, List<Integer>> mapNewInputToOutputs = new HashMap<>();
final Map<RelNode, Integer> mapNewInputToNewOffset = new HashMap<>();
@@ -818,7 +857,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
final RelNode oldInput = getCorRel(corVar);
assert oldInput != null;
- final Frame frame = getFrame(oldInput, true);
+ final Frame frame = getOrCreateFrame(oldInput);
assert frame != null;
final RelNode newInput = frame.r;
@@ -851,12 +890,14 @@ public class RelDecorrelator implements ReflectiveVisitor {
for (CorRef corVar : correlations) {
final RelNode oldInput = getCorRel(corVar);
assert oldInput != null;
- final RelNode newInput = getFrame(oldInput, true).r;
+ final RelNode newInput = getOrCreateFrame(oldInput).r;
assert newInput != null;
if (!joinedInputs.contains(newInput)) {
- final List<Integer> positions = mapNewInputToOutputs.get(newInput);
- final List<String> fieldNames = newInput.getRowType().getFieldNames();
+ final List<Integer> positions =
+ requireNonNull(
+ mapNewInputToOutputs.get(newInput),
+ () -> "mapNewInputToOutputs.get(" + newInput + ")");
RelNode distinct =
relBuilder
@@ -894,11 +935,14 @@ public class RelDecorrelator implements ReflectiveVisitor {
// the correlated variables.
final RelNode oldInput = getCorRel(corRef);
assert oldInput != null;
- final Frame frame = getFrame(oldInput, true);
+ final Frame frame = getOrCreateFrame(oldInput);
final RelNode newInput = frame.r;
assert newInput != null;
- final List<Integer> newLocalOutputs = mapNewInputToOutputs.get(newInput);
+ final List<Integer> newLocalOutputs =
+ requireNonNull(
+ mapNewInputToOutputs.get(newInput),
+ () -> "mapNewInputToOutputs.get(" + newInput + ")");
final int newLocalOutput = frame.oldToNewOutputs.get(corRef.field);
@@ -907,7 +951,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
// each newInput.
final int newOutput =
newLocalOutputs.indexOf(newLocalOutput)
- + mapNewInputToNewOffset.get(newInput)
+ + requireNonNull(
+ mapNewInputToNewOffset.get(newInput),
+ () -> "mapNewInputToNewOffset.get(" + newInput + ")")
+ valueGenFieldOffset;
corDefOutputs.put(corRef.def(), newOutput);
@@ -916,18 +962,25 @@ public class RelDecorrelator implements ReflectiveVisitor {
return r;
}
- private Frame getFrame(RelNode r, boolean safe) {
- final Frame frame = map.get(r);
- if (frame == null && safe) {
+ private Frame getOrCreateFrame(RelNode r) {
+ final Frame frame = getFrame(r);
+ if (frame == null) {
return new Frame(
r, r, ImmutableSortedMap.of(), identityMap(r.getRowType().getFieldCount()));
}
return frame;
}
+ private @Nullable Frame getFrame(RelNode r) {
+ return map.get(r);
+ }
+
private RelNode getCorRel(CorRef corVar) {
- final RelNode r = cm.mapCorToCorRel.get(corVar.corr);
- return r.getInput(0);
+ final RelNode r =
+ requireNonNull(
+ cm.mapCorToCorRel.get(corVar.corr),
+ () -> "cm.mapCorToCorRel.get(" + corVar.corr + ")");
+ return requireNonNull(r.getInput(0), () -> "r.getInput(0) is null for " + r);
}
/**
@@ -951,7 +1004,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
* Returns whether all of a collection of {@link CorRef}s are satisfied by at least one of a
* collection of {@link CorDef}s.
*/
- private boolean hasAll(Collection<CorRef> corRefs, Collection<CorDef> corDefs) {
+ private static boolean hasAll(Collection<CorRef> corRefs, Collection<CorDef> corDefs) {
for (CorRef corRef : corRefs) {
if (!has(corDefs, corRef)) {
return false;
@@ -964,7 +1017,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
* Returns whether a {@link CorrelationId} is satisfied by at least one of a collection of
* {@link CorDef}s.
*/
- private boolean has(Collection<CorDef> corDefs, CorRef corr) {
+ private static boolean has(Collection<CorDef> corDefs, CorRef corr) {
for (CorDef corDef : corDefs) {
if (corDef.corr.equals(corr.corr) && corDef.field == corr.field) {
return true;
@@ -978,14 +1031,14 @@ public class RelDecorrelator implements ReflectiveVisitor {
assert rel.getInputs().size() == 1;
RelNode oldInput = frame.r;
- final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>(frame.corDefOutputs);
+ final NavigableMap<CorDef, Integer> corDefOutputs = new TreeMap<>(frame.corDefOutputs);
final Collection<CorRef> corVarList = cm.mapRefRelToCorRef.get(rel);
// Try to populate correlation variables using local fields.
// This means that we do not need a value generator.
if (rel instanceof Filter) {
- SortedMap<CorDef, Integer> map = new TreeMap<>();
+ NavigableMap<CorDef, Integer> map = new TreeMap<>();
List<RexNode> projects = new ArrayList<>();
for (CorRef correlation : corVarList) {
final CorDef def = correlation.def();
@@ -995,11 +1048,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
try {
findCorrelationEquivalent(correlation, ((Filter) rel).getCondition());
} catch (Util.FoundOne e) {
- if (e.getNode() instanceof RexInputRef) {
- map.put(def, ((RexInputRef) e.getNode()).getIndex());
+ Object node = requireNonNull(e.getNode(), "e.getNode()");
+ if (node instanceof RexInputRef) {
+ map.put(def, ((RexInputRef) node).getIndex());
} else {
map.put(def, frame.r.getRowType().getFieldCount() + projects.size());
- projects.add((RexNode) e.getNode());
+ projects.add((RexNode) node);
}
}
}
@@ -1024,7 +1078,10 @@ public class RelDecorrelator implements ReflectiveVisitor {
// can directly add positions into corDefOutputs since join
// does not change the output ordering from the inputs.
- RelNode valueGen = createValueGenerator(corVarList, leftInputOutputCount, corDefOutputs);
+ RelNode valueGen =
+ requireNonNull(
+ createValueGenerator(corVarList, leftInputOutputCount, corDefOutputs),
+ "createValueGenerator(...) is null");
RelNode join =
relBuilder
@@ -1043,7 +1100,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
* Finds a {@link RexInputRef} that is equivalent to a {@link CorRef}, and if found, throws a
* {@link org.apache.calcite.util.Util.FoundOne}.
*/
- private void findCorrelationEquivalent(CorRef correlation, RexNode e) throws Util.FoundOne {
+ private static void findCorrelationEquivalent(CorRef correlation, RexNode e)
+ throws Util.FoundOne {
switch (e.getKind()) {
case EQUALS:
final RexCall call = (RexCall) e;
@@ -1059,10 +1117,13 @@ public class RelDecorrelator implements ReflectiveVisitor {
for (RexNode operand : ((RexCall) e).getOperands()) {
findCorrelationEquivalent(correlation, operand);
}
+ break;
+ default:
+ break;
}
}
- private boolean references(RexNode e, CorRef correlation) {
+ private static boolean references(RexNode e, CorRef correlation) {
switch (e.getKind()) {
case CAST:
final RexNode operand = ((RexCall) e).getOperands().get(0);
@@ -1094,30 +1155,30 @@ public class RelDecorrelator implements ReflectiveVisitor {
* <li>{@code VARCHAR(10)} is a widening of {@code VARCHAR(10) NOT NULL}.
* </ul>
*/
- private boolean isWidening(RelDataType type, RelDataType type1) {
+ private static boolean isWidening(RelDataType type, RelDataType type1) {
return type.getSqlTypeName() == type1.getSqlTypeName()
&& type.getPrecision() >= type1.getPrecision();
}
- public Frame decorrelateRel(LogicalSnapshot rel) {
+ public @Nullable Frame decorrelateRel(LogicalSnapshot rel, boolean isCorVarDefined) {
if (RexUtil.containsCorrelation(rel.getPeriod())) {
return null;
}
- return decorrelateRel((RelNode) rel);
+ return decorrelateRel((RelNode) rel, isCorVarDefined);
}
- public Frame decorrelateRel(LogicalTableFunctionScan rel) {
+ public @Nullable Frame decorrelateRel(LogicalTableFunctionScan rel, boolean isCorVarDefined) {
if (RexUtil.containsCorrelation(rel.getCall())) {
return null;
}
- return decorrelateRel((RelNode) rel);
+ return decorrelateRel((RelNode) rel, isCorVarDefined);
}
- public Frame decorrelateRel(LogicalFilter rel) {
- return decorrelateRel((Filter) rel);
+ public @Nullable Frame decorrelateRel(LogicalFilter rel, boolean isCorVarDefined) {
+ return decorrelateRel((Filter) rel, isCorVarDefined);
}
- public Frame decorrelateRel(Filter rel) {
+ public @Nullable Frame decorrelateRel(Filter rel, boolean isCorVarDefined) {
//
// Rewrite logic:
//
@@ -1135,7 +1196,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
//
final RelNode oldInput = rel.getInput();
- Frame frame = getInvoke(oldInput, rel);
+ Frame frame = getInvoke(oldInput, isCorVarDefined, rel);
if (frame == null) {
// If input has not been rewritten, do not rewrite this rel.
return null;
@@ -1155,7 +1216,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
// Replace the filter expression to reference output of the join
// Map filter to the new filter over join
- relBuilder.push(frame.r).filter(decorrelateExpr(currentRel, map, cm2, rel.getCondition()));
+ relBuilder
+ .push(frame.r)
+ .filter(decorrelateExpr(castNonNull(currentRel), map, cm2, rel.getCondition()));
// Filter does not change the input ordering.
// Filter rel does not permute the input.
@@ -1164,11 +1227,11 @@ public class RelDecorrelator implements ReflectiveVisitor {
return register(rel, relBuilder.build(), frame.oldToNewOutputs, frame.corDefOutputs);
}
- public Frame decorrelateRel(LogicalCorrelate rel) {
- return decorrelateRel((Correlate) rel);
+ public @Nullable Frame decorrelateRel(LogicalCorrelate rel, boolean isCorVarDefined) {
+ return decorrelateRel((Correlate) rel, isCorVarDefined);
}
- public Frame decorrelateRel(Correlate rel) {
+ public @Nullable Frame decorrelateRel(Correlate rel, boolean isCorVarDefined) {
//
// Rewrite logic:
//
@@ -1182,8 +1245,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
final RelNode oldLeft = rel.getInput(0);
final RelNode oldRight = rel.getInput(1);
- final Frame leftFrame = getInvoke(oldLeft, rel);
- final Frame rightFrame = getInvoke(oldRight, rel);
+ final Frame leftFrame = getInvoke(oldLeft, isCorVarDefined, rel);
+ final Frame rightFrame = getInvoke(oldRight, true, rel);
if (leftFrame == null || rightFrame == null) {
// If any input has not been rewritten, do not rewrite this rel.
@@ -1199,7 +1262,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
// Change correlator rel into a join.
// Join all the correlated variables produced by this correlator rel
// with the values generated and propagated from the right input
- final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>(rightFrame.corDefOutputs);
+ final NavigableMap<CorDef, Integer> corDefOutputs = new TreeMap<>(rightFrame.corDefOutputs);
final List<RexNode> conditions = new ArrayList<>();
final List<RelDataTypeField> newLeftOutput = leftFrame.r.getRowType().getFieldList();
int newLeftFieldCount = newLeftOutput.size();
@@ -1227,9 +1290,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
// Update the output position for the corVars: only pass on the cor
// vars that are not used in the join key.
- for (CorDef corDef : corDefOutputs.keySet()) {
- int newPos = corDefOutputs.get(corDef) + newLeftFieldCount;
- corDefOutputs.put(corDef, newPos);
+ for (Map.Entry<CorDef, Integer> entry : corDefOutputs.entrySet()) {
+ entry.setValue(entry.getValue() + newLeftFieldCount);
}
// then add any corVar from the left input. Do not need to change
@@ -1267,16 +1329,16 @@ public class RelDecorrelator implements ReflectiveVisitor {
return register(rel, newJoin, mapOldToNewOutputs, corDefOutputs);
}
- public Frame decorrelateRel(LogicalJoin rel) {
- return decorrelateRel((Join) rel);
+ public @Nullable Frame decorrelateRel(LogicalJoin rel, boolean isCorVarDefined) {
+ return decorrelateRel((Join) rel, isCorVarDefined);
}
- public Frame decorrelateRel(Join rel) {
+ public @Nullable Frame decorrelateRel(Join rel, boolean isCorVarDefined) {
// For SEMI/ANTI join decorrelate it's input directly,
// because the correlate variables can only be propagated from
// the left side, which is not supported yet.
if (!rel.getJoinType().projectsRight()) {
- return decorrelateRel((RelNode) rel);
+ return decorrelateRel((RelNode) rel, isCorVarDefined);
}
//
// Rewrite logic:
@@ -1288,8 +1350,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
final RelNode oldLeft = rel.getInput(0);
final RelNode oldRight = rel.getInput(1);
- final Frame leftFrame = getInvoke(oldLeft, rel);
- final Frame rightFrame = getInvoke(oldRight, rel);
+ final Frame leftFrame = getInvoke(oldLeft, isCorVarDefined, rel);
+ final Frame rightFrame = getInvoke(oldRight, isCorVarDefined, rel);
if (leftFrame == null || rightFrame == null) {
// If any input has not been rewritten, do not rewrite this rel.
@@ -1302,7 +1364,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
.push(rightFrame.r)
.join(
rel.getJoinType(),
- decorrelateExpr(currentRel, map, cm, rel.getCondition()),
+ decorrelateExpr(
+ castNonNull(currentRel), map, cm, rel.getCondition()),
ImmutableSet.of())
.hints(rel.getHints())
.build();
@@ -1327,7 +1390,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
i + oldLeftFieldCount, rightFrame.oldToNewOutputs.get(i) + newLeftFieldCount);
}
- final SortedMap<CorDef, Integer> corDefOutputs = new TreeMap<>(leftFrame.corDefOutputs);
+ final NavigableMap<CorDef, Integer> corDefOutputs = new TreeMap<>(leftFrame.corDefOutputs);
// Right input positions are shifted by newLeftFieldCount.
for (Map.Entry<CorDef, Integer> entry : rightFrame.corDefOutputs.entrySet()) {
@@ -1354,7 +1417,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
oldInput = oldInput0;
break;
}
- RelNode newInput = map.get(oldInput0).r;
+ RelNode newInput =
+ requireNonNull(map.get(oldInput0), () -> "map.get(oldInput0) for " + oldInput0)
+ .r;
newOrdinal += newInput.getRowType().getFieldCount();
oldOrdinal -= n;
}
@@ -1485,9 +1550,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
*/
private boolean checkCorVars(
Correlate correlate,
- Project project,
- Filter filter,
- List<RexFieldAccess> correlatedJoinKeys) {
+ @Nullable Project project,
+ @Nullable Filter filter,
+ @Nullable List<RexFieldAccess> correlatedJoinKeys) {
if (filter != null) {
assert correlatedJoinKeys != null;
@@ -1534,9 +1599,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
* @param correlate Correlate
*/
private void removeCorVarFromTree(Correlate correlate) {
- if (cm.mapCorToCorRel.get(correlate.getCorrelationId()) == correlate) {
- cm.mapCorToCorRel.remove(correlate.getCorrelationId());
- }
+ cm.mapCorToCorRel.remove(correlate.getCorrelationId(), correlate);
}
/**
@@ -1581,7 +1644,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
RelNode rel,
RelNode newRel,
Map<Integer, Integer> oldToNewOutputs,
- SortedMap<CorDef, Integer> corDefOutputs) {
+ NavigableMap<CorDef, Integer> corDefOutputs) {
final Frame frame = new Frame(rel, newRel, corDefOutputs, oldToNewOutputs);
map.put(rel, frame);
return frame;
@@ -1613,9 +1676,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
private final CorelMap cm;
private DecorrelateRexShuttle(RelNode currentRel, Map<RelNode, Frame> map, CorelMap cm) {
- this.currentRel = Objects.requireNonNull(currentRel);
- this.map = Objects.requireNonNull(map);
- this.cm = Objects.requireNonNull(cm);
+ this.currentRel = requireNonNull(currentRel, "currentRel");
+ this.map = requireNonNull(map, "map");
+ this.cm = requireNonNull(cm, "cm");
}
@Override
@@ -1663,13 +1726,13 @@ public class RelDecorrelator implements ReflectiveVisitor {
final RexBuilder rexBuilder;
final RelDataTypeFactory typeFactory;
final boolean projectPulledAboveLeftCorrelator;
- final RexInputRef nullIndicator;
+ final @Nullable RexInputRef nullIndicator;
final ImmutableSet<Integer> isCount;
RemoveCorrelationRexShuttle(
RexBuilder rexBuilder,
boolean projectPulledAboveLeftCorrelator,
- RexInputRef nullIndicator,
+ @Nullable RexInputRef nullIndicator,
Set<Integer> isCount) {
this.projectPulledAboveLeftCorrelator = projectPulledAboveLeftCorrelator;
this.nullIndicator = nullIndicator; // may be null
@@ -1679,7 +1742,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
private RexNode createCaseExpression(
- RexInputRef nullInputRef, RexLiteral lit, RexNode rexNode) {
+ RexInputRef nullInputRef, @Nullable RexLiteral lit, RexNode rexNode) {
RexNode[] caseOperands = new RexNode[3];
// Construct a CASE expression to handle the null indicator.
@@ -1754,7 +1817,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
int pos = inputRef.getIndex();
RexInputRef newInputRef = new RexInputRef(leftInputFieldCount + pos, newType);
- if ((isCount != null) && isCount.contains(pos)) {
+ if (isCount.contains(pos)) {
return createCaseExpression(
newInputRef, rexBuilder.makeExactLiteral(BigDecimal.ZERO), newInputRef);
} else {
@@ -1857,7 +1920,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
/** Creates a RemoveSingleAggregateRule. */
- protected RemoveSingleAggregateRule(Config config) {
+ RemoveSingleAggregateRule(Config config) {
super(config);
}
@@ -1868,7 +1931,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
Aggregate aggregate = call.rel(2);
// check singleAggRel is single_value agg
- if ((!singleAggregate.getGroupSet().isEmpty())
+ if (!singleAggregate.getGroupSet().isEmpty()
|| (singleAggregate.getAggCallList().size() != 1)
|| !(singleAggregate.getAggCallList().get(0).getAggregation()
instanceof SqlSingleValueAggFunction)) {
@@ -1943,9 +2006,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
/** Creates a RemoveCorrelationForScalarProjectRule. */
- protected RemoveCorrelationForScalarProjectRule(Config config) {
+ RemoveCorrelationForScalarProjectRule(Config config) {
super(config);
- this.d = Objects.requireNonNull(config.decorrelator());
+ this.d = requireNonNull(config.decorrelator());
}
@Override
@@ -1979,7 +2042,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
// check that the agg is of the following type:
// doing a single_value() on the entire input
- if ((!aggregate.getGroupSet().isEmpty())
+ if (!aggregate.getGroupSet().isEmpty()
|| (aggregate.getAggCallList().size() != 1)
|| !(aggregate.getAggCallList().get(0).getAggregation()
instanceof SqlSingleValueAggFunction)) {
@@ -2174,9 +2237,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
/** Creates a RemoveCorrelationForScalarAggregateRule. */
- protected RemoveCorrelationForScalarAggregateRule(Config config) {
+ RemoveCorrelationForScalarAggregateRule(Config config) {
super(config);
- d = Objects.requireNonNull(config.decorrelator());
+ d = requireNonNull(config.decorrelator());
}
@Override
@@ -2568,9 +2631,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
/** Creates an AdjustProjectForCountAggregateRule. */
- protected AdjustProjectForCountAggregateRule(Config config) {
+ AdjustProjectForCountAggregateRule(Config config) {
super(config);
- this.d = Objects.requireNonNull(config.decorrelator());
+ this.d = requireNonNull(config.decorrelator());
}
@Override
@@ -2741,7 +2804,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return this == o
|| o instanceof CorRef
&& uniqueKey == ((CorRef) o).uniqueKey
@@ -2749,7 +2812,8 @@ public class RelDecorrelator implements ReflectiveVisitor {
&& field == ((CorRef) o).field;
}
- public int compareTo(@Nonnull CorRef o) {
+ @Override
+ public int compareTo(CorRef o) {
int c = corr.compareTo(o.corr);
if (c != 0) {
return c;
@@ -2787,14 +2851,15 @@ public class RelDecorrelator implements ReflectiveVisitor {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return this == o
|| o instanceof CorDef
&& corr == ((CorDef) o).corr
&& field == ((CorDef) o).field;
}
- public int compareTo(@Nonnull CorDef o) {
+ @Override
+ public int compareTo(CorDef o) {
int c = corr.compareTo(o.corr);
if (c != 0) {
return c;
@@ -2824,13 +2889,13 @@ public class RelDecorrelator implements ReflectiveVisitor {
*/
protected static class CorelMap {
private final Multimap<RelNode, CorRef> mapRefRelToCorRef;
- private final SortedMap<CorrelationId, RelNode> mapCorToCorRel;
+ private final NavigableMap<CorrelationId, RelNode> mapCorToCorRel;
private final Map<RexFieldAccess, CorRef> mapFieldAccessToCorRef;
// TODO: create immutable copies of all maps
private CorelMap(
Multimap<RelNode, CorRef> mapRefRelToCorRef,
- SortedMap<CorrelationId, RelNode> mapCorToCorRel,
+ NavigableMap<CorrelationId, RelNode> mapCorToCorRel,
Map<RexFieldAccess, CorRef> mapFieldAccessToCorRef) {
this.mapRefRelToCorRef = mapRefRelToCorRef;
this.mapCorToCorRel = mapCorToCorRel;
@@ -2848,10 +2913,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
+ "\n";
}
+ @SuppressWarnings("UndefinedEquals")
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj == this
|| obj instanceof CorelMap
+ // TODO: Multimap does not have well-defined equals behavior
&& mapRefRelToCorRef.equals(((CorelMap) obj).mapRefRelToCorRef)
&& mapCorToCorRel.equals(((CorelMap) obj).mapCorToCorRel)
&& mapFieldAccessToCorRef.equals(
@@ -2866,12 +2933,12 @@ public class RelDecorrelator implements ReflectiveVisitor {
/** Creates a CorelMap with given contents. */
public static CorelMap of(
SortedSetMultimap<RelNode, CorRef> mapRefRelToCorVar,
- SortedMap<CorrelationId, RelNode> mapCorToCorRel,
+ NavigableMap<CorrelationId, RelNode> mapCorToCorRel,
Map<RexFieldAccess, CorRef> mapFieldAccessToCorVar) {
return new CorelMap(mapRefRelToCorVar, mapCorToCorRel, mapFieldAccessToCorVar);
}
- public SortedMap<CorrelationId, RelNode> getMapCorToCorRel() {
+ public NavigableMap<CorrelationId, RelNode> getMapCorToCorRel() {
return mapCorToCorRel;
}
@@ -2887,7 +2954,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
/** Builds a {@link org.apache.calcite.sql2rel.RelDecorrelator.CorelMap}. */
public static class CorelMapBuilder extends RelHomogeneousShuttle {
- final SortedMap<CorrelationId, RelNode> mapCorToCorRel = new TreeMap<>();
+ final NavigableMap<CorrelationId, RelNode> mapCorToCorRel = new TreeMap<>();
final SortedSetMultimap<RelNode, CorRef> mapRefRelToCorRef =
MultimapBuilder.SortedSetMultimapBuilder.hashKeys().treeSetValues().build();
@@ -3003,9 +3070,9 @@ public class RelDecorrelator implements ReflectiveVisitor {
Frame(
RelNode oldRel,
RelNode r,
- SortedMap<CorDef, Integer> corDefOutputs,
+ NavigableMap<CorDef, Integer> corDefOutputs,
Map<Integer, Integer> oldToNewOutputs) {
- this.r = Objects.requireNonNull(r);
+ this.r = requireNonNull(r, "r");
this.corDefOutputs = ImmutableSortedMap.copyOf(corDefOutputs);
this.oldToNewOutputs = ImmutableSortedMap.copyOf(oldToNewOutputs);
assert allLessThan(
@@ -3028,4 +3095,22 @@ public class RelDecorrelator implements ReflectiveVisitor {
/** Sets {@link #decorrelator}. */
Config withDecorrelator(RelDecorrelator decorrelator);
}
+
+ // -------------------------------------------------------------------------
+ // Getter/Setter
+ // -------------------------------------------------------------------------
+
+ /**
+ * Returns the {@code visitor} on which the {@code MethodDispatcher} dispatches each {@code
+ * decorrelateRel} method, the default implementation returns this instance, if you got a
+ * sub-class, override this method to replace the {@code visitor} as the sub-class instance.
+ */
+ protected RelDecorrelator getVisitor() {
+ return this;
+ }
+
+ /** Returns the rules applied on the rel after decorrelation, never null. */
+ protected Collection<RelOptRule> getPostDecorrelateRules() {
+ return Collections.emptyList();
+ }
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 908f788cfa0..7aaf3b01eeb 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -21,6 +21,11 @@ package org.apache.calcite.sql2rel;
import org.apache.flink.table.planner.alias.ClearJoinHintWithInvalidPropagationShuttle;
import org.apache.flink.table.planner.hint.FlinkHints;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import org.apache.calcite.avatica.util.Spaces;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.Convention;
@@ -78,7 +83,6 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
-import org.apache.calcite.rex.RexCallBinding;
import org.apache.calcite.rex.RexCorrelVariable;
import org.apache.calcite.rex.RexDynamicParam;
import org.apache.calcite.rex.RexFieldAccess;
@@ -132,6 +136,7 @@ import org.apache.calcite.sql.SqlSelectKeyword;
import org.apache.calcite.sql.SqlSetOperator;
import org.apache.calcite.sql.SqlSnapshot;
import org.apache.calcite.sql.SqlUnnestOperator;
+import org.apache.calcite.sql.SqlUnpivot;
import org.apache.calcite.sql.SqlUpdate;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.SqlValuesOperator;
@@ -139,7 +144,6 @@ import org.apache.calcite.sql.SqlWindow;
import org.apache.calcite.sql.SqlWith;
import org.apache.calcite.sql.SqlWithItem;
import org.apache.calcite.sql.fun.SqlCase;
-import org.apache.calcite.sql.fun.SqlCountAggFunction;
import org.apache.calcite.sql.fun.SqlInOperator;
import org.apache.calcite.sql.fun.SqlQuantifyOperator;
import org.apache.calcite.sql.fun.SqlRowOperator;
@@ -180,10 +184,9 @@ import org.apache.calcite.util.NumberUtil;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
import org.apache.calcite.util.trace.CalciteTrace;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
-import javax.annotation.Nonnull;
-
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.AbstractList;
@@ -201,17 +204,29 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
+import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
+import static java.util.Objects.requireNonNull;
+import static org.apache.calcite.linq4j.Nullness.castNonNull;
import static org.apache.calcite.sql.SqlUtil.stripAs;
/**
- * Converts a SQL parse tree (consisting of {@link SqlNode} objects) into a relational algebra
- * expression (consisting of {@link RelNode} objects).
+ * Converts a SQL parse tree (consisting of {@link org.apache.calcite.sql.SqlNode} objects) into a
+ * relational algebra expression (consisting of {@link org.apache.calcite.rel.RelNode} objects).
*
* <p>The public entry points are: {@link #convertQuery}, {@link #convertExpression(SqlNode)}.
+ *
+ * <p>FLINK modifications are at lines
+ *
+ * <ol>
+ * <li>Added in FLINK-29081, FLINK-28682: Lines 634 ~ 644
+ * <li>Added in FLINK-28682: Lines 2097 ~ 2114
+ * <li>Added in FLINK-28682: Lines 2152 ~ 2180
+ * <li>Added in FLINK-20873: Lines 5159 ~ 5168
+ * </ol>
*/
@SuppressWarnings("UnstableApiUsage")
public class SqlToRelConverter {
@@ -234,19 +249,19 @@ public class SqlToRelConverter {
// ~ Instance fields --------------------------------------------------------
- protected final SqlValidator validator;
+ protected final @Nullable SqlValidator validator;
protected final RexBuilder rexBuilder;
protected final Prepare.CatalogReader catalogReader;
protected final RelOptCluster cluster;
private SubQueryConverter subQueryConverter;
protected final Map<RelNode, Integer> leaves = new HashMap<>();
- private final List<SqlDynamicParam> dynamicParamSqlNodes = new ArrayList<>();
+ private final List<@Nullable SqlDynamicParam> dynamicParamSqlNodes = new ArrayList<>();
private final SqlOperatorTable opTab;
protected final RelDataTypeFactory typeFactory;
private final SqlNodeToRexConverter exprConverter;
private final HintStrategyTable hintStrategies;
private int explainParamCount;
- public final Config config;
+ public final SqlToRelConverter.Config config;
private final RelBuilder relBuilder;
/** Fields used in name resolution for correlated sub-queries. */
@@ -313,7 +328,7 @@ public class SqlToRelConverter {
/* Creates a converter. */
public SqlToRelConverter(
RelOptTable.ViewExpander viewExpander,
- SqlValidator validator,
+ @Nullable SqlValidator validator,
Prepare.CatalogReader catalogReader,
RelOptCluster cluster,
SqlRexConvertletTable convertletTable,
@@ -328,7 +343,7 @@ public class SqlToRelConverter {
this.typeFactory = rexBuilder.getTypeFactory();
this.exprConverter = new SqlNodeToRexConverterImpl(convertletTable);
this.explainParamCount = 0;
- this.config = Objects.requireNonNull(config);
+ this.config = requireNonNull(config, "config");
this.relBuilder =
config.getRelBuilderFactory()
.create(cluster, null)
@@ -336,11 +351,27 @@ public class SqlToRelConverter {
this.hintStrategies = config.getHintStrategyTable();
cluster.setHintStrategies(this.hintStrategies);
- this.cluster = Objects.requireNonNull(cluster);
+ this.cluster = requireNonNull(cluster, "cluster");
}
// ~ Methods ----------------------------------------------------------------
+ private SqlValidator validator() {
+ return requireNonNull(validator, "validator");
+ }
+
+ private <T extends SqlValidatorNamespace> T getNamespace(SqlNode node) {
+ //noinspection unchecked
+ return (T)
+ requireNonNull(
+ getNamespaceOrNull(node), () -> "Namespace is not found for " + node);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T extends SqlValidatorNamespace> @Nullable T getNamespaceOrNull(SqlNode node) {
+ return (@Nullable T) validator().getNamespace(node);
+ }
+
/** Returns the RelOptCluster in use. */
public RelOptCluster getCluster() {
return cluster;
@@ -372,7 +403,7 @@ public class SqlToRelConverter {
if (sqlNode == null) {
throw Util.needToImplement("dynamic param type inference");
}
- return validator.getValidatedNodeType(sqlNode);
+ return validator().getValidatedNodeType(sqlNode);
}
/**
@@ -438,9 +469,9 @@ public class SqlToRelConverter {
// validator type information associated with its result,
// hence the namespace check above.)
final List<RelDataTypeField> validatedFields =
- validator.getValidatedNodeType(query).getFieldList();
+ validator().getValidatedNodeType(query).getFieldList();
final RelDataType validatedRowType =
- validator
+ validator()
.getTypeFactory()
.createStructType(
Pair.right(validatedFields),
@@ -451,7 +482,7 @@ public class SqlToRelConverter {
final List<RelDataTypeField> convertedFields =
result.getRowType().getFieldList().subList(0, validatedFields.size());
final RelDataType convertedRowType =
- validator.getTypeFactory().createStructType(convertedFields);
+ validator().getTypeFactory().createStructType(convertedFields);
if (!RelOptUtil.equal(
"validated row type",
@@ -476,7 +507,7 @@ public class SqlToRelConverter {
new RelStructuredTypeFlattener(
relBuilder,
rexBuilder,
- createToRelContext(com.google.common.collect.ImmutableList.of()),
+ createToRelContext(ImmutableList.of()),
restructure);
return typeFlattener.rewrite(rootRel);
}
@@ -526,8 +557,7 @@ public class SqlToRelConverter {
if (!ordered
&& collations != null
&& !collations.isEmpty()
- && !collations.equals(
- com.google.common.collect.ImmutableList.of(RelCollations.EMPTY))) {
+ && !collations.equals(ImmutableList.of(RelCollations.EMPTY))) {
final RelTraitSet traitSet =
rootRel.getTraitSet().replace(RelCollationTraitDef.INSTANCE, collations);
rootRel = rootRel.copy(traitSet, rootRel.getInputs());
@@ -564,7 +594,7 @@ public class SqlToRelConverter {
*/
public RelRoot convertQuery(SqlNode query, final boolean needsValidation, final boolean top) {
if (needsValidation) {
- query = validator.validate(query);
+ query = validator().validate(query);
}
RelNode result = convertQueryRecursive(query, top, null).rel;
@@ -590,7 +620,7 @@ public class SqlToRelConverter {
SqlExplainLevel.EXPPLAN_ATTRIBUTES));
}
- final RelDataType validatedRowType = validator.getValidatedNodeType(query);
+ final RelDataType validatedRowType = validator().getValidatedNodeType(query);
List<RelHint> hints = new ArrayList<>();
if (query.getKind() == SqlKind.SELECT) {
final SqlSelect select = (SqlSelect) query;
@@ -598,7 +628,7 @@ public class SqlToRelConverter {
hints = SqlUtil.getRelHint(hintStrategies, select.getHints());
}
}
-
+ // propagate the hints.
result = RelOptUtil.propagateRelHints(result, false);
// ----- FLINK MODIFICATION BEGIN -----
@@ -626,8 +656,8 @@ public class SqlToRelConverter {
public static boolean isOrdered(SqlNode query) {
switch (query.getKind()) {
case SELECT:
- return ((SqlSelect) query).getOrderList() != null
- && ((SqlSelect) query).getOrderList().size() > 0;
+ SqlNodeList orderList = ((SqlSelect) query).getOrderList();
+ return orderList != null && orderList.size() > 0;
case WITH:
return isOrdered(((SqlWith) query).body);
case ORDER_BY:
@@ -637,7 +667,7 @@ public class SqlToRelConverter {
}
}
- private RelCollation requiredCollation(RelNode r) {
+ private static RelCollation requiredCollation(RelNode r) {
if (r instanceof Sort) {
return ((Sort) r).collation;
}
@@ -652,15 +682,17 @@ public class SqlToRelConverter {
/** Converts a SELECT statement's parse tree into a relational expression. */
public RelNode convertSelect(SqlSelect select, boolean top) {
- final SqlValidatorScope selectScope = validator.getWhereScope(select);
+ final SqlValidatorScope selectScope = validator().getWhereScope(select);
final Blackboard bb = createBlackboard(selectScope, null, top);
convertSelectImpl(bb, select);
- return bb.root;
+ return castNonNull(bb.root);
}
/** Factory method for creating translation workspace. */
protected Blackboard createBlackboard(
- SqlValidatorScope scope, Map<String, RexNode> nameToNodeMap, boolean top) {
+ @Nullable SqlValidatorScope scope,
+ @Nullable Map<String, RexNode> nameToNodeMap,
+ boolean top) {
return new Blackboard(scope, nameToNodeMap, top);
}
@@ -674,7 +706,7 @@ public class SqlToRelConverter {
gatherOrderExprs(bb, select, select.getOrderList(), orderExprList, collationList);
final RelCollation collation = cluster.traitSet().canonize(RelCollations.of(collationList));
- if (validator.isAggregate(select)) {
+ if (validator().isAggregate(select)) {
convertAgg(bb, select, orderExprList);
} else {
convertSelectList(bb, select, orderExprList);
@@ -690,23 +722,25 @@ public class SqlToRelConverter {
final List<RelHint> hints = SqlUtil.getRelHint(hintStrategies, select.getHints());
// Attach the hints to the first Hintable node we found from the root node.
bb.setRoot(
- bb.root.accept(
- new RelShuttleImpl() {
- boolean attached = false;
-
- @Override
- public RelNode visitChild(RelNode parent, int i, RelNode child) {
- if (parent instanceof Hintable && !attached) {
- attached = true;
- return ((Hintable) parent).attachHints(hints);
- } else {
- return super.visitChild(parent, i, child);
- }
- }
- }),
+ bb.root()
+ .accept(
+ new RelShuttleImpl() {
+ boolean attached = false;
+
+ @Override
+ public RelNode visitChild(
+ RelNode parent, int i, RelNode child) {
+ if (parent instanceof Hintable && !attached) {
+ attached = true;
+ return ((Hintable) parent).attachHints(hints);
+ } else {
+ return super.visitChild(parent, i, child);
+ }
+ }
+ }),
true);
} else {
- bb.setRoot(bb.root, true);
+ bb.setRoot(bb.root(), true);
}
}
@@ -714,9 +748,10 @@ public class SqlToRelConverter {
* Having translated 'SELECT ... FROM ... [GROUP BY ...] [HAVING ...]', adds a relational
* expression to make the results unique.
*
- * <p>If the SELECT clause contains duplicate expressions, adds {@link LogicalProject}s so that
- * we are grouping on the minimal set of keys. The performance gain isn't huge, but it is
- * difficult to detect these duplicate expressions later.
+ * <p>If the SELECT clause contains duplicate expressions, adds {@link
+ * org.apache.calcite.rel.logical.LogicalProject}s so that we are grouping on the minimal set of
+ * keys. The performance gain isn't huge, but it is difficult to detect these duplicate
+ * expressions later.
*
* @param bb Blackboard
* @param checkForDupExprs Check for duplicate expressions
@@ -758,12 +793,12 @@ public class SqlToRelConverter {
rel =
LogicalProject.create(
rel,
- com.google.common.collect.ImmutableList.of(),
+ ImmutableList.of(),
Pair.left(newProjects),
Pair.right(newProjects));
bb.root = rel;
distinctify(bb, false);
- rel = bb.root;
+ rel = bb.root();
// Create the expressions to reverse the mapping.
// Project($0, $1, $0, $2).
@@ -773,14 +808,14 @@ public class SqlToRelConverter {
RelDataTypeField field = fields.get(i);
undoProjects.add(
Pair.of(
- new RexInputRef(squished.get(origin), field.getType()),
+ new RexInputRef(castNonNull(squished.get(origin)), field.getType()),
field.getName()));
}
rel =
LogicalProject.create(
rel,
- com.google.common.collect.ImmutableList.of(),
+ ImmutableList.of(),
Pair.left(undoProjects),
Pair.right(undoProjects));
bb.setRoot(rel, false);
@@ -788,15 +823,11 @@ public class SqlToRelConverter {
return;
}
+ assert rel != null : "rel must not be null, root = " + bb.root;
// Usual case: all of the expressions in the SELECT clause are
// different.
final ImmutableBitSet groupSet = ImmutableBitSet.range(rel.getRowType().getFieldCount());
- rel =
- createAggregate(
- bb,
- groupSet,
- com.google.common.collect.ImmutableList.of(groupSet),
- com.google.common.collect.ImmutableList.of());
+ rel = createAggregate(bb, groupSet, ImmutableList.of(groupSet), ImmutableList.of());
bb.setRoot(rel, false);
}
@@ -820,17 +851,17 @@ public class SqlToRelConverter {
Blackboard bb,
RelCollation collation,
List<SqlNode> orderExprList,
- SqlNode offset,
- SqlNode fetch) {
+ @Nullable SqlNode offset,
+ @Nullable SqlNode fetch) {
if (removeSortInSubQuery(bb.top)
|| select.getOrderList() == null
- || select.getOrderList().getList().isEmpty()) {
+ || select.getOrderList().isEmpty()) {
assert removeSortInSubQuery(bb.top) || collation.getFieldCollations().isEmpty();
if ((offset == null
|| (offset instanceof SqlLiteral
- && ((SqlLiteral) offset)
- .bigDecimalValue()
- .equals(BigDecimal.ZERO)))
+ && Objects.equals(
+ ((SqlLiteral) offset).bigDecimalValue(),
+ BigDecimal.ZERO)))
&& fetch == null) {
return;
}
@@ -839,7 +870,7 @@ public class SqlToRelConverter {
// Create a sorter using the previously constructed collations.
bb.setRoot(
LogicalSort.create(
- bb.root,
+ bb.root(),
collation,
offset == null ? null : convertExpression(offset),
fetch == null ? null : convertExpression(fetch)),
@@ -852,15 +883,15 @@ public class SqlToRelConverter {
// If it is the top node, use the real collation, but don't trim fields.
if (orderExprList.size() > 0 && !bb.top) {
final List<RexNode> exprs = new ArrayList<>();
- final RelDataType rowType = bb.root.getRowType();
+ final RelDataType rowType = bb.root().getRowType();
final int fieldCount = rowType.getFieldCount() - orderExprList.size();
for (int i = 0; i < fieldCount; i++) {
- exprs.add(rexBuilder.makeInputRef(bb.root, i));
+ exprs.add(rexBuilder.makeInputRef(bb.root(), i));
}
bb.setRoot(
LogicalProject.create(
- bb.root,
- com.google.common.collect.ImmutableList.of(),
+ bb.root(),
+ ImmutableList.of(),
exprs,
rowType.getFieldNames().subList(0, fieldCount)),
false);
@@ -885,6 +916,7 @@ public class SqlToRelConverter {
try {
SqlVisitor<Void> visitor =
new SqlBasicVisitor<Void>() {
+ @Override
public Void visit(SqlCall call) {
if (call.getOperator() instanceof SqlInOperator) {
throw new Util.FoundOne(call);
@@ -937,7 +969,10 @@ public class SqlToRelConverter {
SqlParserPos.ZERO, thenOperand);
thenOperands.add(pushDownNotForIn(scope, reg(scope, not)));
}
- SqlNode elseOperand = caseNode.getElseOperand();
+ SqlNode elseOperand =
+ requireNonNull(
+ caseNode.getElseOperand(),
+ "getElseOperand for " + caseNode);
if (!SqlUtil.isNull(elseOperand)) {
// "not(unknown)" is "unknown", so no need to simplify
final SqlCall not =
@@ -1000,7 +1035,12 @@ public class SqlToRelConverter {
scope,
SqlStdOperatorTable.NOT_IN.createCall(
SqlParserPos.ZERO, call.getOperandList()));
+ default:
+ break;
}
+ break;
+ default:
+ break;
}
return sqlNode;
}
@@ -1020,11 +1060,11 @@ public class SqlToRelConverter {
* @param bb Blackboard
* @param where WHERE clause, may be null
*/
- private void convertWhere(final Blackboard bb, final SqlNode where) {
+ private void convertWhere(final Blackboard bb, final @Nullable SqlNode where) {
if (where == null) {
return;
}
- SqlNode newWhere = pushDownNotForIn(bb.scope, where);
+ SqlNode newWhere = pushDownNotForIn(bb.scope(), where);
replaceSubQueries(bb, newWhere, RelOptUtil.Logic.UNKNOWN_AS_FALSE);
final RexNode convertedWhere = bb.convertExpression(newWhere);
final RexNode convertedWhere2 = RexUtil.removeNullabilityCast(typeFactory, convertedWhere);
@@ -1036,18 +1076,13 @@ public class SqlToRelConverter {
final RelFactories.FilterFactory filterFactory = RelFactories.DEFAULT_FILTER_FACTORY;
final RelNode filter =
- filterFactory.createFilter(
- bb.root, convertedWhere2, com.google.common.collect.ImmutableSet.of());
+ filterFactory.createFilter(bb.root(), convertedWhere2, ImmutableSet.of());
final RelNode r;
final CorrelationUse p = getCorrelationUse(bb, filter);
if (p != null) {
assert p.r instanceof Filter;
Filter f = (Filter) p.r;
- r =
- LogicalFilter.create(
- f.getInput(),
- f.getCondition(),
- com.google.common.collect.ImmutableSet.of(p.id));
+ r = LogicalFilter.create(f.getInput(), f.getCondition(), ImmutableSet.of(p.id));
} else {
r = filter;
}
@@ -1082,9 +1117,7 @@ public class SqlToRelConverter {
case MULTISET_QUERY_CONSTRUCTOR:
case MULTISET_VALUE_CONSTRUCTOR:
case ARRAY_QUERY_CONSTRUCTOR:
- rel =
- convertMultisets(
- com.google.common.collect.ImmutableList.of(subQuery.node), bb);
+ rel = convertMultisets(ImmutableList.of(subQuery.node), bb);
subQuery.expr = bb.register(rel, JoinRelType.INNER);
return;
@@ -1108,9 +1141,7 @@ public class SqlToRelConverter {
}
break;
default:
- leftKeys =
- com.google.common.collect.ImmutableList.of(
- bb.convertExpression(leftKeyNode));
+ leftKeys = ImmutableList.of(bb.convertExpression(leftKeyNode));
}
if (query instanceof SqlNodeList) {
@@ -1167,7 +1198,7 @@ public class SqlToRelConverter {
}
final RelDataType targetRowType =
SqlTypeUtil.promoteToRowType(
- typeFactory, validator.getValidatedNodeType(leftKeyNode), null);
+ typeFactory, validator().getValidatedNodeType(leftKeyNode), null);
final boolean notIn = call.getOperator().kind == SqlKind.NOT_IN;
converted =
convertExists(
@@ -1187,17 +1218,18 @@ public class SqlToRelConverter {
LogicalAggregate aggregate =
LogicalAggregate.create(
seek,
- com.google.common.collect.ImmutableList.of(),
+ ImmutableList.of(),
ImmutableBitSet.of(),
null,
- com.google.common.collect.ImmutableList.of(
+ ImmutableList.of(
AggregateCall.create(
SqlStdOperatorTable.COUNT,
false,
false,
false,
- com.google.common.collect.ImmutableList.of(),
+ ImmutableList.of(),
-1,
+ null,
RelCollations.EMPTY,
longType,
null),
@@ -1208,16 +1240,17 @@ public class SqlToRelConverter {
false,
args,
-1,
+ null,
RelCollations.EMPTY,
longType,
null)));
LogicalJoin join =
LogicalJoin.create(
- bb.root,
+ bb.root(),
aggregate,
- com.google.common.collect.ImmutableList.of(),
+ ImmutableList.of(),
rexBuilder.makeLiteral(true),
- com.google.common.collect.ImmutableSet.of(),
+ ImmutableSet.of(),
JoinRelType.INNER);
bb.setRoot(join, false);
}
@@ -1234,6 +1267,9 @@ public class SqlToRelConverter {
if (!converted.indicator) {
logic = RelOptUtil.Logic.TRUE_FALSE;
}
+ break;
+ default:
+ break;
}
subQuery.expr = translateIn(logic, bb.root, rex);
if (notIn) {
@@ -1258,10 +1294,11 @@ public class SqlToRelConverter {
}
final SqlValidatorScope seekScope =
(query instanceof SqlSelect)
- ? validator.getSelectScope((SqlSelect) query)
+ ? validator().getSelectScope((SqlSelect) query)
: null;
final Blackboard seekBb = createBlackboard(seekScope, null, false);
final RelNode seekRel = convertQueryOrInList(seekBb, query, null);
+ requireNonNull(seekRel, () -> "seelkRel is null for query " + query);
// An EXIST sub-query whose inner child has at least 1 tuple
// (e.g. an Aggregate with no grouping columns or non-empty Values
// node) should be simplified to a Boolean constant expression.
@@ -1331,7 +1368,7 @@ public class SqlToRelConverter {
}
}
- private RexNode translateIn(RelOptUtil.Logic logic, RelNode root, final RexNode rex) {
+ private RexNode translateIn(RelOptUtil.Logic logic, @Nullable RelNode root, final RexNode rex) {
switch (logic) {
case TRUE:
return rexBuilder.makeLiteral(true);
@@ -1377,7 +1414,7 @@ public class SqlToRelConverter {
// cross join (select count(*) as c, count(deptno) as ck from v) as ct
// left join (select distinct deptno, true as i from v) as dt
// on e.deptno = dt.deptno
- final Join join = (Join) root;
+ final Join join = (Join) requireNonNull(root, "root");
final Project left = (Project) join.getLeft();
final RelNode leftLeft = ((Join) left.getInput()).getLeft();
final int leftLeftCount = leftLeft.getRowType().getFieldCount();
@@ -1392,8 +1429,7 @@ public class SqlToRelConverter {
final RexLiteral falseLiteral = rexBuilder.makeLiteral(false);
final RexNode unknownLiteral = rexBuilder.makeNullLiteral(trueLiteral.getType());
- final com.google.common.collect.ImmutableList.Builder<RexNode> args =
- com.google.common.collect.ImmutableList.builder();
+ final ImmutableList.Builder<RexNode> args = ImmutableList.builder();
args.add(
rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, cRef, zero),
falseLiteral,
@@ -1418,7 +1454,7 @@ public class SqlToRelConverter {
}
private static boolean containsNullLiteral(SqlNodeList valueList) {
- for (SqlNode node : valueList.getList()) {
+ for (SqlNode node : valueList) {
if (node instanceof SqlLiteral) {
SqlLiteral lit = (SqlLiteral) node;
if (lit.getValue() == null) {
@@ -1484,9 +1520,10 @@ public class SqlToRelConverter {
// If there is a limit with 0 or 1,
// it is ensured to produce a single value
- if (select.getFetch() != null && select.getFetch() instanceof SqlNumericLiteral) {
- SqlNumericLiteral limitNum = (SqlNumericLiteral) select.getFetch();
- if (((BigDecimal) limitNum.getValue()).intValue() < 2) {
+ SqlNode fetch = select.getFetch();
+ if (fetch instanceof SqlNumericLiteral) {
+ long value = ((SqlNumericLiteral) fetch).getValueAs(Long.class);
+ if (value < 2) {
return plan;
}
}
@@ -1514,7 +1551,7 @@ public class SqlToRelConverter {
* @param op The operator (IN, NOT IN, > SOME, ...)
* @return converted expression
*/
- private RexNode convertInToOr(
+ private @Nullable RexNode convertInToOr(
final Blackboard bb,
final List<RexNode> leftKeys,
SqlNodeList valuesList,
@@ -1553,8 +1590,13 @@ public class SqlToRelConverter {
rexBuilder.makeCall(
comparisonOp,
pair.left,
+ // TODO: remove requireNonNull when
+ // checkerframework issue resolved
ensureSqlType(
- pair.left.getType(),
+ requireNonNull(
+ pair.left,
+ "pair.left")
+ .getType(),
bb.convertExpression(
pair.right)))));
}
@@ -1567,7 +1609,7 @@ public class SqlToRelConverter {
case NOT_IN:
return rexBuilder.makeCall(
SqlStdOperatorTable.NOT,
- RexUtil.composeDisjunction(rexBuilder, comparisons, true));
+ RexUtil.composeDisjunction(rexBuilder, comparisons));
case IN:
case SOME:
return RexUtil.composeDisjunction(rexBuilder, comparisons, true);
@@ -1592,8 +1634,9 @@ public class SqlToRelConverter {
/**
* Gets the list size threshold under which {@link #convertInToOr} is used. Lists of this size
* or greater will instead be converted to use a join against an inline table ({@link
- * LogicalValues}) rather than a predicate. A threshold of 0 forces usage of an inline table in
- * all cases; a threshold of Integer.MAX_VALUE forces usage of OR in all cases
+ * org.apache.calcite.rel.logical.LogicalValues}) rather than a predicate. A threshold of 0
+ * forces usage of an inline table in all cases; a threshold of Integer.MAX_VALUE forces usage
+ * of OR in all cases
*
* @return threshold, default {@link #DEFAULT_IN_SUB_QUERY_THRESHOLD}
*/
@@ -1621,16 +1664,18 @@ public class SqlToRelConverter {
RelOptUtil.SubQueryType subQueryType,
RelOptUtil.Logic logic,
boolean notIn,
- RelDataType targetDataType) {
+ @Nullable RelDataType targetDataType) {
final SqlValidatorScope seekScope =
- (seek instanceof SqlSelect) ? validator.getSelectScope((SqlSelect) seek) : null;
+ (seek instanceof SqlSelect) ? validator().getSelectScope((SqlSelect) seek) : null;
final Blackboard seekBb = createBlackboard(seekScope, null, false);
RelNode seekRel = convertQueryOrInList(seekBb, seek, targetDataType);
+ requireNonNull(seekRel, () -> "seelkRel is null for query " + seek);
return RelOptUtil.createExistsPlan(seekRel, subQueryType, logic, notIn, relBuilder);
}
- private RelNode convertQueryOrInList(Blackboard bb, SqlNode seek, RelDataType targetRowType) {
+ private @Nullable RelNode convertQueryOrInList(
+ Blackboard bb, SqlNode seek, @Nullable RelDataType targetRowType) {
// NOTE: Once we start accepting single-row queries as row constructors,
// there will be an ambiguity here for a case like X IN ((SELECT Y FROM
// Z)). The SQL standard resolves the ambiguity by saying that a lone
@@ -1638,34 +1683,32 @@ public class SqlToRelConverter {
// expression. The semantic difference is that a table expression can
// return multiple rows.
if (seek instanceof SqlNodeList) {
- return convertRowValues(bb, seek, ((SqlNodeList) seek).getList(), false, targetRowType);
+ return convertRowValues(bb, seek, (SqlNodeList) seek, false, targetRowType);
} else {
return convertQueryRecursive(seek, false, null).project();
}
}
- private RelNode convertRowValues(
+ private @Nullable RelNode convertRowValues(
Blackboard bb,
SqlNode rowList,
Collection<SqlNode> rows,
boolean allowLiteralsOnly,
- RelDataType targetRowType) {
+ @Nullable RelDataType targetRowType) {
// NOTE jvs 30-Apr-2006: We combine all rows consisting entirely of
// literals into a single LogicalValues; this gives the optimizer a smaller
// input tree. For everything else (computed expressions, row
// sub-queries), we union each row in as a projection on top of a
// LogicalOneRow.
- final com.google.common.collect.ImmutableList.Builder<
- com.google.common.collect.ImmutableList<RexLiteral>>
- tupleList = com.google.common.collect.ImmutableList.builder();
+ final ImmutableList.Builder<ImmutableList<RexLiteral>> tupleList = ImmutableList.builder();
final RelDataType rowType;
if (targetRowType != null) {
rowType = targetRowType;
} else {
rowType =
SqlTypeUtil.promoteToRowType(
- typeFactory, validator.getValidatedNodeType(rowList), null);
+ typeFactory, validator().getValidatedNodeType(rowList), null);
}
final List<RelNode> unionInputs = new ArrayList<>();
@@ -1673,9 +1716,8 @@ public class SqlToRelConverter {
SqlBasicCall call;
if (isRowConstructor(node)) {
call = (SqlBasicCall) node;
- com.google.common.collect.ImmutableList.Builder<RexLiteral> tuple =
- com.google.common.collect.ImmutableList.builder();
- for (Ord<SqlNode> operand : Ord.zip(call.operands)) {
+ ImmutableList.Builder<RexLiteral> tuple = ImmutableList.builder();
+ for (Ord<@Nullable SqlNode> operand : Ord.zip(call.operands)) {
RexLiteral rexLiteral =
convertLiteralInValuesList(operand.e, bb, rowType, operand.i);
if ((rexLiteral == null) && allowLiteralsOnly) {
@@ -1695,7 +1737,7 @@ public class SqlToRelConverter {
} else {
RexLiteral rexLiteral = convertLiteralInValuesList(node, bb, rowType, 0);
if ((rexLiteral != null) && config.isCreateValuesRel()) {
- tupleList.add(com.google.common.collect.ImmutableList.of(rexLiteral));
+ tupleList.add(ImmutableList.of(rexLiteral));
continue;
} else {
if ((rexLiteral == null) && allowLiteralsOnly) {
@@ -1722,8 +1764,8 @@ public class SqlToRelConverter {
return resultRel;
}
- private RexLiteral convertLiteralInValuesList(
- SqlNode sqlNode, Blackboard bb, RelDataType rowType, int iField) {
+ private @Nullable RexLiteral convertLiteralInValuesList(
+ @Nullable SqlNode sqlNode, Blackboard bb, RelDataType rowType, int iField) {
if (!(sqlNode instanceof SqlLiteral)) {
return null;
}
@@ -1735,8 +1777,11 @@ public class SqlToRelConverter {
// don't use LogicalValues for those
return null;
}
+ return convertLiteral((SqlLiteral) sqlNode, bb, type);
+ }
- RexNode literalExpr = exprConverter.convertLiteral(bb, (SqlLiteral) sqlNode);
+ private RexLiteral convertLiteral(SqlLiteral sqlLiteral, Blackboard bb, RelDataType type) {
+ RexNode literalExpr = exprConverter.convertLiteral(bb, sqlLiteral);
if (!(literalExpr instanceof RexLiteral)) {
assert literalExpr.isA(SqlKind.CAST);
@@ -1770,7 +1815,7 @@ public class SqlToRelConverter {
return literal;
}
- private boolean isRowConstructor(SqlNode node) {
+ private static boolean isRowConstructor(SqlNode node) {
if (!(node.getKind() == SqlKind.ROW)) {
return false;
}
@@ -1814,6 +1859,8 @@ public class SqlToRelConverter {
case NOT:
logic = logic.negate();
break;
+ default:
+ break;
}
if (node instanceof SqlCall) {
switch (kind) {
@@ -1868,7 +1915,7 @@ public class SqlToRelConverter {
case ALL:
switch (logic) {
case TRUE_FALSE_UNKNOWN:
- RelDataType type = validator.getValidatedNodeTypeIfKnown(node);
+ RelDataType type = validator().getValidatedNodeTypeIfKnown(node);
if (type == null) {
// The node might not be validated if we still don't know type of the
// node.
@@ -1877,12 +1924,16 @@ public class SqlToRelConverter {
} else {
break;
}
- // fall through
case UNKNOWN_AS_FALSE:
logic = RelOptUtil.Logic.TRUE;
+ break;
+ default:
+ break;
}
bb.registerSubQuery(node, logic);
break;
+ default:
+ break;
}
}
@@ -1895,8 +1946,9 @@ public class SqlToRelConverter {
public RexNode convertExpression(SqlNode node) {
Map<String, RelDataType> nameToTypeMap = Collections.emptyMap();
final ParameterScope scope =
- new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap);
+ new ParameterScope((SqlValidatorImpl) validator(), nameToTypeMap);
final Blackboard bb = createBlackboard(scope, null, false);
+ replaceSubQueries(bb, node, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
return bb.convertExpression(node);
}
@@ -1915,8 +1967,9 @@ public class SqlToRelConverter {
nameToTypeMap.put(entry.getKey(), entry.getValue().getType());
}
final ParameterScope scope =
- new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap);
+ new ParameterScope((SqlValidatorImpl) validator(), nameToTypeMap);
final Blackboard bb = createBlackboard(scope, nameToNodeMap, false);
+ replaceSubQueries(bb, node, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
return bb.convertExpression(node);
}
@@ -1931,7 +1984,7 @@ public class SqlToRelConverter {
* @param bb Blackboard
* @return null to proceed with the usual expression translation process
*/
- protected RexNode convertExtendedExpression(SqlNode node, Blackboard bb) {
+ protected @Nullable RexNode convertExtendedExpression(SqlNode node, Blackboard bb) {
return null;
}
@@ -1945,10 +1998,13 @@ public class SqlToRelConverter {
// fall through
case RESPECT_NULLS:
aggCall = aggCall.operand(0);
+ break;
+ default:
+ break;
}
SqlNode windowOrRef = call.operand(1);
- final SqlWindow window = validator.resolveWindow(windowOrRef, bb.scope);
+ final SqlWindow window = validator().resolveWindow(windowOrRef, bb.scope());
SqlNode sqlLowerBound = window.getLowerBound();
SqlNode sqlUpperBound = window.getUpperBound();
@@ -1977,39 +2033,39 @@ public class SqlToRelConverter {
sqlLowerBound = SqlWindow.createCurrentRow(SqlParserPos.ZERO);
}
final SqlNodeList partitionList = window.getPartitionList();
- final com.google.common.collect.ImmutableList.Builder<RexNode> partitionKeys =
- com.google.common.collect.ImmutableList.builder();
+ final ImmutableList.Builder<RexNode> partitionKeys = ImmutableList.builder();
for (SqlNode partition : partitionList) {
partitionKeys.add(bb.convertExpression(partition));
}
- final RexNode lowerBound = bb.convertExpression(sqlLowerBound);
- final RexNode upperBound = bb.convertExpression(sqlUpperBound);
+ final RexNode lowerBound =
+ bb.convertExpression(requireNonNull(sqlLowerBound, "sqlLowerBound"));
+ final RexNode upperBound =
+ bb.convertExpression(requireNonNull(sqlUpperBound, "sqlUpperBound"));
if (orderList.size() == 0 && !rows) {
// A logical range requires an ORDER BY clause. Use the implicit
// ordering of this relation. There must be one, otherwise it would
// have failed validation.
- orderList = bb.scope.getOrderList();
+ orderList = bb.scope().getOrderList();
if (orderList == null) {
throw new AssertionError("Relation should have sort key for implicit ORDER BY");
}
}
- final com.google.common.collect.ImmutableList.Builder<RexFieldCollation> orderKeys =
- com.google.common.collect.ImmutableList.builder();
+ final ImmutableList.Builder<RexNode> orderKeys = ImmutableList.builder();
for (SqlNode order : orderList) {
orderKeys.add(
bb.convertSortExpression(
order,
RelFieldCollation.Direction.ASCENDING,
- RelFieldCollation.NullDirection.UNSPECIFIED));
+ RelFieldCollation.NullDirection.UNSPECIFIED,
+ bb::sortToRex));
}
try {
- com.google.common.base.Preconditions.checkArgument(
- bb.window == null, "already in window agg mode");
+ Preconditions.checkArgument(bb.window == null, "already in window agg mode");
bb.window = window;
RexNode rexAgg = exprConverter.convertCall(bb, aggCall);
- rexAgg = rexBuilder.ensureType(validator.getValidatedNodeType(call), rexAgg, false);
+ rexAgg = rexBuilder.ensureType(validator().getValidatedNodeType(call), rexAgg, false);
// Walk over the tree and apply 'over' to all agg functions. This is
// necessary because the returned expression is not necessarily a call
@@ -2022,9 +2078,9 @@ public class SqlToRelConverter {
new HistogramShuttle(
partitionKeys.build(),
orderKeys.build(),
+ rows,
RexWindowBounds.create(sqlLowerBound, lowerBound),
RexWindowBounds.create(sqlUpperBound, upperBound),
- rows,
window.isAllowPartial(),
isDistinct,
ignoreNulls);
@@ -2034,7 +2090,7 @@ public class SqlToRelConverter {
}
}
- protected void convertFrom(Blackboard bb, SqlNode from) {
+ protected void convertFrom(Blackboard bb, @Nullable SqlNode from) {
convertFrom(bb, from, Collections.emptyList());
}
@@ -2072,26 +2128,25 @@ public class SqlToRelConverter {
* <li>or any combination of the above.
* </ul>
*
- * @param fieldNames Field aliases, usually come from AS clause
+ * @param fieldNames Field aliases, usually come from AS clause, or null
*/
- protected void convertFrom(Blackboard bb, SqlNode from, List<String> fieldNames) {
+ protected void convertFrom(
+ Blackboard bb, @Nullable SqlNode from, @Nullable List<String> fieldNames) {
if (from == null) {
bb.setRoot(LogicalValues.createOneRow(cluster), false);
return;
}
final SqlCall call;
- final SqlNode[] operands;
+ final @Nullable SqlNode[] operands;
switch (from.getKind()) {
case AS:
call = (SqlCall) from;
SqlNode firstOperand = call.operand(0);
- final List<String> fieldNameList = new ArrayList<>();
- if (call.operandCount() > 2) {
- for (SqlNode node : Util.skip(call.getOperandList(), 2)) {
- fieldNameList.add(((SqlIdentifier) node).getSimple());
- }
- }
+ final List<String> fieldNameList =
+ call.operandCount() > 2
+ ? SqlIdentifier.simpleNames(Util.skip(call.getOperandList(), 2))
+ : null;
convertFrom(bb, firstOperand, fieldNameList);
// ----- FLINK MODIFICATION BEGIN -----
@@ -2133,6 +2188,10 @@ public class SqlToRelConverter {
convertPivot(bb, (SqlPivot) from);
return;
+ case UNPIVOT:
+ convertUnpivot(bb, (SqlUnpivot) from);
+ return;
+
case WITH_ITEM:
convertFrom(bb, ((SqlWithItem) from).query);
return;
@@ -2143,7 +2202,9 @@ public class SqlToRelConverter {
case TABLESAMPLE:
operands = ((SqlBasicCall) from).getOperands();
- SqlSampleSpec sampleSpec = SqlLiteral.sampleValue(operands[1]);
+ SqlSampleSpec sampleSpec =
+ SqlLiteral.sampleValue(
+ requireNonNull(operands[1], () -> "operand[1] of " + from));
if (sampleSpec instanceof SqlSampleSpec.SqlSubstitutionSampleSpec) {
String sampleName =
((SqlSampleSpec.SqlSubstitutionSampleSpec) sampleSpec).getName();
@@ -2160,7 +2221,7 @@ public class SqlToRelConverter {
tableSampleSpec.getSamplePercentage(),
tableSampleSpec.isRepeatable(),
tableSampleSpec.getRepeatableSeed());
- bb.setRoot(new Sample(cluster, bb.root, params), false);
+ bb.setRoot(new Sample(cluster, bb.root(), params), false);
} else {
throw new AssertionError("unknown TABLESAMPLE type: " + sampleSpec);
}
@@ -2204,8 +2265,8 @@ public class SqlToRelConverter {
case VALUES:
convertValuesImpl(bb, (SqlCall) from, null);
- if (fieldNames.size() > 0) {
- bb.setRoot(relBuilder.push(bb.root).rename(fieldNames).build(), true);
+ if (fieldNames != null) {
+ bb.setRoot(relBuilder.push(bb.root()).rename(fieldNames).build(), true);
}
return;
@@ -2227,7 +2288,7 @@ public class SqlToRelConverter {
}
}
- private void convertUnnest(Blackboard bb, SqlCall call, List<String> fieldNames) {
+ private void convertUnnest(Blackboard bb, SqlCall call, @Nullable List<String> fieldNames) {
... 3412 lines suppressed ...