diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rel/PartialDruidQuery.java b/sql/src/main/java/org/apache/druid/sql/calcite/rel/PartialDruidQuery.java index 727e448b17a2..068ff49308dd 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rel/PartialDruidQuery.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rel/PartialDruidQuery.java @@ -78,7 +78,14 @@ public enum Stage // WHERE_FILTER, SELECT_PROJECT may be present on any query, except ones with WINDOW. WHERE_FILTER, - SELECT_PROJECT, + SELECT_PROJECT { + @Override + public boolean canFollow(Stage stage) + { + // SELECT_PROJECT can be stacked on top of another SELECT_PROJECT. + return stage.compareTo(this) <= 0; + } + }, // AGGREGATE, HAVING_FILTER, AGGREGATE_PROJECT can be present on non-WINDOW aggregating queries. AGGREGATE, diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidRules.java b/sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidRules.java index 52841fd99a01..b5e91916cfae 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidRules.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/rule/DruidRules.java @@ -313,9 +313,31 @@ public DruidOuterQueryRule( @Override public boolean matches(final RelOptRuleCall call) { - // Only consider doing a subquery when the stage cannot be fused into a single query. - final DruidRel druidRel = call.rel(call.getRelList().size() - 1); - return !stage.canFollow(druidRel.getPartialDruidQuery().stage()); + final DruidRel lowerDruidRel = call.rel(call.getRelList().size() - 1); + final RelNode lowerRel = lowerDruidRel.getPartialDruidQuery().leafRel(); + final PartialDruidQuery.Stage lowerStage = lowerDruidRel.getPartialDruidQuery().stage(); + + if (stage.canFollow(lowerStage) + || (stage == PartialDruidQuery.Stage.WHERE_FILTER + && PartialDruidQuery.Stage.HAVING_FILTER.canFollow(lowerStage)) + || (stage == PartialDruidQuery.Stage.SELECT_PROJECT + && PartialDruidQuery.Stage.SORT_PROJECT.canFollow(lowerStage))) { + // Don't consider cases that can be fused into a single query. + return false; + } else if (stage == PartialDruidQuery.Stage.WHERE_FILTER && lowerRel instanceof Filter) { + // Don't consider filter-on-filter. FilterMergeRule will handle it. + return false; + } else if (stage == PartialDruidQuery.Stage.WHERE_FILTER + && lowerStage == PartialDruidQuery.Stage.SELECT_PROJECT) { + // Don't consider filter-on-project. ProjectFilterTransposeRule will handle it by swapping them. + return false; + } else if (stage == PartialDruidQuery.Stage.SELECT_PROJECT && lowerRel instanceof Project) { + // Don't consider project-on-project. ProjectMergeRule will handle it. + return false; + } else { + // Consider subqueries in all other cases. + return true; + } } } } diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java index be65a64e80a1..f413fc09c1ee 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java @@ -2718,8 +2718,9 @@ public void testLeftJoinOnTwoInlineDataSourcesWithTimeFilter(Map JoinType.LEFT ) ) + .virtualColumns(expressionVirtualColumn("_v0", "'10.1'", ColumnType.STRING)) .intervals(querySegmentSpec(Filtration.eternity())) - .columns("__time", "v0") + .columns("__time", "_v0") .filters(new SelectorDimFilter("v0", "10.1", null)) .context(queryContext) .build() @@ -2829,8 +2830,10 @@ public void testLeftJoinOnTwoInlineDataSourcesWithOuterWhere(Map JoinType.LEFT ) ) + .virtualColumns(expressionVirtualColumn("_v0", "'10.1'", ColumnType.STRING)) .intervals(querySegmentSpec(Filtration.eternity())) - .columns("__time", "v0") + .filters(selector("v0", "10.1", null)) + .columns("__time", "_v0") .context(queryContext) .build() ), @@ -3022,8 +3025,9 @@ public void testInnerJoinOnTwoInlineDataSourcesWithOuterWhere(Map