This is an automated email from the ASF dual-hosted git repository.
zhenchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new b9e9e9d958 [CALCITE-7342] Quidem test support for
TopDownGeneralDecorrelator
b9e9e9d958 is described below
commit b9e9e9d958c6f5c4e6bff52796cdb6a4bfff8833
Author: Zhen Chen <[email protected]>
AuthorDate: Tue Dec 30 20:39:16 2025 +0800
[CALCITE-7342] Quidem test support for TopDownGeneralDecorrelator
---
.../calcite/config/CalciteConnectionConfig.java | 3 +
.../config/CalciteConnectionConfigImpl.java | 5 +
.../calcite/config/CalciteConnectionProperty.java | 3 +
.../apache/calcite/prepare/CalcitePrepareImpl.java | 10 ++
.../org/apache/calcite/prepare/PlannerImpl.java | 28 +++---
.../rel/rules/FilterProjectTransposeRule.java | 5 +-
.../apache/calcite/sql2rel/SqlToRelConverter.java | 11 +++
.../java/org/apache/calcite/tools/Programs.java | 25 ++++-
.../org/apache/calcite/test/CoreQuidemTest.java | 56 ++++++-----
.../org/apache/calcite/test/CoreQuidemTest2.java | 70 ++++++++++++++
core/src/test/resources/sql/blank.iq | 16 ++++
core/src/test/resources/sql/conditions.iq | 17 ++++
core/src/test/resources/sql/hep.iq | 13 +++
core/src/test/resources/sql/planner.iq | 11 +++
.../org/apache/calcite/test/MockDdlExecutor.java | 2 +
.../java/org/apache/calcite/test/QuidemTest.java | 104 ++++++++++++---------
16 files changed, 294 insertions(+), 85 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
index 3999a6ce9b..52c8d4cd56 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
@@ -110,6 +110,9 @@ public interface CalciteConnectionConfig extends
ConnectionConfig {
boolean lenientOperatorLookup();
/** Returns the value of {@link CalciteConnectionProperty#TOPDOWN_OPT}. */
boolean topDownOpt();
+ /** Returns the value of
+ * {@link CalciteConnectionProperty#TOPDOWN_GENERAL_DECORRELATION_ENABLED}.
*/
+ boolean topDownGeneralDecorrelationEnabled();
/** Returns the value of {@link
CalciteConnectionProperty#META_TABLE_FACTORY},
* or a default meta table factory if not set. If
diff --git
a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
index 7d9f9f76d1..221259cef9 100644
---
a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
+++
b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
@@ -215,6 +215,11 @@ public boolean isSet(CalciteConnectionProperty property) {
.getBoolean();
}
+ @Override public boolean topDownGeneralDecorrelationEnabled() {
+ return
CalciteConnectionProperty.TOPDOWN_GENERAL_DECORRELATION_ENABLED.wrap(properties)
+ .getBoolean();
+ }
+
@Override public <T> @PolyNull T metaTableFactory(
Class<T> metaTableFactoryClass,
@PolyNull T defaultMetaTableFactory) {
diff --git
a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
index a9c7627025..32079be2cf 100644
---
a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
+++
b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
@@ -154,6 +154,9 @@ public enum CalciteConnectionProperty implements
ConnectionProperty {
* If true (the default), Calcite de-correlates the plan. */
FORCE_DECORRELATE("forceDecorrelate", Type.BOOLEAN, true, false),
+ TOPDOWN_GENERAL_DECORRELATION_ENABLED("topDownGeneralDecorrelationEnabled",
+ Type.BOOLEAN, false, false),
+
/** Type system. The name of a class that implements
* {@link org.apache.calcite.rel.type.RelDataTypeSystem} and has a public
* default constructor or an {@code INSTANCE} constant. */
diff --git
a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 6bb21e1ae2..8439fabd5f 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -107,8 +107,10 @@
import org.apache.calcite.sql2rel.SqlRexConvertletTable;
import org.apache.calcite.sql2rel.SqlToRelConverter;
import org.apache.calcite.sql2rel.StandardConvertletTable;
+import org.apache.calcite.sql2rel.TopDownGeneralDecorrelator;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Frameworks;
+import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
@@ -1091,6 +1093,9 @@ private PreparedResult prepare_(Supplier<RelNode> fn,
SqlValidator validator,
CatalogReader catalogReader,
SqlToRelConverter.Config config) {
+ config =
+ config.withTopDownGeneralDecorrelationEnabled(
+ context.config().topDownGeneralDecorrelationEnabled());
return new SqlToRelConverter(this, validator, catalogReader, cluster,
convertletTable, config);
}
@@ -1107,6 +1112,11 @@ private PreparedResult prepare_(Supplier<RelNode> fn,
@Override protected RelNode decorrelate(SqlToRelConverter
sqlToRelConverter,
SqlNode query, RelNode rootRel) {
+ if (context.config().topDownGeneralDecorrelationEnabled()) {
+ final RelBuilder relBuilder =
+
sqlToRelConverter.config().getRelBuilderFactory().create(rootRel.getCluster(),
null);
+ return TopDownGeneralDecorrelator.decorrelateQuery(rootRel,
relBuilder);
+ }
return sqlToRelConverter.decorrelate(query, rootRel);
}
diff --git a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
index ff2903047d..5fece8fa97 100644
--- a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
@@ -51,6 +51,7 @@
import org.apache.calcite.sql2rel.RelDecorrelator;
import org.apache.calcite.sql2rel.SqlRexConvertletTable;
import org.apache.calcite.sql2rel.SqlToRelConverter;
+import org.apache.calcite.sql2rel.TopDownGeneralDecorrelator;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Planner;
import org.apache.calcite.tools.Program;
@@ -262,8 +263,10 @@ private void ready() {
final RelOptCluster cluster =
RelOptCluster.create(requireNonNull(planner, "planner"),
rexBuilder);
- final SqlToRelConverter.Config config =
- sqlToRelConverterConfig.withTrimUnusedFields(false);
+ final SqlToRelConverter.Config config = sqlToRelConverterConfig
+ .withTrimUnusedFields(false)
+ .withTopDownGeneralDecorrelationEnabled(
+ connectionConfig.topDownGeneralDecorrelationEnabled());
final SqlToRelConverter sqlToRelConverter =
new SqlToRelConverter(this, validator,
createCatalogReader(), cluster, convertletTable, config);
@@ -272,8 +275,9 @@ private void ready() {
root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true));
final RelBuilder relBuilder =
config.getRelBuilderFactory().create(cluster, null);
- root =
- root.withRel(RelDecorrelator.decorrelateQuery(root.rel, relBuilder));
+ root = config.isTopDownGeneralDecorrelationEnabled()
+ ? root.withRel(TopDownGeneralDecorrelator.decorrelateQuery(root.rel,
relBuilder))
+ : root.withRel(RelDecorrelator.decorrelateQuery(root.rel, relBuilder));
state = State.STATE_5_CONVERTED;
return root;
}
@@ -314,20 +318,22 @@ public class ViewExpanderImpl implements ViewExpander {
final RexBuilder rexBuilder = createRexBuilder();
final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder);
- final SqlToRelConverter.Config config =
- sqlToRelConverterConfig.withTrimUnusedFields(false);
+ final SqlToRelConverter.Config config = sqlToRelConverterConfig
+ .withTrimUnusedFields(false)
+ .withTopDownGeneralDecorrelationEnabled(
+ connectionConfig.topDownGeneralDecorrelationEnabled());
final SqlToRelConverter sqlToRelConverter =
new SqlToRelConverter(this, validator,
catalogReader, cluster, convertletTable, config);
- final RelRoot root =
+ RelRoot root =
sqlToRelConverter.convertQuery(sqlNode, true, false);
- final RelRoot root2 =
- root.withRel(sqlToRelConverter.flattenTypes(root.rel, true));
+ root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true));
final RelBuilder relBuilder =
config.getRelBuilderFactory().create(cluster, null);
- return root2.withRel(
- RelDecorrelator.decorrelateQuery(root.rel, relBuilder));
+ return config.isTopDownGeneralDecorrelationEnabled()
+ ? root.withRel(TopDownGeneralDecorrelator.decorrelateQuery(root.rel,
relBuilder))
+ : root.withRel(RelDecorrelator.decorrelateQuery(root.rel, relBuilder));
}
// CalciteCatalogReader is stateless; no need to store one
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
index 726c07bb14..3e3cf46a11 100644
---
a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
+++
b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
@@ -175,11 +175,10 @@ protected FilterProjectTransposeRule(
final RelNode input = project.getInput();
final RelTraitSet traitSet = filter.getTraitSet()
.replaceIfs(RelCollationTraitDef.INSTANCE,
- () -> Collections.singletonList(
-
input.getTraitSet().getTrait(RelCollationTraitDef.INSTANCE)))
+ () ->
input.getTraitSet().getTraits(RelCollationTraitDef.INSTANCE))
.replaceIfs(RelDistributionTraitDef.INSTANCE,
() -> Collections.singletonList(
-
input.getTraitSet().getTrait(RelDistributionTraitDef.INSTANCE)));
+
input.getTraitSet().getTrait(RelDistributionTraitDef.INSTANCE)));
newCondition =
RexUtil.removeNullabilityCast(relBuilder.getTypeFactory(), newCondition);
newFilterRel = filter.copy(traitSet, input, newCondition);
} else {
diff --git
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index da96b98b06..dd97b2a8fd 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -3966,6 +3966,9 @@ protected boolean enableDecorrelation() {
}
protected RelNode decorrelateQuery(RelNode rootRel) {
+ if (config.isTopDownGeneralDecorrelationEnabled()) {
+ return TopDownGeneralDecorrelator.decorrelateQuery(rootRel, relBuilder);
+ }
return RelDecorrelator.decorrelateQuery(rootRel, relBuilder);
}
@@ -6496,6 +6499,14 @@ public interface Config {
/** Sets {@link #isDecorrelationEnabled()}. */
Config withDecorrelationEnabled(boolean decorrelationEnabled);
+ /** Returns whether to use the top-down general decorrelator. */
+ @Value.Default default boolean isTopDownGeneralDecorrelationEnabled() {
+ return false;
+ }
+
+ /** Sets {@link #isTopDownGeneralDecorrelationEnabled()}. */
+ Config withTopDownGeneralDecorrelationEnabled(boolean
topDownGeneralDecorrelationEnabled);
+
/** Returns the {@code trimUnusedFields} option. Controls whether to trim
* unused fields as part of the conversion process. */
@Value.Default default boolean isTrimUnusedFields() {
diff --git a/core/src/main/java/org/apache/calcite/tools/Programs.java
b/core/src/main/java/org/apache/calcite/tools/Programs.java
index d473e54f4f..4974db3c20 100644
--- a/core/src/main/java/org/apache/calcite/tools/Programs.java
+++ b/core/src/main/java/org/apache/calcite/tools/Programs.java
@@ -46,6 +46,7 @@
import org.apache.calcite.sql2rel.RelDecorrelator;
import org.apache.calcite.sql2rel.RelFieldTrimmer;
import org.apache.calcite.sql2rel.SqlToRelConverter;
+import org.apache.calcite.sql2rel.TopDownGeneralDecorrelator;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
@@ -259,7 +260,26 @@ public static Program subQuery(RelMetadataProvider
metadataProvider) {
CoreRules.PROJECT_SUB_QUERY_TO_CORRELATE,
CoreRules.JOIN_SUB_QUERY_TO_CORRELATE,
CoreRules.PROJECT_OVER_SUM_TO_SUM0_RULE));
- return of(builder.build(), true, metadataProvider);
+ final Program oldProgram = of(builder.build(), true, metadataProvider);
+
+ final HepProgramBuilder newBuilder = HepProgram.builder();
+ newBuilder.addRuleCollection(
+ ImmutableList.of(CoreRules.FILTER_SUB_QUERY_TO_MARK_CORRELATE,
+ CoreRules.PROJECT_SUB_QUERY_TO_MARK_CORRELATE,
+ CoreRules.JOIN_SUB_QUERY_TO_CORRELATE,
+ CoreRules.PROJECT_OVER_SUM_TO_SUM0_RULE));
+ final Program newProgram = of(newBuilder.build(), true, metadataProvider);
+
+ return (planner, rel, requiredOutputTraits, materializations, lattices) ->
{
+ final CalciteConnectionConfig config =
+ planner.getContext().maybeUnwrap(CalciteConnectionConfig.class)
+ .orElse(CalciteConnectionConfig.DEFAULT);
+ final Program program = config.topDownGeneralDecorrelationEnabled()
+ ? newProgram
+ : oldProgram;
+ return program.run(planner, rel, requiredOutputTraits, materializations,
+ lattices);
+ };
}
public static Program measure(RelMetadataProvider metadataProvider) {
@@ -430,6 +450,9 @@ private static class DecorrelateProgram implements Program {
if (config.forceDecorrelate()) {
final RelBuilder relBuilder =
RelFactories.LOGICAL_BUILDER.create(rel.getCluster(), null);
+ if (config.topDownGeneralDecorrelationEnabled()) {
+ return TopDownGeneralDecorrelator.decorrelateQuery(rel, relBuilder);
+ }
return RelDecorrelator.decorrelateQuery(rel, relBuilder);
}
return rel;
diff --git a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
index 1aa6ab1871..b878bfb085 100644
--- a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
+++ b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
@@ -41,7 +41,7 @@
/**
* Test that runs every Quidem file in the "core" module as a test.
*/
-class CoreQuidemTest extends QuidemTest {
+public class CoreQuidemTest extends QuidemTest {
/** Runs a test from the command line.
*
* <p>For example:
@@ -57,6 +57,12 @@ public static void main(String[] args) throws Exception {
/** For {@link QuidemTest#test(String)} parameters. */
@Override public Collection<String> getPath() {
+ return data();
+ }
+
+ /** Returns the list of Quidem files to run.
+ * Subclasses can override this method to gradually add files. */
+ protected Collection<String> data() {
// Start with a test file we know exists, then find the directory and list
// its files.
final String first = "sql/agg.iq";
@@ -68,31 +74,31 @@ public static void main(String[] args) throws Exception {
@Override public Connection connect(String name, boolean reference)
throws Exception {
switch (name) {
case "blank":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
- .with(CalciteAssert.SchemaSpec.BLANK)
+ .with(CalciteAssert.SchemaSpec.BLANK))
.connect();
case "scott":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-spark":
discard(CustomTypeSystems.SPARK_TYPE_SYSTEM);
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
.with(CalciteConnectionProperty.TYPE_SYSTEM,
CustomTypeSystems.class.getName() + "#SPARK_TYPE_SYSTEM")
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-checked-rounding-half-up":
discard(CustomTypeSystems.ROUNDING_MODE_HALF_UP);
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
// Use bigquery conformance, which forces checked arithmetic
@@ -100,84 +106,84 @@ public static void main(String[] args) throws Exception {
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
.with(CalciteConnectionProperty.TYPE_SYSTEM,
CustomTypeSystems.class.getName() + "#ROUNDING_MODE_HALF_UP")
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-negative-scale":
discard(CustomTypeSystems.NEGATIVE_SCALE);
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
.with(CalciteConnectionProperty.TYPE_SYSTEM,
CustomTypeSystems.class.getName() + "#NEGATIVE_SCALE")
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-negative-scale-rounding-half-up":
discard(CustomTypeSystems.NEGATIVE_SCALE_ROUNDING_MODE_HALF_UP);
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
.with(CalciteConnectionProperty.TYPE_SYSTEM,
CustomTypeSystems.class.getName()
+ "#NEGATIVE_SCALE_ROUNDING_MODE_HALF_UP")
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-lenient":
// Same as "scott", but uses LENIENT conformance.
// TODO: add a way to change conformance without defining a new
// connection
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.CONFORMANCE,
SqlConformanceEnum.LENIENT)
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-babel":
// Same as "scott", but uses BABEL conformance.
// connection
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.CONFORMANCE,
SqlConformanceEnum.BABEL)
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-mysql":
// Same as "scott", but uses MySQL conformance.
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.CONFORMANCE,
SqlConformanceEnum.MYSQL_5)
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-oracle":
// Same as "scott", but uses Oracle conformance.
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.CONFORMANCE,
SqlConformanceEnum.ORACLE_10)
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "scott-mssql":
// Same as "scott", but uses SQL_SERVER_2008 conformance.
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.CONFORMANCE,
SqlConformanceEnum.SQL_SERVER_2008)
- .with(CalciteAssert.Config.SCOTT)
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "steelwheels":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
.with(CalciteAssert.SchemaSpec.STEELWHEELS)
- .with(Lex.BIG_QUERY)
+ .with(Lex.BIG_QUERY))
.connect();
default:
return super.connect(name, reference);
diff --git a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest2.java
b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest2.java
new file mode 100644
index 0000000000..406bd3bc8f
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest2.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.config.CalciteConnectionProperty;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Test that runs Quidem files with the top-down decorrelator enabled.
+ */
+public class CoreQuidemTest2 extends CoreQuidemTest {
+ /** Runs a test from the command line.
+ *
+ * <p>For example:
+ *
+ * <blockquote>
+ * <code>java CoreQuidemTest2 sql/dummy.iq</code>
+ * </blockquote> */
+ public static void main(String[] args) throws Exception {
+ for (String arg : args) {
+ new CoreQuidemTest2().test(arg);
+ }
+ }
+
+ @Override protected Collection<String> data() {
+ final List<String> paths = new ArrayList<>(super.data());
+ // These remove operations are temporary and will be deleted
+ // once the new decorrelator can adapt to all scenarios.
+
+ // TODO: The following files involves UNNEST and LEFT_MARK JOIN
+ paths.remove("sql/agg.iq");
+ paths.remove("sql/measure.iq");
+ paths.remove("sql/unnest.iq");
+ paths.remove("sql/lateral.iq");
+ paths.remove("sql/some.iq");
+ paths.remove("sql/sub-query.iq");
+ paths.remove("sql/scalar.iq");
+ paths.remove("sql/join.iq");
+ paths.remove("sql/spatial.iq");
+ paths.remove("sql/measure-paper.iq");
+ paths.remove("sql/misc.iq");
+ return paths;
+ }
+
+ @Override protected CalciteAssert.AssertThat
customize(CalciteAssert.AssertThat assertThat) {
+ return super.customize(assertThat)
+ .with(CalciteConnectionProperty.TOPDOWN_GENERAL_DECORRELATION_ENABLED,
true);
+ }
+
+ @Override protected boolean useTopDownGeneralDecorrelator() {
+ return true;
+ }
+}
diff --git a/core/src/test/resources/sql/blank.iq
b/core/src/test/resources/sql/blank.iq
index c44c39530a..4053cbb26b 100644
--- a/core/src/test/resources/sql/blank.iq
+++ b/core/src/test/resources/sql/blank.iq
@@ -89,6 +89,7 @@ insert into table2 values (NULL, 1), (2, 1);
# Checked on Oracle
!set lateDecorrelate true
select i, j from table1 where table1.j NOT IN (select i from table2 where
table1.i=table2.j);
+!if (use_old_decorr) {
EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0], expr#9=[=($t3, $t8)],
expr#10=[IS NULL($t1)], expr#11=[IS NOT NULL($t7)], expr#12=[<($t4, $t3)],
expr#13=[OR($t10, $t11, $t12)], expr#14=[IS NOT TRUE($t13)], expr#15=[OR($t9,
$t14)], proj#0..1=[{exprs}], $condition=[$t15])
EnumerableMergeJoin(condition=[AND(=($0, $6), =($1, $5))], joinType=[left])
EnumerableSort(sort0=[$0], sort1=[$1], dir0=[ASC], dir1=[ASC])
@@ -113,7 +114,15 @@ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0],
expr#9=[=($t3, $t8)], expr#10=[
(0 rows)
!ok
+!}
+# TODO: This error needs to be fixed
+!if (use_new_decorr) {
+Unable to convert LEFT_MARK to Linq4j JoinType
+!error
+!}
+
+!if (use_old_decorr) {
select * from table1 where j not in (select i from table2);
+---+---+
| I | J |
@@ -153,6 +162,13 @@ select * from table1 where j not in (select i from table2)
or j = 3;
(1 row)
!ok
+!}
+
+# TODO: This error needs to be fixed
+!if (use_new_decorr) {
+Unable to convert LEFT_MARK to Linq4j JoinType
+!error
+!}
# [CALCITE-4813] ANY_VALUE assumes that arguments should be comparable
select any_value(r) over(), s from(select array[f, s] r, s from (select 1 as
f, 2 as s) t) t;
diff --git a/core/src/test/resources/sql/conditions.iq
b/core/src/test/resources/sql/conditions.iq
index 5fa94d0889..7ce0c78c9c 100644
--- a/core/src/test/resources/sql/conditions.iq
+++ b/core/src/test/resources/sql/conditions.iq
@@ -418,6 +418,7 @@ where empno = 7369;
!ok
+!if (use_old_decorr) {
EnumerableCalc(expr#0..2=[{inputs}], EMPNO=[$t0])
EnumerableNestedLoopJoin(condition=[true], joinType=[left])
EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t0):INTEGER NOT NULL],
expr#9=[7369], expr#10=[=($t8, $t9)], EMPNO=[$t0], $condition=[$t10])
@@ -430,6 +431,22 @@ EnumerableCalc(expr#0..2=[{inputs}], EMPNO=[$t0])
EnumerableAggregate(group=[{}], agg#0=[COUNT()])
EnumerableTableScan(table=[[scott, EMP]])
!plan
+!}
+
+!if (use_new_decorr) {
+EnumerableCalc(expr#0..1=[{inputs}], EMPNO=[$t0])
+ EnumerableNestedLoopJoin(condition=[true], joinType=[left])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t0):INTEGER NOT NULL],
expr#9=[7369], expr#10=[=($t8, $t9)], EMPNO=[$t0], $condition=[$t10])
+ EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableCalc(expr#0..1=[{inputs}], expr#2=[0], DUMMY=[$t2])
+ EnumerableNestedLoopJoin(condition=[true], joinType=[inner])
+ EnumerableCalc(expr#0..2=[{inputs}], DEPTNO=[$t0])
+ EnumerableTableScan(table=[[scott, DEPT]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[0:BIGINT], expr#2=[>($t0,
$t1)], $f0=[$t0], $condition=[$t2])
+ EnumerableAggregate(group=[{}], agg#0=[COUNT()])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+!}
# sub-query return true with Equal condition
select r.empno, s.deptno
diff --git a/core/src/test/resources/sql/hep.iq
b/core/src/test/resources/sql/hep.iq
index baa57592fa..556d10f721 100644
--- a/core/src/test/resources/sql/hep.iq
+++ b/core/src/test/resources/sql/hep.iq
@@ -141,6 +141,8 @@ WHERE e1.mgr > 12
(5 rows)
!ok
+
+!if (use_old_decorr) {
EnumerableCalc(expr#0..3=[{inputs}], MGR=[$t1], COMM=[$t2])
EnumerableHashJoin(condition=[=($1, $3)], joinType=[inner])
EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t3):INTEGER],
expr#9=[12], expr#10=[>($t8, $t9)], EMPNO=[$t0], MGR=[$t3], COMM=[$t6],
$condition=[$t10])
@@ -149,6 +151,17 @@ EnumerableCalc(expr#0..3=[{inputs}], MGR=[$t1], COMM=[$t2])
EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t6):DECIMAL(12, 2)],
expr#9=[5.00:DECIMAL(12, 2)], expr#10=[>($t8, $t9)], expr#11=[IS NOT
NULL($t3)], expr#12=[AND($t10, $t11)], MGR=[$t3], $condition=[$t12])
EnumerableTableScan(table=[[scott, EMP]])
!plan
+!}
+
+!if (use_new_decorr) {
+EnumerableCalc(expr#0..2=[{inputs}], MGR=[$t1], COMM=[$t2])
+ EnumerableHashJoin(condition=[IS NOT DISTINCT FROM($1, $4)], joinType=[semi])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t3):INTEGER],
expr#9=[12], expr#10=[>($t8, $t9)], EMPNO=[$t0], MGR=[$t3], COMM=[$t6],
$condition=[$t10])
+ EnumerableTableScan(table=[[scott, EMP]])
+ EnumerableCalc(expr#0..7=[{inputs}], expr#8=[CAST($t6):DECIMAL(12, 2)],
expr#9=[5.00:DECIMAL(12, 2)], expr#10=[>($t8, $t9)], expr#11=[IS NOT
NULL($t3)], expr#12=[AND($t10, $t11)], EMPNO=[$t0], MGR=[$t3], COMM=[$t6],
$condition=[$t12])
+ EnumerableTableScan(table=[[scott, EMP]])
+!plan
+!}
!set hep-rules original
# End hep.iq
diff --git a/core/src/test/resources/sql/planner.iq
b/core/src/test/resources/sql/planner.iq
index 0461cc644b..1147a534ef 100644
--- a/core/src/test/resources/sql/planner.iq
+++ b/core/src/test/resources/sql/planner.iq
@@ -359,6 +359,7 @@ or
!ok
+!if (use_old_decorr) {
EnumerableHashJoin(condition=[AND(=($0, $6), OR(AND(>($1, 11), <=($7, 32)),
AND(<($5, 255), >=($8, 344))))], joinType=[inner])
EnumerableMergeJoin(condition=[AND(=($0, $3), OR(>($1, 11), <($5, 255)))],
joinType=[inner])
EnumerableValues(tuples=[[{ 1, 11, 111 }, { 2, 12, 122 }, { 3, 13, 133 },
{ 4, 14, 144 }, { 5, 15, 155 }]])
@@ -366,6 +367,16 @@ EnumerableHashJoin(condition=[AND(=($0, $6), OR(AND(>($1,
11), <=($7, 32)), AND(
EnumerableCalc(expr#0..2=[{inputs}], expr#3=[32], expr#4=[<=($t1, $t3)],
expr#5=[344], expr#6=[>=($t2, $t5)], expr#7=[OR($t4, $t6)],
proj#0..2=[{exprs}], $condition=[$t7])
EnumerableValues(tuples=[[{ 1, 31, 311 }, { 2, 32, 322 }, { 3, 33, 333 },
{ 4, 34, 344 }, { 5, 35, 355 }]])
!plan
+!}
+
+!if (use_new_decorr) {
+EnumerableMergeJoin(condition=[AND(=($0, $6), OR(AND(>($1, 11), <=($7, 32)),
AND(<($5, 255), >=($8, 344))))], joinType=[inner])
+ EnumerableMergeJoin(condition=[=($0, $3)], joinType=[inner])
+ EnumerableValues(tuples=[[{ 1, 11, 111 }, { 2, 12, 122 }, { 3, 13, 133 },
{ 4, 14, 144 }, { 5, 15, 155 }]])
+ EnumerableValues(tuples=[[{ 1, 21, 211 }, { 2, 22, 222 }, { 3, 23, 233 },
{ 4, 24, 244 }, { 5, 25, 255 }]])
+ EnumerableValues(tuples=[[{ 1, 31, 311 }, { 2, 32, 322 }, { 3, 33, 333 }, {
4, 34, 344 }, { 5, 35, 355 }]])
+!plan
+!}
!set planner-rules original
# [CALCITE-7086] Implement a rule that performs the inverse operation of
AggregateCaseToFilterRule
diff --git a/testkit/src/main/java/org/apache/calcite/test/MockDdlExecutor.java
b/testkit/src/main/java/org/apache/calcite/test/MockDdlExecutor.java
index c12618e13a..8d744d829b 100644
--- a/testkit/src/main/java/org/apache/calcite/test/MockDdlExecutor.java
+++ b/testkit/src/main/java/org/apache/calcite/test/MockDdlExecutor.java
@@ -25,6 +25,7 @@
import org.apache.calcite.linq4j.QueryProvider;
import org.apache.calcite.linq4j.Queryable;
import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.plan.Contexts;
import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -207,6 +208,7 @@ protected static void populate(SqlIdentifier name, SqlNode
query,
requireNonNull(
Schemas.subSchema(context.getRootSchema(),
context.getDefaultSchemaPath())).plus())
+ .context(Contexts.of(context.config()))
.build();
final Planner planner = Frameworks.getPlanner(config);
try {
diff --git a/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
b/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
index bc0b94f14f..67b43ff462 100644
--- a/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/QuidemTest.java
@@ -145,10 +145,18 @@ public static class ExplainValidatedCommand extends
AbstractCommand {
private static final Pattern PATTERN = Pattern.compile("\\.iq$");
// Saved original planner rules
- private static @Nullable List<RelOptRule> originalRules;
+ private @Nullable List<RelOptRule> originalRules;
- private static @Nullable Object getEnv(String varName) {
+ protected boolean useTopDownGeneralDecorrelator() {
+ return false;
+ }
+
+ private @Nullable Object getEnv(String varName) {
switch (varName) {
+ case "use_old_decorr":
+ return !useTopDownGeneralDecorrelator();
+ case "use_new_decorr":
+ return useTopDownGeneralDecorrelator();
case "jdk18":
return System.getProperty("java.version").startsWith("1.8");
case "fixed":
@@ -200,7 +208,7 @@ protected static Collection<String> data(String first) {
final List<String> paths = new ArrayList<>();
final FilenameFilter filter = new PatternFilenameFilter(".*\\.iq$");
for (File f : Util.first(dir.listFiles(filter), new File[0])) {
- paths.add(f.getAbsolutePath().substring(commonPrefixLength));
+ paths.add(n2u(f.getAbsolutePath().substring(commonPrefixLength)));
}
return paths;
}
@@ -221,7 +229,8 @@ protected void checkRun(String path) throws Exception {
// outFile = "/home/fred/calcite/core/build/quidem/test/sql/agg.iq"
final URL inUrl = QuidemTest.class.getResource("/" + n2u(path));
inFile = Sources.of(requireNonNull(inUrl, "inUrl")).file();
- outFile = replaceDir(inFile, "resources", "quidem");
+ outFile = replaceDir(inFile, "resources", "quidem/"
+ + getClass().getSimpleName());
}
Util.discard(outFile.getParentFile().mkdirs());
try (Reader reader = Util.reader(inFile);
@@ -266,7 +275,7 @@ protected void checkRun(String path) throws Exception {
// - Reset defaults: "original"
if (propertyName.equals("planner-rules")) {
if (value.equals("original")) {
- closer.add(Hook.PLANNER.addThread(QuidemTest::resetPlanner));
+ closer.add(Hook.PLANNER.addThread(this::resetPlanner));
} else {
closer.add(
Hook.PLANNER.addThread((Consumer<RelOptPlanner>)
@@ -314,7 +323,7 @@ protected void checkRun(String path) throws Exception {
}
}
})
- .withEnv(QuidemTest::getEnv)
+ .withEnv(this::getEnv)
.build();
new Quidem(config).execute();
}
@@ -337,7 +346,7 @@ private static void updatePlanner(RelOptPlanner planner,
String value) {
rulesAdd.forEach(planner::addRule);
}
- private static void resetPlanner(RelOptPlanner planner) {
+ private void resetPlanner(RelOptPlanner planner) {
if (originalRules != null) {
planner.getRules().forEach(planner::removeRule);
originalRules.forEach(planner::addRule);
@@ -457,6 +466,11 @@ private static File replaceDir(File file, String target,
String replacement) {
n2u('/' + replacement + '/')));
}
+ /** Allows subclasses to customize the connection. */
+ protected CalciteAssert.AssertThat customize(CalciteAssert.AssertThat
assertThat) {
+ return assertThat;
+ }
+
/** Creates a command handler. */
protected CommandHandler createCommandHandler() {
return Quidem.EMPTY_COMMAND_HANDLER;
@@ -501,7 +515,7 @@ public void test(String path) throws Exception {
protected abstract Collection<String> getPath();
/** Quidem connection factory for Calcite's built-in test schemas. */
- protected static class QuidemConnectionFactory
+ protected class QuidemConnectionFactory
implements Quidem.ConnectionFactory {
public Connection connect(String name) throws Exception {
return connect(name, false);
@@ -523,89 +537,89 @@ public Connection connect(String name) throws Exception {
}
switch (name) {
case "hr":
- return CalciteAssert.hr()
+ return customize(CalciteAssert.hr())
.connect();
case "aux":
- return CalciteAssert.hr()
- .with(CalciteAssert.Config.AUX)
+ return customize(CalciteAssert.hr()
+ .with(CalciteAssert.Config.AUX))
.connect();
case "foodmart":
- return CalciteAssert.that()
- .with(CalciteAssert.Config.FOODMART_CLONE)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.Config.FOODMART_CLONE))
.connect();
case "geo":
- return CalciteAssert.that()
- .with(CalciteAssert.Config.GEO)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.Config.GEO))
.connect();
case "scott":
- return CalciteAssert.that()
- .with(CalciteAssert.Config.SCOTT)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.Config.SCOTT))
.connect();
case "jdbc_scott":
- return CalciteAssert.that()
- .with(CalciteAssert.Config.JDBC_SCOTT)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.Config.JDBC_SCOTT))
.connect();
case "steelwheels":
- return CalciteAssert.that()
- .with(CalciteAssert.SchemaSpec.STEELWHEELS)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.SchemaSpec.STEELWHEELS))
.connect();
case "jdbc_steelwheels":
- return CalciteAssert.that()
- .with(CalciteAssert.SchemaSpec.JDBC_STEELWHEELS)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.SchemaSpec.JDBC_STEELWHEELS))
.connect();
case "post":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteAssert.Config.REGULAR)
- .with(CalciteAssert.SchemaSpec.POST)
+ .with(CalciteAssert.SchemaSpec.POST))
.connect();
case "post-postgresql":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.FUN, "standard,postgresql")
.with(CalciteAssert.Config.REGULAR)
- .with(CalciteAssert.SchemaSpec.POST)
+ .with(CalciteAssert.SchemaSpec.POST))
.connect();
case "post-big-query":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.FUN, "standard,bigquery")
.with(CalciteAssert.Config.REGULAR)
- .with(CalciteAssert.SchemaSpec.POST)
+ .with(CalciteAssert.SchemaSpec.POST))
.connect();
case "mysqlfunc":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.FUN, "mysql")
.with(CalciteAssert.Config.REGULAR)
- .with(CalciteAssert.SchemaSpec.POST)
+ .with(CalciteAssert.SchemaSpec.POST))
.connect();
case "sparkfunc":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.FUN, "spark")
.with(CalciteAssert.Config.REGULAR)
- .with(CalciteAssert.SchemaSpec.POST)
+ .with(CalciteAssert.SchemaSpec.POST))
.connect();
case "oraclefunc":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.FUN, "oracle")
- .with(CalciteAssert.Config.REGULAR)
+ .with(CalciteAssert.Config.REGULAR))
.connect();
case "mssqlfunc":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.FUN, "mssql")
- .with(CalciteAssert.Config.REGULAR)
+ .with(CalciteAssert.Config.REGULAR))
.connect();
case "catchall":
- return CalciteAssert.that()
+ return customize(CalciteAssert.that()
.with(CalciteConnectionProperty.TIME_ZONE, "UTC")
.withSchema("s",
new ReflectiveSchemaWithoutRowCount(
- new CatchallSchema()))
+ new CatchallSchema())))
.connect();
case "orinoco":
- return CalciteAssert.that()
- .with(CalciteAssert.SchemaSpec.ORINOCO)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.SchemaSpec.ORINOCO))
.connect();
case "seq":
- final Connection connection = CalciteAssert.that()
- .withSchema("s", new AbstractSchema())
+ final Connection connection = customize(CalciteAssert.that()
+ .withSchema("s", new AbstractSchema()))
.connect();
connection.unwrap(CalciteConnection.class).getRootSchema()
.subSchemas().get("s")
@@ -623,8 +637,8 @@ public Connection connect(String name) throws Exception {
});
return connection;
case "bookstore":
- return CalciteAssert.that()
- .with(CalciteAssert.SchemaSpec.BOOKSTORE)
+ return customize(CalciteAssert.that()
+ .with(CalciteAssert.SchemaSpec.BOOKSTORE))
.connect();
default:
throw new RuntimeException("unknown connection '" + name + "'");