Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}

Expand Down
28 changes: 17 additions & 11 deletions core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this change related to this issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm not entirely sure why; I made some adjustments based on the error message. I will revert the changes to RelDistribution later.

> Error while executing command ExplainCommand [sql: with t (a, b) as (select * from (values (60, 'b')))
> select * from t where a in (select deptno from "scott".dept)]
> java.sql.SQLException: Error while executing SQL "explain plan for with t (a, b) as (select * from (values (60, 'b')))
> select * from t where a in (select deptno from "scott".dept)": class org.apache.calcite.plan.RelCompositeTrait cannot be cast to class org.apache.calcite.rel.RelCollation (org.apache.calcite.plan.RelCompositeTrait and org.apache.calcite.rel.RelCollation are in unnamed module of loader 'app')

.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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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() {
Expand Down
25 changes: 24 additions & 1 deletion core/src/main/java/org/apache/calcite/tools/Programs.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these cover all subquery cases?
I am not sure why the SUM rule is here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only found this one instance of logic for handling subqueries; there's nothing else. I'm also a bit confused about why it handles SUM, but since this is code from 2024, I've decided to keep it here for now, we can see CALCITE-6020.

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) {
Expand Down Expand Up @@ -425,6 +445,9 @@ private static class DecorrelateProgram implements Program {
if (config.forceDecorrelate()) {
final RelBuilder relBuilder =
RelFactories.LOGICAL_BUILDER.create(rel.getCluster(), null);
if (config.topDownGeneralDecorrelationEnabled()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we also need to adapt the Program of removing subquery. When using the new decorrelator, We recommend using CoreRules.FILTER_SUB_QUERY_TO_MARK_CORRELATE and CoreRules.PROJECT_SUB_QUERY_TO_MARK_CORRELATE to remove subqueries from Filter and Project.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I will fix it later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After replacing these two rules, left_mark join will be generated in agg.iq, so I removed this test for now.

return TopDownGeneralDecorrelator.decorrelateQuery(rel, relBuilder);
}
return RelDecorrelator.decorrelateQuery(rel, relBuilder);
}
return rel;
Expand Down
56 changes: 31 additions & 25 deletions core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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";
Expand All @@ -68,116 +74,116 @@ 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
.with(CalciteConnectionProperty.CONFORMANCE, SqlConformanceEnum.BIG_QUERY)
.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);
Expand Down
Loading
Loading