You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by hu...@apache.org on 2022/09/02 09:34:27 UTC
[doris] branch master updated: [feature](nereids) Convert subqueries into algebraic expressions and … (#11454)
This is an automated email from the ASF dual-hosted git repository.
huajianlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 7f7a3a7524 [feature](nereids) Convert subqueries into algebraic expressions and … (#11454)
7f7a3a7524 is described below
commit 7f7a3a75242d00f41e7243e5f4105b27d954a29e
Author: zhengshiJ <32...@users.noreply.github.com>
AuthorDate: Fri Sep 2 17:34:19 2022 +0800
[feature](nereids) Convert subqueries into algebraic expressions and … (#11454)
1.Convert subqueries to Apply nodes.
2.Convert ApplyNode to ordinary join.
### Detailed design:
There are three types of current subexpressions, scalarSubquery, inSubquery, and Exists. The scalarSubquery refers to the returned data as 1 row and 1 column.
**Subquery replacement**
```
before:
scalarSubquery: filter(t1.a = scalarSubquery(output b));
inSubquery: filter(inSubquery); inSubquery = (t1.a in select ***);
exists: filter(exists); exists = (select ***);
end:
scalarSubquery: filter(t1.a = b);
inSubquery: filter(True);
exists: filter(True);
```
**Subquery Transformation Rules**
```
PushApplyUnderFilter
* before:
* Apply
* / \
* Input(output:b) Filter(Correlated predicate/UnCorrelated predicate)
*
* after:
* Filter(Correlated predicate)
* |
* Apply
* / \
* Input(output:b) Filter(UnCorrelated predicate)
```
```
PushApplyUnderProject
* before:
* Apply
* / \
* Input(output:b) Project(output:a)
*
* after:
* Project(b,(if the Subquery is Scalar add 'a' as the output column))
* / \
* Input(output:b) Apply
```
```
ApplyPullFilterOnAgg
* before:
* Apply
* / \
* Input(output:b) agg(output:fn,c; group by:null)
* |
* Filter(Correlated predicate(Input.e = this.f)/UnCorrelated predicate)
*
* end:
* Apply(Correlated predicate(Input.e = this.f))
* / \
* Input(output:b) agg(output:fn,this.f; group by:this.f)
* |
* Filter(UnCorrelated predicate)
```
```
ApplyPullFilterOnProjectUnderAgg
* before:
* apply
* / \
* Input(output:b) agg
* |
* Project(output:a)
* |
* Filter(correlated predicate(Input.e = this.f)/Unapply predicate)
* |
* child
* apply
* / \
* Input(output:b) agg
* |
* Filter(correlated predicate(Input.e = this.f)/Unapply predicate)
* |
* Project(output:a,this.f, Unapply predicate(slots))
* |
* child
```
```
ScalarToJoin
* UnCorrelated -> CROSS_JOIN
* Correlated -> LEFT_OUTER_JOIN
```
```
InToJoin
* Not In -> LEFT_ANTI_JOIN
* In -> LEFT_SEMI_JOIN
```
```
existsToJoin
* Exists
* Correlated -> LEFT_SEMI_JOIN
* correlated LEFT_SEMI_JOIN(Correlated Predicate)
* / \ --> / \
* input queryPlan input queryPlan
*
* UnCorrelated -> CROSS_JOIN(limit(1))
* uncorrelated CROSS_JOIN
* / \ --> / \
* input queryPlan input limit(1)
* |
* queryPlan
*
* Not Exists
* Correlated -> LEFT_ANTI_JOIN
* correlated LEFT_ANTI_JOIN(Correlated Predicate)
* / \ --> / \
* input queryPlan input queryPlan
*
* UnCorrelated -> CROSS_JOIN(Count(*))
* Filter(count(*) = 0)
* |
* apply Cross_Join
* / \ --> / \
* input queryPlan input agg(output:count(*))
* |
* limit(1)
* |
* queryPlan
```
---
.../org/apache/doris/nereids/CascadesContext.java | 18 +
.../doris/nereids/analyzer/NereidsAnalyzer.java | 2 +
.../glue/translator/ExpressionTranslator.java | 35 +-
.../glue/translator/PhysicalPlanTranslator.java | 36 +-
.../AdjustApplyFromCorrelatToUnCorrelatJob.java | 50 +++
.../jobs/batch/AnalyzeSubqueryRulesJob.java | 34 ++
.../nereids/jobs/batch/ConvertApplyToJoinJob.java | 43 ++
.../doris/nereids/jobs/batch/RewriteJob.java | 8 +
.../doris/nereids/parser/LogicalPlanBuilder.java | 5 +-
.../org/apache/doris/nereids/rules/RuleSet.java | 2 +
.../org/apache/doris/nereids/rules/RuleType.java | 12 +
.../nereids/rules/analysis/AnalyzeSubquery.java | 132 ++++++
.../nereids/rules/analysis/BindSlotReference.java | 186 ++++++++-
.../apache/doris/nereids/rules/analysis/Scope.java | 41 +-
...ogicalAssertNumRowsToPhysicalAssertNumRows.java | 36 ++
.../rewrite/logical/ApplyPullFilterOnAgg.java | 91 +++++
.../logical/ApplyPullFilterOnProjectUnderAgg.java | 84 ++++
.../rules/rewrite/logical/ExistsApplyToJoin.java | 120 ++++++
.../rules/rewrite/logical/InApplyToJoin.java | 68 ++++
.../rewrite/logical/PushApplyUnderFilter.java | 79 ++++
.../rewrite/logical/PushApplyUnderProject.java | 64 +++
.../rules/rewrite/logical/ScalarApplyToJoin.java | 74 ++++
.../apache/doris/nereids/stats/JoinEstimation.java | 12 +-
.../doris/nereids/stats/StatsCalculator.java | 13 +
.../doris/nereids/trees/expressions/Alias.java | 2 +-
.../trees/expressions/AssertNumRowsElement.java | 109 +++++
.../doris/nereids/trees/expressions/Exists.java | 41 +-
.../nereids/trees/expressions/InSubquery.java | 35 +-
.../doris/nereids/trees/expressions/ListQuery.java | 11 +-
.../nereids/trees/expressions/ScalarSubquery.java | 6 +
.../nereids/trees/expressions/SlotReference.java | 11 +-
.../nereids/trees/expressions/SubqueryExpr.java | 39 +-
.../visitor/DefaultSubExprRewriter.java | 76 ----
.../expressions/visitor/ExpressionVisitor.java | 5 +
.../apache/doris/nereids/trees/plans/PlanType.java | 6 +-
.../nereids/trees/plans/logical/LogicalApply.java | 101 +++--
...rceSingleRow.java => LogicalAssertNumRows.java} | 69 ++--
.../trees/plans/logical/LogicalCorrelatedJoin.java | 136 -------
.../PhysicalAssertNumRows.java} | 60 +--
.../nereids/trees/plans/visitor/PlanVisitor.java | 16 +-
.../java/org/apache/doris/nereids/util/Utils.java | 59 +++
.../nereids/trees/expressions/SubqueryTest.java | 24 +-
.../nereids/util/AnalyzeWhereSubqueryTest.java | 451 +++++++++++++++++++++
.../nereids_syntax_p0/sub_query_correlated.groovy | 243 +++++++++++
44 files changed, 2372 insertions(+), 373 deletions(-)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java
index a6ae32b5e4..88f3601361 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java
@@ -32,12 +32,15 @@ import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleFactory;
import org.apache.doris.nereids.rules.RuleSet;
import org.apache.doris.nereids.rules.analysis.Scope;
+import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.qe.ConnectContext;
import com.google.common.collect.ImmutableList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
/**
@@ -50,6 +53,8 @@ public class CascadesContext {
private JobPool jobPool;
private final JobScheduler jobScheduler;
private JobContext currentJobContext;
+ // subqueryExprIsAnalyzed: whether the subquery has been analyzed.
+ private Map<SubqueryExpr, Boolean> subqueryExprIsAnalyzed;
/**
* Constructor of OptimizerContext.
@@ -64,6 +69,7 @@ public class CascadesContext {
this.jobPool = new JobStack();
this.jobScheduler = new SimpleJobScheduler();
this.currentJobContext = new JobContext(this, PhysicalProperties.ANY, Double.MAX_VALUE);
+ this.subqueryExprIsAnalyzed = new HashMap<>();
}
public static CascadesContext newContext(StatementContext statementContext, Plan initPlan) {
@@ -127,6 +133,18 @@ public class CascadesContext {
return this;
}
+ public void setSubqueryExprIsAnalyzed(SubqueryExpr subqueryExpr, boolean isAnalyzed) {
+ subqueryExprIsAnalyzed.put(subqueryExpr, isAnalyzed);
+ }
+
+ public boolean subqueryIsAnalyzed(SubqueryExpr subqueryExpr) {
+ if (subqueryExprIsAnalyzed.get(subqueryExpr) == null) {
+ setSubqueryExprIsAnalyzed(subqueryExpr, false);
+ return false;
+ }
+ return subqueryExprIsAnalyzed.get(subqueryExpr);
+ }
+
public CascadesContext bottomUpRewrite(RuleFactory... rules) {
return execute(new RewriteBottomUpJob(memo.getRoot(), currentJobContext, ImmutableList.copyOf(rules)));
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java
index 3b461e16a0..596bf9cd0e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.analyzer;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.jobs.batch.AnalyzeRulesJob;
+import org.apache.doris.nereids.jobs.batch.AnalyzeSubqueryRulesJob;
import org.apache.doris.nereids.jobs.batch.CheckAnalysisJob;
import org.apache.doris.nereids.jobs.batch.FinalizeAnalyzeJob;
import org.apache.doris.nereids.rules.analysis.Scope;
@@ -45,6 +46,7 @@ public class NereidsAnalyzer {
public void analyze() {
new AnalyzeRulesJob(cascadesContext, outerScope).execute();
+ new AnalyzeSubqueryRulesJob(cascadesContext).execute();
new FinalizeAnalyzeJob(cascadesContext).execute();
// check whether analyze result is meaningful
new CheckAnalysisJob(cascadesContext).execute();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
index 18b5bea6ef..ebfb969758 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
@@ -18,8 +18,10 @@
package org.apache.doris.nereids.glue.translator;
import org.apache.doris.analysis.ArithmeticExpr;
+import org.apache.doris.analysis.AssertNumRowsElement.Assertion;
import org.apache.doris.analysis.BinaryPredicate;
import org.apache.doris.analysis.BinaryPredicate.Operator;
+import org.apache.doris.analysis.BoolLiteral;
import org.apache.doris.analysis.CaseExpr;
import org.apache.doris.analysis.CaseWhenClause;
import org.apache.doris.analysis.CastExpr;
@@ -32,15 +34,18 @@ import org.apache.doris.analysis.TimestampArithmeticExpr;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
import org.apache.doris.nereids.trees.expressions.Between;
import org.apache.doris.nereids.trees.expressions.BinaryArithmetic;
import org.apache.doris.nereids.trees.expressions.CaseWhen;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Exists;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.GreaterThan;
import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
import org.apache.doris.nereids.trees.expressions.InPredicate;
+import org.apache.doris.nereids.trees.expressions.InSubquery;
import org.apache.doris.nereids.trees.expressions.LessThan;
import org.apache.doris.nereids.trees.expressions.LessThanEqual;
import org.apache.doris.nereids.trees.expressions.Like;
@@ -148,10 +153,12 @@ public class ExpressionTranslator extends DefaultExpressionVisitor<Expr, PlanTra
true);
} else if (not.child() instanceof EqualTo) {
EqualTo equalTo = (EqualTo) not.child();
- BinaryPredicate binaryPredicate = new BinaryPredicate(Operator.NE,
+ BinaryPredicate binaryPredicate = new BinaryPredicate(Operator.NE,
equalTo.child(0).accept(this, context),
equalTo.child(1).accept(this, context));
return binaryPredicate;
+ } else if (not.child() instanceof InSubquery || not.child() instanceof Exists) {
+ return new BoolLiteral(true);
} else {
return new CompoundPredicate(CompoundPredicate.Operator.NOT,
not.child(0).accept(this, context),
@@ -276,4 +283,30 @@ public class ExpressionTranslator extends DefaultExpressionVisitor<Expr, PlanTra
arithmetic.getDataType().toCatalogDataType());
}
}
+
+ public static org.apache.doris.analysis.AssertNumRowsElement translateAssert(
+ AssertNumRowsElement assertNumRowsElement) {
+ return new org.apache.doris.analysis.AssertNumRowsElement(assertNumRowsElement.getDesiredNumOfRows(),
+ assertNumRowsElement.getSubqueryString(), translateAssertion(assertNumRowsElement.getAssertion()));
+ }
+
+ private static org.apache.doris.analysis.AssertNumRowsElement.Assertion translateAssertion(
+ AssertNumRowsElement.Assertion assertion) {
+ switch (assertion) {
+ case EQ:
+ return Assertion.EQ;
+ case NE:
+ return Assertion.NE;
+ case LT:
+ return Assertion.LT;
+ case LE:
+ return Assertion.LE;
+ case GT:
+ return Assertion.GT;
+ case GE:
+ return Assertion.GE;
+ default:
+ throw new AnalysisException("UnSupported type: " + assertion);
+ }
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
index 890b2e2cbf..9f2270eb35 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
@@ -45,6 +45,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort;
import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribution;
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
@@ -58,9 +59,9 @@ import org.apache.doris.nereids.trees.plans.physical.PhysicalTopN;
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.JoinUtils;
-import org.apache.doris.nereids.util.SlotExtractor;
import org.apache.doris.nereids.util.Utils;
import org.apache.doris.planner.AggregationNode;
+import org.apache.doris.planner.AssertNumRowsNode;
import org.apache.doris.planner.CrossJoinNode;
import org.apache.doris.planner.DataPartition;
import org.apache.doris.planner.ExchangeNode;
@@ -96,24 +97,6 @@ import java.util.stream.Stream;
* </STRONG>
*/
public class PhysicalPlanTranslator extends DefaultPlanVisitor<PlanFragment, PlanTranslatorContext> {
- /**
- * The left and right child of origin predicates need to be swap sometimes.
- * Case A:
- * select * from t1 join t2 on t2.id=t1.id
- * The left plan node is t1 and the right plan node is t2.
- * The left child of origin predicate is t2.id and the right child of origin predicate is t1.id.
- * In this situation, the children of predicate need to be swap => t1.id=t2.id.
- */
- private static Expression swapEqualToForChildrenOrder(EqualTo equalTo, List<Slot> leftOutput) {
- Set<ExprId> leftSlots = SlotExtractor.extractSlot(equalTo.left()).stream()
- .map(NamedExpression::getExprId).collect(Collectors.toSet());
- if (leftOutput.stream().map(NamedExpression::getExprId).collect(Collectors.toSet()).containsAll(leftSlots)) {
- return equalTo;
- } else {
- return new EqualTo(equalTo.right(), equalTo.left());
- }
- }
-
/**
* Translate Nereids Physical Plan tree to Stale Planner PlanFragment tree.
*
@@ -367,7 +350,7 @@ public class PhysicalPlanTranslator extends DefaultPlanVisitor<PlanFragment, Pla
List<Expr> execEqConjuncts = hashJoin.getHashJoinConjuncts().stream()
.map(EqualTo.class::cast)
- .map(e -> swapEqualToForChildrenOrder(e, hashJoin.left().getOutput()))
+ .map(e -> JoinUtils.swapEqualToForChildrenOrder(e, hashJoin.left().getOutput()))
.map(e -> ExpressionTranslator.translate(e, context))
.collect(Collectors.toList());
@@ -543,6 +526,19 @@ public class PhysicalPlanTranslator extends DefaultPlanVisitor<PlanFragment, Pla
return distribution.child().accept(this, context);
}
+ @Override
+ public PlanFragment visitPhysicalAssertNumRows(PhysicalAssertNumRows<Plan> assertNumRows,
+ PlanTranslatorContext context) {
+ PlanFragment inputFragment = assertNumRows.child(0).accept(this, context);
+ //create assertNode
+ PlanNode child = inputFragment.getPlanRoot();
+ AssertNumRowsNode assertNumRowsNode = new AssertNumRowsNode(context.nextPlanNodeId(),
+ child, ExpressionTranslator.translateAssert(assertNumRows.getAssertNumRowsElement()));
+ PlanFragment mergeFragment = createParentFragment(inputFragment, DataPartition.UNPARTITIONED, context);
+ mergeFragment.addPlanRoot(assertNumRowsNode);
+ return mergeFragment;
+ }
+
private void extractExecSlot(Expr root, Set<Integer> slotRefList) {
if (root instanceof SlotRef) {
slotRefList.add(((SlotRef) root).getDesc().getId().asInt());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/AdjustApplyFromCorrelatToUnCorrelatJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/AdjustApplyFromCorrelatToUnCorrelatJob.java
new file mode 100644
index 0000000000..c61bb007cf
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/AdjustApplyFromCorrelatToUnCorrelatJob.java
@@ -0,0 +1,50 @@
+// 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.doris.nereids.jobs.batch;
+
+import org.apache.doris.nereids.CascadesContext;
+import org.apache.doris.nereids.rules.rewrite.logical.ApplyPullFilterOnAgg;
+import org.apache.doris.nereids.rules.rewrite.logical.ApplyPullFilterOnProjectUnderAgg;
+import org.apache.doris.nereids.rules.rewrite.logical.PushApplyUnderFilter;
+import org.apache.doris.nereids.rules.rewrite.logical.PushApplyUnderProject;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Adjust the plan in logicalApply so that there are no correlated columns in the subquery.
+ * Adjust the positions of apply and sub query nodes and apply,
+ * and adjust the correlated columns to apply to prepare for de nesting.
+ *
+ * For the project and filter on AGG, try to adjust them to apply.
+ * For the project and filter under AGG, bring the filter under AGG and merge it with agg.
+ */
+public class AdjustApplyFromCorrelatToUnCorrelatJob extends BatchRulesJob {
+ /**
+ * Constructor.
+ */
+ public AdjustApplyFromCorrelatToUnCorrelatJob(CascadesContext cascadesContext) {
+ super(cascadesContext);
+ rulesJob.addAll(ImmutableList.of(
+ topDownBatch(ImmutableList.of(
+ new PushApplyUnderProject(),
+ new PushApplyUnderFilter(),
+ new ApplyPullFilterOnAgg(),
+ new ApplyPullFilterOnProjectUnderAgg()
+ ))));
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/AnalyzeSubqueryRulesJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/AnalyzeSubqueryRulesJob.java
new file mode 100644
index 0000000000..1e41652e2b
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/AnalyzeSubqueryRulesJob.java
@@ -0,0 +1,34 @@
+// 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.doris.nereids.jobs.batch;
+
+import org.apache.doris.nereids.CascadesContext;
+import org.apache.doris.nereids.rules.analysis.AnalyzeSubquery;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Analyze subquery.
+ */
+public class AnalyzeSubqueryRulesJob extends BatchRulesJob {
+ public AnalyzeSubqueryRulesJob(CascadesContext cascadesContext) {
+ super(cascadesContext);
+ rulesJob.addAll(ImmutableList.of(
+ bottomUpBatch(ImmutableList.of(new AnalyzeSubquery()))));
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/ConvertApplyToJoinJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/ConvertApplyToJoinJob.java
new file mode 100644
index 0000000000..d086c4fb82
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/ConvertApplyToJoinJob.java
@@ -0,0 +1,43 @@
+// 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.doris.nereids.jobs.batch;
+
+import org.apache.doris.nereids.CascadesContext;
+import org.apache.doris.nereids.rules.rewrite.logical.ExistsApplyToJoin;
+import org.apache.doris.nereids.rules.rewrite.logical.InApplyToJoin;
+import org.apache.doris.nereids.rules.rewrite.logical.ScalarApplyToJoin;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Convert logicalApply without a correlated to a logicalJoin.
+ */
+public class ConvertApplyToJoinJob extends BatchRulesJob {
+ /**
+ * Constructor.
+ */
+ public ConvertApplyToJoinJob(CascadesContext cascadesContext) {
+ super(cascadesContext);
+ rulesJob.addAll(ImmutableList.of(
+ topDownBatch(ImmutableList.of(
+ new ScalarApplyToJoin(),
+ new InApplyToJoin(),
+ new ExistsApplyToJoin())
+ )));
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java
index 7563ffddf7..ed145d1179 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java
@@ -47,6 +47,14 @@ public class RewriteJob extends BatchRulesJob {
public RewriteJob(CascadesContext cascadesContext) {
super(cascadesContext);
ImmutableList<Job> jobs = new ImmutableList.Builder<Job>()
+ /*
+ * Subquery unnesting.
+ * 1. Adjust the plan in correlated logicalApply
+ * so that there are no correlated columns in the subquery.
+ * 2. Convert logicalApply to a logicalJoin.
+ */
+ .addAll(new AdjustApplyFromCorrelatToUnCorrelatJob(cascadesContext).rulesJob)
+ .addAll(new ConvertApplyToJoinJob(cascadesContext).rulesJob)
.add(topDownBatch(ImmutableList.of(new ExpressionNormalization())))
.add(topDownBatch(ImmutableList.of(new NormalizeAggregate())))
.add(topDownBatch(ImmutableList.of(new ReorderJoin())))
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 36c894b873..92566ea317 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -912,7 +912,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
} else {
outExpression = new InSubquery(
valueExpression,
- new ListQuery(typedVisit(ctx.query()))
+ new ListQuery(typedVisit(ctx.query())),
+ ctx.NOT() != null
);
}
break;
@@ -943,7 +944,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
@Override
public Expression visitExist(ExistContext context) {
- return ParserUtils.withOrigin(context, () -> new Exists(typedVisit(context.query())));
+ return ParserUtils.withOrigin(context, () -> new Exists(typedVisit(context.query()), false));
}
public List<Expression> withInList(PredicateContext ctx) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
index d744c12064..fb4ec44c40 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.rules;
import org.apache.doris.nereids.rules.exploration.join.JoinCommute;
import org.apache.doris.nereids.rules.exploration.join.JoinCommuteProject;
import org.apache.doris.nereids.rules.implementation.LogicalAggToPhysicalHashAgg;
+import org.apache.doris.nereids.rules.implementation.LogicalAssertNumRowsToPhysicalAssertNumRows;
import org.apache.doris.nereids.rules.implementation.LogicalFilterToPhysicalFilter;
import org.apache.doris.nereids.rules.implementation.LogicalJoinToHashJoin;
import org.apache.doris.nereids.rules.implementation.LogicalJoinToNestedLoopJoin;
@@ -58,6 +59,7 @@ public class RuleSet {
.add(new LogicalLimitToPhysicalLimit())
.add(new LogicalSortToPhysicalQuickSort())
.add(new LogicalTopNToPhysicalTopN())
+ .add(new LogicalAssertNumRowsToPhysicalAssertNumRows())
.build();
public List<Rule> getExplorationRules() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index b0be522fb8..d545013506 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -61,6 +61,17 @@ public enum RuleType {
JOIN_LEFT_CHILD_ELIMINATE_ALIAS_NODE(RuleTypeClass.REWRITE),
JOIN_RIGHT_CHILD_ELIMINATE_ALIAS_NODE(RuleTypeClass.REWRITE),
AGGREGATE_ELIMINATE_ALIAS_NODE(RuleTypeClass.REWRITE),
+
+ //subquery analyze
+ ANALYZE_FILTER_SUBQUERY(RuleTypeClass.REWRITE),
+ //subquery rewrite rule
+ PUSH_APPLY_UNDER_PROJECT(RuleTypeClass.REWRITE),
+ PUSH_APPLY_UNDER_FILTER(RuleTypeClass.REWRITE),
+ APPLY_PULL_FILTER_ON_AGG(RuleTypeClass.REWRITE),
+ APPLY_PULL_FILTER_ON_PROJECT_UNDER_AGG(RuleTypeClass.REWRITE),
+ SCALAR_APPLY_TO_JOIN(RuleTypeClass.REWRITE),
+ IN_APPLY_TO_JOIN(RuleTypeClass.REWRITE),
+ EXISTS_APPLY_TO_JOIN(RuleTypeClass.REWRITE),
// predicate push down rules
PUSH_DOWN_PREDICATE_THROUGH_JOIN(RuleTypeClass.REWRITE),
PUSH_DOWN_PREDICATE_THROUGH_AGGREGATION(RuleTypeClass.REWRITE),
@@ -100,6 +111,7 @@ public enum RuleType {
LOGICAL_TOP_N_TO_PHYSICAL_TOP_N_RULE(RuleTypeClass.IMPLEMENTATION),
LOGICAL_LIMIT_TO_PHYSICAL_LIMIT_RULE(RuleTypeClass.IMPLEMENTATION),
LOGICAL_OLAP_SCAN_TO_PHYSICAL_OLAP_SCAN_RULE(RuleTypeClass.IMPLEMENTATION),
+ LOGICAL_ASSERT_NUM_ROWS_TO_PHYSICAL_ASSERT_NUM_ROWS(RuleTypeClass.IMPLEMENTATION),
IMPLEMENTATION_SENTINEL(RuleTypeClass.IMPLEMENTATION),
// sentinel, use to count rules
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/AnalyzeSubquery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/AnalyzeSubquery.java
new file mode 100644
index 0000000000..461b83298d
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/AnalyzeSubquery.java
@@ -0,0 +1,132 @@
+// 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.doris.nereids.rules.analysis;
+
+import org.apache.doris.nereids.CascadesContext;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.expressions.Exists;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.InSubquery;
+import org.apache.doris.nereids.trees.expressions.ScalarSubquery;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
+import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
+import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * AnalyzeSubquery. translate from subquery to LogicalApply.
+ * In two steps
+ * The first step is to replace the predicate corresponding to the filter where the subquery is located.
+ * The second step converts the subquery into an apply node.
+ */
+public class AnalyzeSubquery implements AnalysisRuleFactory {
+ @Override
+ public List<Rule> buildRules() {
+ return ImmutableList.of(
+ RuleType.ANALYZE_FILTER_SUBQUERY.build(
+ logicalFilter().thenApply(ctx -> {
+ LogicalFilter filter = ctx.root;
+ List<SubqueryExpr> subqueryExprs = filter.getPredicates()
+ .collect(SubqueryExpr.class::isInstance);
+ if (subqueryExprs.isEmpty()) {
+ return filter;
+ }
+
+ // first step: Replace the subquery of predicate in LogicalFilter
+ // second step: Replace subquery with LogicalApply
+ return new LogicalFilter<>(new ReplaceSubquery().replace(filter.getPredicates()),
+ analyzedSubquery(
+ subqueryExprs, (LogicalPlan) filter.child(), ctx.cascadesContext
+ ));
+ })
+ )
+ );
+ }
+
+ private LogicalPlan analyzedSubquery(List<SubqueryExpr> subqueryExprs,
+ LogicalPlan childPlan, CascadesContext ctx) {
+ LogicalPlan tmpPlan = childPlan;
+ for (SubqueryExpr subqueryExpr : subqueryExprs) {
+ if (!ctx.subqueryIsAnalyzed(subqueryExpr)) {
+ tmpPlan = addApply(subqueryExpr, tmpPlan, ctx);
+ }
+ }
+ return tmpPlan;
+ }
+
+ private LogicalPlan addApply(SubqueryExpr subquery,
+ LogicalPlan childPlan, CascadesContext ctx) {
+ ctx.setSubqueryExprIsAnalyzed(subquery, true);
+ LogicalApply newApply = new LogicalApply(
+ subquery.getCorrelateSlots(),
+ subquery, Optional.empty(), childPlan, subquery.getQueryPlan());
+ List<Slot> projects = new ArrayList<>(childPlan.getOutput());
+ if (subquery instanceof ScalarSubquery) {
+ projects.add(subquery.getQueryPlan().getOutput().get(0));
+ }
+ return new LogicalProject(projects, newApply);
+ }
+
+ /**
+ * The Subquery in the LogicalFilter will change to LogicalApply, so we must replace the origin Subquery.
+ * LogicalFilter(predicate(contain subquery)) -> LogicalFilter(predicate(not contain subquery)
+ * Replace the subquery in logical with the relevant expression.
+ *
+ * The replacement rules are as follows:
+ * before:
+ * 1.filter(t1.a = scalarSubquery(output b));
+ * 2.filter(inSubquery); inSubquery = (t1.a in select ***);
+ * 3.filter(exists); exists = (select ***);
+ *
+ * after:
+ * 1.filter(t1.a = b);
+ * 2.filter(True);
+ * 3.filter(True);
+ */
+ private static class ReplaceSubquery extends DefaultExpressionRewriter {
+ public Expression replace(Expression expression) {
+ return (Expression) expression.accept(this, null);
+ }
+
+ @Override
+ public Object visitExistsSubquery(Exists exists, Object context) {
+ return BooleanLiteral.TRUE;
+ }
+
+ @Override
+ public Object visitInSubquery(InSubquery in, Object context) {
+ return BooleanLiteral.TRUE;
+ }
+
+ @Override
+ public Object visitScalarSubquery(ScalarSubquery scalar, Object context) {
+ return scalar.getQueryPlan().getOutput().get(0);
+ }
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
index 7ce820a483..fcb2bd2eda 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
@@ -22,14 +22,21 @@ import org.apache.doris.nereids.analyzer.UnboundAlias;
import org.apache.doris.nereids.analyzer.UnboundSlot;
import org.apache.doris.nereids.analyzer.UnboundStar;
import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.memo.Memo;
import org.apache.doris.nereids.properties.OrderKey;
import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.Exists;
import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.InSubquery;
+import org.apache.doris.nereids.trees.expressions.ListQuery;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Not;
+import org.apache.doris.nereids.trees.expressions.ScalarSubquery;
import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.expressions.visitor.DefaultSubExprRewriter;
+import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
+import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
import org.apache.doris.nereids.trees.plans.GroupPlan;
import org.apache.doris.nereids.trees.plans.LeafPlan;
import org.apache.doris.nereids.trees.plans.Plan;
@@ -39,11 +46,13 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
+import org.apache.doris.planner.PlannerContext;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang.StringUtils;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -66,7 +75,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
private Scope toScope(List<Slot> slots) {
if (outerScope.isPresent()) {
- return new Scope(outerScope, slots);
+ return new Scope(outerScope, slots, outerScope.get().getSubquery());
} else {
return new Scope(slots);
}
@@ -158,7 +167,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
return (E) new SlotBinder(toScope(boundedSlots), plan, cascadesContext).bind(expr);
}
- private class SlotBinder extends DefaultSubExprRewriter<Void> {
+ private class SlotBinder extends SubExprAnalyzer {
private final Plan plan;
public SlotBinder(Scope scope, Plan plan, CascadesContext cascadesContext) {
@@ -171,7 +180,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
}
@Override
- public Expression visitUnboundAlias(UnboundAlias unboundAlias, Void context) {
+ public Expression visitUnboundAlias(UnboundAlias unboundAlias, PlannerContext context) {
Expression child = unboundAlias.child().accept(this, context);
if (child instanceof NamedExpression) {
return new Alias(child, ((NamedExpression) child).getName());
@@ -182,19 +191,26 @@ public class BindSlotReference implements AnalysisRuleFactory {
}
@Override
- public Slot visitUnboundSlot(UnboundSlot unboundSlot, Void context) {
- Optional<List<Slot>> boundedOpt = getScope()
- .toScopeLink() // Scope Link from inner scope to outer scope
- .stream()
- .map(scope -> bindSlot(unboundSlot, scope.getSlots()))
- .filter(slots -> !slots.isEmpty())
- .findFirst();
+ public Slot visitUnboundSlot(UnboundSlot unboundSlot, PlannerContext context) {
+ Optional<List<Slot>> boundedOpt = Optional.of(bindSlot(unboundSlot, getScope().getSlots()));
+ boolean foundInThisScope = !boundedOpt.get().isEmpty();
+ // Currently only looking for symbols on the previous level.
+ if (!foundInThisScope && getScope().getOuterScope().isPresent()) {
+ boundedOpt = Optional.of(bindSlot(unboundSlot,
+ getScope()
+ .getOuterScope()
+ .get()
+ .getSlots()));
+ }
if (!boundedOpt.isPresent()) {
throw new AnalysisException("Cannot resolve " + unboundSlot.toString());
}
List<Slot> bounded = boundedOpt.get();
switch (bounded.size()) {
case 1:
+ if (!foundInThisScope) {
+ getScope().getOuterScope().get().getCorrelatedSlots().add(bounded.get(0));
+ }
return bounded.get(0);
default:
throw new AnalysisException(unboundSlot + " is ambiguous: "
@@ -205,7 +221,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
}
@Override
- public Expression visitUnboundStar(UnboundStar unboundStar, Void context) {
+ public Expression visitUnboundStar(UnboundStar unboundStar, PlannerContext context) {
if (!(plan instanceof LogicalProject)) {
throw new AnalysisException("UnboundStar must exists in Projection");
}
@@ -222,7 +238,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
}
}
- private BoundStar bindQualifiedStar(List<String> qualifierStar, Void context) {
+ private BoundStar bindQualifiedStar(List<String> qualifierStar, PlannerContext context) {
// FIXME: compatible with previous behavior:
// https://github.com/apache/doris/pull/10415/files/3fe9cb0c3f805ab3a9678033b281b16ad93ec60a#r910239452
List<Slot> slots = getScope().getSlots().stream().filter(boundSlot -> {
@@ -319,4 +335,148 @@ public class BindSlotReference implements AnalysisRuleFactory {
return (List) children();
}
}
+
+ /**
+ * Use the visitor to iterate sub expression.
+ */
+ private static class SubExprAnalyzer extends DefaultExpressionRewriter<PlannerContext> {
+ private final Scope scope;
+ private final CascadesContext cascadesContext;
+
+ public SubExprAnalyzer(Scope scope, CascadesContext cascadesContext) {
+ this.scope = scope;
+ this.cascadesContext = cascadesContext;
+ }
+
+ @Override
+ public Expression visitNot(Not not, PlannerContext context) {
+ Expression child = not.child();
+ if (child instanceof Exists) {
+ return visitExistsSubquery(
+ new Exists(((Exists) child).getQueryPlan(), true), context);
+ } else if (child instanceof InSubquery) {
+ return visitInSubquery(new InSubquery(((InSubquery) child).getCompareExpr(),
+ ((InSubquery) child).getListQuery(), true), context);
+ }
+ return visit(not, context);
+ }
+
+ @Override
+ public Expression visitExistsSubquery(Exists exists, PlannerContext context) {
+ AnalyzedResult analyzedResult = analyzeSubquery(exists);
+
+ return new Exists(analyzedResult.getLogicalPlan(),
+ analyzedResult.getCorrelatedSlots(), exists.isNot());
+ }
+
+ @Override
+ public Expression visitInSubquery(InSubquery expr, PlannerContext context) {
+ AnalyzedResult analyzedResult = analyzeSubquery(expr);
+
+ checkOutputColumn(analyzedResult.getLogicalPlan());
+ checkHasGroupBy(analyzedResult);
+
+ return new InSubquery(
+ expr.getCompareExpr().accept(this, context),
+ new ListQuery(analyzedResult.getLogicalPlan()),
+ analyzedResult.getCorrelatedSlots(), expr.isNot());
+ }
+
+ @Override
+ public Expression visitScalarSubquery(ScalarSubquery scalar, PlannerContext context) {
+ AnalyzedResult analyzedResult = analyzeSubquery(scalar);
+
+ checkOutputColumn(analyzedResult.getLogicalPlan());
+ checkRootIsAgg(analyzedResult);
+ checkHasGroupBy(analyzedResult);
+
+ return new ScalarSubquery(analyzedResult.getLogicalPlan(), analyzedResult.getCorrelatedSlots());
+ }
+
+ private void checkOutputColumn(LogicalPlan plan) {
+ if (plan.getOutput().size() != 1) {
+ throw new AnalysisException("Multiple columns returned by subquery are not yet supported. Found "
+ + plan.getOutput().size());
+ }
+ }
+
+ private void checkRootIsAgg(AnalyzedResult analyzedResult) {
+ if (!analyzedResult.isCorrelated()) {
+ return;
+ }
+ if (!analyzedResult.rootIsAgg()) {
+ throw new AnalysisException("The select item in correlated subquery of binary predicate "
+ + "should only be sum, min, max, avg and count. Current subquery: "
+ + analyzedResult.getLogicalPlan());
+ }
+ }
+
+ private void checkHasGroupBy(AnalyzedResult analyzedResult) {
+ if (!analyzedResult.isCorrelated()) {
+ return;
+ }
+ if (analyzedResult.hasGroupBy()) {
+ throw new AnalysisException("Unsupported correlated subquery with grouping and/or aggregation "
+ + analyzedResult.getLogicalPlan());
+ }
+ }
+
+ private AnalyzedResult analyzeSubquery(SubqueryExpr expr) {
+ CascadesContext subqueryContext = new Memo(expr.getQueryPlan())
+ .newCascadesContext((cascadesContext.getStatementContext()));
+ Scope subqueryScope = genScopeWithSubquery(expr);
+ subqueryContext
+ .newAnalyzer(Optional.of(subqueryScope))
+ .analyze();
+ return new AnalyzedResult((LogicalPlan) subqueryContext.getMemo().copyOut(false),
+ subqueryScope.getCorrelatedSlots());
+ }
+
+ private Scope genScopeWithSubquery(SubqueryExpr expr) {
+ return new Scope(getScope().getOuterScope(),
+ getScope().getSlots(),
+ Optional.ofNullable(expr));
+ }
+
+ public Scope getScope() {
+ return scope;
+ }
+
+ public CascadesContext getCascadesContext() {
+ return cascadesContext;
+ }
+ }
+
+ private static class AnalyzedResult {
+ private final LogicalPlan logicalPlan;
+ private final List<Slot> correlatedSlots;
+
+ public AnalyzedResult(LogicalPlan logicalPlan, List<Slot> correlatedSlots) {
+ this.logicalPlan = Objects.requireNonNull(logicalPlan, "logicalPlan can not be null");
+ this.correlatedSlots = correlatedSlots == null ? new ArrayList<>() : ImmutableList.copyOf(correlatedSlots);
+ }
+
+ public LogicalPlan getLogicalPlan() {
+ return logicalPlan;
+ }
+
+ public List<Slot> getCorrelatedSlots() {
+ return correlatedSlots;
+ }
+
+ public boolean isCorrelated() {
+ return !correlatedSlots.isEmpty();
+ }
+
+ public boolean rootIsAgg() {
+ return logicalPlan instanceof LogicalAggregate;
+ }
+
+ public boolean hasGroupBy() {
+ if (rootIsAgg()) {
+ return !((LogicalAggregate) logicalPlan).getGroupByExpressions().isEmpty();
+ }
+ return false;
+ }
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/Scope.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/Scope.java
index a9df300964..918392f9a5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/Scope.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/Scope.java
@@ -18,28 +18,58 @@
package org.apache.doris.nereids.rules.analysis;
import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* The slot range required for expression analyze.
+ *
+ * slots: The symbols used at this level are stored in slots.
+ * outerScope: The scope information corresponding to the parent is stored in outerScope.
+ * ownerSubquery: The subquery corresponding to ownerSubquery.
+ * subqueryToOuterCorrelatedSlots: The slots correlated in the subquery,
+ * only the slots that cannot be resolved at this level.
+ *
+ * eg:
+ * t1(k1, v1) / t2(k2, v2)
+ * select * from t1 where t1.k1 = (select sum(k2) from t2 where t1.v1 = t2.v2);
+ *
+ * When analyzing subquery:
+ *
+ * slots: k2, v2;
+ * outerScope:
+ * slots: k1, v1;
+ * outerScope: Optional.empty();
+ * ownerSubquery: Optionsl.empty();
+ * subqueryToOuterCorrelatedSlots: empty();
+ * ownerSubquery: subquery((select sum(k2) from t2 where t1.v1 = t2.v2));
+ * subqueryToOuterCorrelatedSlots: (subquery, v1);
*/
public class Scope {
private final Optional<Scope> outerScope;
private final List<Slot> slots;
- public Scope(Optional<Scope> outerScope, List<Slot> slots) {
+ private final Optional<SubqueryExpr> ownerSubquery;
+ private List<Slot> correlatedSlots;
+
+ public Scope(Optional<Scope> outerScope, List<Slot> slots, Optional<SubqueryExpr> subqueryExpr) {
this.outerScope = outerScope;
this.slots = slots;
+ this.ownerSubquery = subqueryExpr;
+ this.correlatedSlots = new ArrayList<>();
}
public Scope(List<Slot> slots) {
this.outerScope = Optional.empty();
this.slots = slots;
+ this.ownerSubquery = Optional.empty();
+ this.correlatedSlots = new ArrayList<>();
}
public List<Slot> getSlots() {
@@ -50,8 +80,17 @@ public class Scope {
return outerScope;
}
+ public Optional<SubqueryExpr> getSubquery() {
+ return ownerSubquery;
+ }
+
+ public List<Slot> getCorrelatedSlots() {
+ return correlatedSlots;
+ }
+
/**
* generate scope link from inner to outer.
+ * Used for multi-level subquery parsing.
*/
public List<Scope> toScopeLink() {
Scope scope = this;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAssertNumRowsToPhysicalAssertNumRows.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAssertNumRowsToPhysicalAssertNumRows.java
new file mode 100644
index 0000000000..9c18dab5c3
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAssertNumRowsToPhysicalAssertNumRows.java
@@ -0,0 +1,36 @@
+// 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.doris.nereids.rules.implementation;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
+
+/**
+ * Implementation rule that convert logical assertNumRows to physical assertNumRows.
+ */
+public class LogicalAssertNumRowsToPhysicalAssertNumRows extends OneImplementationRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalAssertNumRows().then(topN -> new PhysicalAssertNumRows<>(
+ topN.getAssertNumRowsElement(),
+ topN.getLogicalProperties(),
+ topN.child())
+ ).toRule(RuleType.LOGICAL_ASSERT_NUM_ROWS_TO_PHYSICAL_ASSERT_NUM_ROWS);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnAgg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnAgg.java
new file mode 100644
index 0000000000..293aff291f
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnAgg.java
@@ -0,0 +1,91 @@
+// 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.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.nereids.util.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Merge the correlated predicate and agg in the filter under apply.
+ * And keep the unCorrelated predicate under agg.
+ *
+ * Use the correlated column as the group by column of agg,
+ * the output column is the correlated column and the input column.
+ *
+ * before:
+ * apply
+ * / \
+ * Input(output:b) agg(output:fn; group by:null)
+ * |
+ * Filter(correlated predicate(Input.e = this.f)/Unapply predicate)
+ *
+ * end:
+ * apply(correlated predicate(Input.e = this.f))
+ * / \
+ * Input(output:b) agg(output:fn,this.f; group by:this.f)
+ * |
+ * Filter(Uncorrelated predicate)
+ */
+public class ApplyPullFilterOnAgg extends OneRewriteRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalApply(group(), logicalAggregate(logicalFilter())).when(LogicalApply::isCorrelated).then(apply -> {
+ LogicalAggregate<LogicalFilter<GroupPlan>> agg = apply.right();
+ LogicalFilter<GroupPlan> filter = agg.child();
+ List<Expression> predicates = ExpressionUtils.extractConjunction(filter.getPredicates());
+ Map<Boolean, List<Expression>> split = Utils.splitCorrelatedConjuncts(
+ predicates, apply.getCorrelationSlot());
+ List<Expression> correlatedPredicate = split.get(true);
+ List<Expression> unCorrelatedPredicate = split.get(false);
+
+ LogicalFilter<GroupPlan> newUnCorrelatedFilter = null;
+ if (!unCorrelatedPredicate.isEmpty()) {
+ newUnCorrelatedFilter = new LogicalFilter<>(ExpressionUtils.and(unCorrelatedPredicate),
+ filter.child());
+ }
+
+ List<NamedExpression> newAggOutput = new ArrayList<>(agg.getOutputExpressions());
+ List<Expression> newGroupby = Utils.getCorrelatedSlots(correlatedPredicate,
+ apply.getCorrelationSlot());
+ newGroupby.addAll(agg.getGroupByExpressions());
+ newAggOutput.addAll(newGroupby.stream().map(NamedExpression.class::cast).collect(Collectors.toList()));
+ LogicalAggregate newAgg = new LogicalAggregate<>(
+ newGroupby, newAggOutput,
+ newUnCorrelatedFilter == null ? filter.child() : newUnCorrelatedFilter);
+ return new LogicalApply<>(apply.getCorrelationSlot(),
+ apply.getSubqueryExpr(),
+ Optional.ofNullable(ExpressionUtils.and(correlatedPredicate)),
+ apply.left(), newAgg);
+ }).toRule(RuleType.APPLY_PULL_FILTER_ON_AGG);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnProjectUnderAgg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnProjectUnderAgg.java
new file mode 100644
index 0000000000..0c5d8f29f6
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnProjectUnderAgg.java
@@ -0,0 +1,84 @@
+// 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.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Swap the order of project and filter under agg in correlated subqueries.
+ * before:
+ * apply
+ * / \
+ * Input(output:b) agg
+ * |
+ * Project(output:a)
+ * |
+ * Filter(correlated predicate(Input.e = this.f)/Unapply predicate)
+ * |
+ * child
+ *
+ * after:
+ * apply
+ * / \
+ * Input(output:b) agg
+ * |
+ * Filter(correlated predicate(Input.e = this.f)/Unapply predicate)
+ * |
+ * Project(output:a,this.f, Unapply predicate(slots))
+ * |
+ * child
+ */
+public class ApplyPullFilterOnProjectUnderAgg extends OneRewriteRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalApply(group(), logicalAggregate(logicalProject(logicalFilter())))
+ .when(LogicalApply::isCorrelated).then(apply -> {
+ LogicalAggregate<LogicalProject<LogicalFilter<GroupPlan>>> agg = apply.right();
+
+ LogicalProject<LogicalFilter<GroupPlan>> project = agg.child();
+ LogicalFilter<GroupPlan> filter = project.child();
+ List<NamedExpression> newProjects = Lists.newArrayList();
+ newProjects.addAll(project.getProjects());
+ filter.child().getOutput()
+ .stream().forEach(slot -> {
+ if (!newProjects.contains(slot)) {
+ newProjects.add(slot);
+ }
+ });
+
+ LogicalProject newProject = new LogicalProject<>(newProjects, filter.child());
+ LogicalFilter newFilter = new LogicalFilter<>(filter.getPredicates(), newProject);
+ LogicalAggregate newAgg = new LogicalAggregate<>(
+ agg.getGroupByExpressions(), agg.getOutputExpressions(), newFilter);
+ return new LogicalApply<>(apply.getCorrelationSlot(), apply.getSubqueryExpr(),
+ apply.getCorrelationFilter(), apply.left(), newAgg);
+ }).toRule(RuleType.APPLY_PULL_FILTER_ON_PROJECT_UNDER_AGG);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
new file mode 100644
index 0000000000..5f02210d95
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
@@ -0,0 +1,120 @@
+// 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.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Exists;
+import org.apache.doris.nereids.trees.expressions.functions.Count;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Convert Existsapply to LogicalJoin.
+ *
+ * Exists
+ * Correlated -> LEFT_SEMI_JOIN
+ * apply LEFT_SEMI_JOIN(Correlated Predicate)
+ * / \ --> / \
+ * input queryPlan input queryPlan
+ *
+ * UnCorrelated -> CROSS_JOIN(limit(1))
+ * apply CROSS_JOIN
+ * / \ --> / \
+ * input queryPlan input limit(1)
+ * |
+ * queryPlan
+ *
+ * Not Exists
+ * Correlated -> LEFT_ANTI_JOIN
+ * apply LEFT_ANTI_JOIN(Correlated Predicate)
+ * / \ --> / \
+ * input queryPlan input queryPlan
+ *
+ * UnCorrelated -> CROSS_JOIN(Count(*))
+ * Filter(count(*) = 0)
+ * |
+ * apply Cross_Join
+ * / \ --> / \
+ * input queryPlan input agg(output:count(*))
+ * |
+ * limit(1)
+ * |
+ * queryPlan
+ */
+public class ExistsApplyToJoin extends OneRewriteRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalApply().when(LogicalApply::isExist).then(apply -> {
+ if (apply.isCorrelated()) {
+ return correlatedToJoin(apply);
+ } else {
+ return unCorrelatedToJoin(apply);
+ }
+ }).toRule(RuleType.EXISTS_APPLY_TO_JOIN);
+ }
+
+ private Plan correlatedToJoin(LogicalApply apply) {
+ if (((Exists) apply.getSubqueryExpr()).isNot()) {
+ return new LogicalJoin<>(JoinType.LEFT_ANTI_JOIN, Lists.newArrayList(), apply.getCorrelationFilter(),
+ (LogicalPlan) apply.left(), (LogicalPlan) apply.right());
+ } else {
+ return new LogicalJoin<>(JoinType.LEFT_SEMI_JOIN, Lists.newArrayList(), apply.getCorrelationFilter(),
+ (LogicalPlan) apply.left(), (LogicalPlan) apply.right());
+ }
+ }
+
+ private Plan unCorrelatedToJoin(LogicalApply unapply) {
+ if (((Exists) unapply.getSubqueryExpr()).isNot()) {
+ return unCorrelatedNotExist(unapply);
+ } else {
+ return unCorrelatedExist(unapply);
+ }
+ }
+
+ private Plan unCorrelatedNotExist(LogicalApply unapply) {
+ LogicalLimit newLimit = new LogicalLimit<>(1, 0, (LogicalPlan) unapply.right());
+ Alias alias = new Alias(new Count(), "count(*)");
+ LogicalAggregate newAgg = new LogicalAggregate<>(new ArrayList<>(),
+ ImmutableList.of(alias), newLimit);
+ LogicalJoin newJoin = new LogicalJoin<>(JoinType.CROSS_JOIN,
+ (LogicalPlan) unapply.left(), newAgg);
+ return new LogicalFilter<>(new EqualTo(newAgg.getOutput().get(0),
+ new IntegerLiteral(0)), newJoin);
+ }
+
+ private Plan unCorrelatedExist(LogicalApply unapply) {
+ LogicalLimit newLimit = new LogicalLimit<>(1, 0, (LogicalPlan) unapply.right());
+ return new LogicalJoin<>(JoinType.CROSS_JOIN, (LogicalPlan) unapply.left(), newLimit);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InApplyToJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InApplyToJoin.java
new file mode 100644
index 0000000000..baf459c983
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InApplyToJoin.java
@@ -0,0 +1,68 @@
+// 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.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.InSubquery;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.util.ExpressionUtils;
+
+import com.google.common.collect.Lists;
+
+import java.util.Optional;
+
+/**
+ * Convert InApply to LogicalJoin.
+ *
+ * Not In -> LEFT_ANTI_JOIN
+ * In -> LEFT_SEMI_JOIN
+ */
+public class InApplyToJoin extends OneRewriteRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalApply().when(LogicalApply::isIn).then(apply -> {
+ Expression predicate;
+ if (apply.isCorrelated()) {
+ predicate = ExpressionUtils.and(
+ new EqualTo(((InSubquery) apply.getSubqueryExpr()).getCompareExpr(),
+ apply.right().getOutput().get(0)),
+ apply.getCorrelationFilter().get());
+ } else {
+ predicate = new EqualTo(((InSubquery) apply.getSubqueryExpr()).getCompareExpr(),
+ apply.right().getOutput().get(0));
+ }
+
+ LogicalJoin newJoin;
+ if (((InSubquery) apply.getSubqueryExpr()).isNot()) {
+ newJoin = new LogicalJoin<>(JoinType.LEFT_ANTI_JOIN, Lists.newArrayList(), Optional.of(predicate),
+ apply.left(), apply.right());
+ } else {
+ newJoin = new LogicalJoin<>(JoinType.LEFT_SEMI_JOIN, Lists.newArrayList(), Optional.of(predicate),
+ apply.left(), apply.right());
+ }
+ return new LogicalProject(apply.left().getOutput(), newJoin);
+ }).toRule(RuleType.IN_APPLY_TO_JOIN);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderFilter.java
new file mode 100644
index 0000000000..ed9b565090
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderFilter.java
@@ -0,0 +1,79 @@
+// 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.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.nereids.util.Utils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Exchange apply and filter.
+ * Keep the non correlated predicate in apply and raise the correlated predicate.
+ *
+ * before:
+ * apply
+ * / \
+ * Input(output:b) Filter(Correlated predicate/UnCorrelated predicate)
+ *
+ * after:
+ * Filter(Correlated predicate)
+ * |
+ * apply
+ * / \
+ * Input(output:b) Filter(UnCorrelated predicate)
+ */
+public class PushApplyUnderFilter extends OneRewriteRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalApply(group(), logicalFilter()).when(LogicalApply::isCorrelated).then(apply -> {
+ LogicalFilter<GroupPlan> filter = apply.right();
+ List<Expression> predicates = ExpressionUtils.extractConjunction(filter.getPredicates());
+ Map<Boolean, List<Expression>> split = Utils.splitCorrelatedConjuncts(
+ predicates, apply.getCorrelationSlot());
+ List<Expression> correlatedPredicate = split.get(true);
+ List<Expression> unCorrelatedPredicate = split.get(false);
+
+ // the representative has experienced the rule and added the correlated predicate to the apply node
+ if (correlatedPredicate.isEmpty()) {
+ return apply;
+ }
+
+ if (unCorrelatedPredicate.isEmpty()) {
+ return new LogicalApply<>(apply.getCorrelationSlot(), apply.getSubqueryExpr(),
+ Optional.ofNullable(ExpressionUtils.and(correlatedPredicate)),
+ apply.left(), filter.child());
+ } else {
+ LogicalFilter<GroupPlan> newFilter = new LogicalFilter<>(
+ ExpressionUtils.and(unCorrelatedPredicate), filter.child());
+ return new LogicalApply<>(apply.getCorrelationSlot(), apply.getSubqueryExpr(),
+ Optional.ofNullable(ExpressionUtils.and(correlatedPredicate)),
+ apply.left(), newFilter);
+ }
+ }).toRule(RuleType.PUSH_APPLY_UNDER_FILTER);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderProject.java
new file mode 100644
index 0000000000..faccf00998
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderProject.java
@@ -0,0 +1,64 @@
+// 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.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.ScalarSubquery;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adjust the order of Project and apply in correlated subqueries.
+ *
+ * before:
+ * apply
+ * / \
+ * Input(output:b) Project(output:a)
+ * |
+ * child
+ *
+ * after:
+ * Project(b,(if the Subquery is Scalar add 'a' as the output column))
+ * |
+ * apply
+ * / \
+ * Input(output:b) child
+ */
+public class PushApplyUnderProject extends OneRewriteRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalApply(group(), logicalProject()).when(LogicalApply::isCorrelated).then(apply -> {
+ LogicalProject<GroupPlan> project = apply.right();
+ LogicalApply newCorrelate = new LogicalApply<>(apply.getCorrelationSlot(), apply.getSubqueryExpr(),
+ apply.getCorrelationFilter(), apply.left(), project.child());
+ List<Slot> newSlots = new ArrayList<>();
+ newSlots.addAll(apply.left().getOutput());
+ if (apply.getSubqueryExpr() instanceof ScalarSubquery) {
+ newSlots.add(apply.right().getOutput().get(0));
+ }
+ return new LogicalProject(newSlots, newCorrelate);
+ }).toRule(RuleType.PUSH_APPLY_UNDER_PROJECT);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ScalarApplyToJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ScalarApplyToJoin.java
new file mode 100644
index 0000000000..944f9c89a8
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ScalarApplyToJoin.java
@@ -0,0 +1,74 @@
+// 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.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAssertNumRows;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Convert scalarApply to LogicalJoin.
+ *
+ * UnCorrelated -> CROSS_JOIN
+ * Correlated -> LEFT_OUTER_JOIN
+ */
+public class ScalarApplyToJoin extends OneRewriteRuleFactory {
+ @Override
+ public Rule build() {
+ return logicalApply().when(LogicalApply::isScalar).then(apply -> {
+ if (apply.isCorrelated()) {
+ return correlatedToJoin(apply);
+ } else {
+ return unCorrelatedToJoin(apply);
+ }
+ }).toRule(RuleType.SCALAR_APPLY_TO_JOIN);
+ }
+
+ private Plan unCorrelatedToJoin(LogicalApply apply) {
+ LogicalAssertNumRows assertNumRows = new LogicalAssertNumRows(
+ new AssertNumRowsElement(
+ 1, apply.getSubqueryExpr().toString(),
+ AssertNumRowsElement.Assertion.EQ),
+ (LogicalPlan) apply.right());
+ LogicalJoin newJoin = new LogicalJoin<>(JoinType.CROSS_JOIN,
+ (LogicalPlan) apply.left(), assertNumRows);
+ List<Slot> projects = ((LogicalPlan) apply.left()).getOutput();
+ return new LogicalProject(projects, newJoin);
+ }
+
+ private Plan correlatedToJoin(LogicalApply apply) {
+ return new LogicalJoin<>(JoinType.LEFT_OUTER_JOIN,
+ Lists.newArrayList(),
+ apply.getCorrelationFilter(),
+ (LogicalPlan) apply.left(),
+ (LogicalPlan) apply.right());
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java
index 2e315c5604..a7c47e6ce6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/JoinEstimation.java
@@ -18,6 +18,7 @@
package org.apache.doris.nereids.stats;
import org.apache.doris.common.CheckedMath;
+import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
@@ -69,6 +70,13 @@ public class JoinEstimation {
return statsDeriveResult;
}
+ private static Expression removeCast(Expression parent) {
+ if (parent instanceof Cast) {
+ return removeCast(((Cast) parent).child());
+ }
+ return parent;
+ }
+
// TODO: If the condition of Join Plan could any expression in addition to EqualTo type,
// we should handle that properly.
private static long getSemiJoinRowCount(StatsDeriveResult leftStats, StatsDeriveResult rightStats,
@@ -89,9 +97,9 @@ public class JoinEstimation {
Map<Slot, ColumnStats> rightSlotToColStats = rightStats.getSlotToColumnStats();
double minSelectivity = 1.0;
for (Expression eqJoinPredicate : eqConjunctList) {
- long lhsNdv = leftSlotToColStats.get(eqJoinPredicate.child(0)).getNdv();
+ long lhsNdv = leftSlotToColStats.get(removeCast(eqJoinPredicate.child(0))).getNdv();
lhsNdv = Math.min(lhsNdv, leftStats.getRowCount());
- long rhsNdv = rightSlotToColStats.get(eqJoinPredicate.child(1)).getNdv();
+ long rhsNdv = rightSlotToColStats.get(removeCast(eqJoinPredicate.child(1))).getNdv();
rhsNdv = Math.min(rhsNdv, rightStats.getRowCount());
// Skip conjuncts with unknown NDV on either side.
if (lhsNdv == -1 || rhsNdv == -1) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
index 50052a959b..85f480bec5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
@@ -34,6 +34,7 @@ import org.apache.doris.nereids.trees.plans.algebra.Project;
import org.apache.doris.nereids.trees.plans.algebra.Scan;
import org.apache.doris.nereids.trees.plans.algebra.TopN;
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAssertNumRows;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
@@ -42,6 +43,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
import org.apache.doris.nereids.trees.plans.logical.LogicalTopN;
import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribution;
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
@@ -140,6 +142,11 @@ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void>
groupExpression.getCopyOfChildStats(1), join);
}
+ @Override
+ public StatsDeriveResult visitLogicalAssertNumRows(LogicalAssertNumRows<Plan> assertNumRows, Void context) {
+ return groupExpression.getCopyOfChildStats(0);
+ }
+
@Override
public StatsDeriveResult visitPhysicalAggregate(PhysicalAggregate<Plan> agg, Void context) {
return computeAggregate(agg);
@@ -190,6 +197,12 @@ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void>
return groupExpression.getCopyOfChildStats(0);
}
+ @Override
+ public StatsDeriveResult visitPhysicalAssertNumRows(PhysicalAssertNumRows<Plan> assertNumRows,
+ Void context) {
+ return groupExpression.getCopyOfChildStats(0);
+ }
+
private StatsDeriveResult computeFilter(Filter filter) {
StatsDeriveResult stats = groupExpression.getCopyOfChildStats(0);
FilterSelectivityCalculator selectivityCalculator =
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
index a84955b287..d9f8735103 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
@@ -49,7 +49,7 @@ public class Alias extends NamedExpression implements UnaryExpression {
}
@VisibleForTesting
- Alias(ExprId exprId, Expression child, String name) {
+ public Alias(ExprId exprId, Expression child, String name) {
super(child);
this.exprId = exprId;
this.name = name;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/AssertNumRowsElement.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/AssertNumRowsElement.java
new file mode 100644
index 0000000000..60ae3a2e35
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/AssertNumRowsElement.java
@@ -0,0 +1,109 @@
+// 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.doris.nereids.trees.expressions;
+
+import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Number of rows returned by inspection in subquery.
+ */
+public class AssertNumRowsElement extends Expression implements LeafExpression {
+ /**
+ * Assertion type.
+ */
+ public enum Assertion {
+ EQ, // val1 == val2
+ NE, // val1 != val2
+ LT, // val1 < val2
+ LE, // val1 <= val2
+ GT, // val1 > val2
+ GE; // val1 >= val2
+ }
+
+ private final long desiredNumOfRows;
+ private final String subqueryString;
+ private final Assertion assertion;
+
+ public AssertNumRowsElement(long desiredNumOfRows, String subqueryString,
+ Assertion assertion) {
+ this.desiredNumOfRows = desiredNumOfRows;
+ this.subqueryString = subqueryString;
+ this.assertion = assertion;
+ }
+
+ public long getDesiredNumOfRows() {
+ return desiredNumOfRows;
+ }
+
+ public String getSubqueryString() {
+ return subqueryString;
+ }
+
+ public Assertion getAssertion() {
+ return assertion;
+ }
+
+ @Override
+ public AssertNumRowsElement withChildren(List<Expression> children) {
+ Preconditions.checkArgument(children.size() == 0);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return Utils.toSqlString("desiredNumOfRows: ",
+ Long.toString(desiredNumOfRows),
+ "assertion: " + assertion);
+ }
+
+ @Override
+ public String toSql() {
+ return toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AssertNumRowsElement that = (AssertNumRowsElement) o;
+ return Objects.equals(this.desiredNumOfRows, that.getDesiredNumOfRows())
+ && Objects.equals(this.subqueryString, that.getSubqueryString())
+ && Objects.equals(this.assertion, that.getAssertion());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(desiredNumOfRows, subqueryString, assertion);
+ }
+
+ @Override
+ public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+ return visitor.visitAssertNumRowsElement(this, context);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java
index 121da5beb7..d9cd7c695e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java
@@ -24,15 +24,30 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.types.BooleanType;
import org.apache.doris.nereids.types.DataType;
+import com.google.common.base.Preconditions;
+
+import java.util.List;
import java.util.Objects;
/**
* Exists subquery expression.
*/
public class Exists extends SubqueryExpr implements LeafExpression {
+ private final boolean isNot;
- public Exists(LogicalPlan subquery) {
+ public Exists(LogicalPlan subquery, boolean isNot) {
super(Objects.requireNonNull(subquery, "subquery can not be null"));
+ this.isNot = Objects.requireNonNull(isNot, "isNot can not be null");
+ }
+
+ public Exists(LogicalPlan subquery, List<Slot> correlateSlots, boolean isNot) {
+ super(Objects.requireNonNull(subquery, "subquery can not be null"),
+ Objects.requireNonNull(correlateSlots, "subquery can not be null"));
+ this.isNot = Objects.requireNonNull(isNot, "isNot can not be null");
+ }
+
+ public boolean isNot() {
+ return isNot;
}
@Override
@@ -53,4 +68,28 @@ public class Exists extends SubqueryExpr implements LeafExpression {
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitExistsSubquery(this, context);
}
+
+ @Override
+ public Expression withChildren(List<Expression> children) {
+ Preconditions.checkArgument(children.size() == 1);
+ return new Exists(((Exists) children.get(0)).getQueryPlan(), isNot);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Exists other = (Exists) o;
+ return Objects.equals(this.queryPlan, other.getQueryPlan())
+ && Objects.equals(this.isNot, other.isNot());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.queryPlan, this.isNot);
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java
index 39c655d66a..77423b165f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java
@@ -33,11 +33,21 @@ public class InSubquery extends SubqueryExpr {
private final Expression compareExpr;
private final ListQuery listQuery;
+ private final boolean isNot;
- public InSubquery(Expression compareExpression, ListQuery listQuery) {
+ public InSubquery(Expression compareExpression, ListQuery listQuery, boolean isNot) {
super(Objects.requireNonNull(listQuery.getQueryPlan(), "subquery can not be null"));
- this.compareExpr = Objects.requireNonNull(compareExpression);
- this.listQuery = listQuery;
+ this.compareExpr = Objects.requireNonNull(compareExpression, "compareExpr can not be null");
+ this.listQuery = Objects.requireNonNull(listQuery, "listQuery can not be null");
+ this.isNot = Objects.requireNonNull(isNot, "isNot can not be null");
+ }
+
+ public InSubquery(Expression compareExpr, ListQuery listQuery, List<Slot> correlateSlots, boolean isNot) {
+ super(Objects.requireNonNull(listQuery.getQueryPlan(), "subquery can not be null"),
+ Objects.requireNonNull(correlateSlots, "correlateSlots can not be null"));
+ this.compareExpr = Objects.requireNonNull(compareExpr, "compareExpr can not be null");
+ this.listQuery = Objects.requireNonNull(listQuery, "listQuery can not be null");
+ this.isNot = Objects.requireNonNull(isNot, "isNot can not be null");
}
@Override
@@ -52,12 +62,12 @@ public class InSubquery extends SubqueryExpr {
@Override
public String toSql() {
- return this.compareExpr.toSql() + "IN (SUBQUERY) " + super.toSql();
+ return this.compareExpr.toSql() + " IN (INSUBQUERY) " + super.toSql();
}
@Override
public String toString() {
- return this.compareExpr + "IN (SUBQUERY) " + super.toString();
+ return this.compareExpr + " IN (INSUBQUERY) " + super.toString();
}
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
@@ -68,11 +78,19 @@ public class InSubquery extends SubqueryExpr {
return this.compareExpr;
}
+ public boolean isNot() {
+ return isNot;
+ }
+
+ public ListQuery getListQuery() {
+ return listQuery;
+ }
+
@Override
public InSubquery withChildren(List<Expression> children) {
Preconditions.checkArgument(children.size() == 2);
Preconditions.checkArgument(children.get(1) instanceof ListQuery);
- return new InSubquery(children.get(0), (ListQuery) children.get(1));
+ return new InSubquery(children.get(0), (ListQuery) children.get(1), isNot);
}
@Override
@@ -85,11 +103,12 @@ public class InSubquery extends SubqueryExpr {
}
InSubquery inSubquery = (InSubquery) o;
return Objects.equals(this.compareExpr, inSubquery.getCompareExpr())
- && checkEquals(this.queryPlan, inSubquery.queryPlan);
+ && checkEquals(this.queryPlan, inSubquery.queryPlan)
+ && Objects.equals(this.isNot, inSubquery.isNot);
}
@Override
public int hashCode() {
- return Objects.hash(this.compareExpr, this.listQuery);
+ return Objects.hash(this.compareExpr, this.listQuery, this.isNot);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java
index 023195bcb2..961a46ff68 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java
@@ -17,12 +17,13 @@
package org.apache.doris.nereids.trees.expressions;
-import org.apache.doris.nereids.exceptions.UnboundException;
import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.types.DataType;
+import com.google.common.base.Preconditions;
+
import java.util.Objects;
/**
@@ -35,11 +36,9 @@ public class ListQuery extends SubqueryExpr implements LeafExpression {
}
@Override
- public DataType getDataType() throws UnboundException {
- // TODO:
- // For multiple lines, struct type is returned, in the form of splicing,
- // but it seems that struct type is not currently supported
- throw new UnboundException("not support");
+ public DataType getDataType() {
+ Preconditions.checkArgument(queryPlan.getOutput().size() == 1);
+ return queryPlan.getOutput().get(0).getDataType();
}
@Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java
index 8b664ce657..e49e514511 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.types.DataType;
import com.google.common.base.Preconditions;
+import java.util.List;
import java.util.Objects;
/**
@@ -35,6 +36,11 @@ public class ScalarSubquery extends SubqueryExpr implements LeafExpression {
super(Objects.requireNonNull(subquery, "subquery can not be null"));
}
+ public ScalarSubquery(LogicalPlan subquery, List<Slot> correlateSlots) {
+ super(Objects.requireNonNull(subquery, "subquery can not be null"),
+ Objects.requireNonNull(correlateSlots, "correlateSlots can not be null"));
+ }
+
@Override
public DataType getDataType() throws UnboundException {
Preconditions.checkArgument(queryPlan.getOutput().size() == 1);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
index cc77e75f7f..ec62dbe25e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
@@ -116,10 +116,19 @@ public class SlotReference extends Slot {
return false;
}
SlotReference that = (SlotReference) o;
+ // The equals of slotRefrance only compares exprId,
+ // because in subqueries with aliases,
+ // there will be scenarios where the same exprId but different qualifiers are used,
+ // resulting in an error due to different qualifiers during comparison.
+ // eg:
+ // select * from t6 where t6.k1 < (select max(aa) from (select v1 as aa from t7 where t6.k2=t7.v2) t2 )
+ //
+ // For aa, the qualifier of aa in the subquery is empty, but in the output column of agg,
+ // the qualifier of aa is t2. but both actually represent the same column.
return exprId.equals(that.exprId);
-
}
+ // The contains method needs to use hashCode, so similar to equals, it only compares exprId
@Override
public int hashCode() {
return Objects.hash(exprId);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java
index 836e13a064..37e3f4c0e5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java
@@ -22,7 +22,7 @@ import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.types.DataType;
-import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Objects;
@@ -31,10 +31,21 @@ import java.util.Objects;
* Subquery Expression.
*/
public abstract class SubqueryExpr extends Expression {
- protected LogicalPlan queryPlan;
+ protected final LogicalPlan queryPlan;
+ protected final List<Slot> correlateSlots;
public SubqueryExpr(LogicalPlan subquery) {
this.queryPlan = Objects.requireNonNull(subquery, "subquery can not be null");
+ this.correlateSlots = ImmutableList.of();
+ }
+
+ public SubqueryExpr(LogicalPlan subquery, List<Slot> correlateSlots) {
+ this.queryPlan = Objects.requireNonNull(subquery, "subquery can not be null");
+ this.correlateSlots = ImmutableList.copyOf(correlateSlots);
+ }
+
+ public List<Slot> getCorrelateSlots() {
+ return correlateSlots;
}
@Override
@@ -44,19 +55,18 @@ public abstract class SubqueryExpr extends Expression {
@Override
public boolean nullable() throws UnboundException {
- // TODO:
- // Any child is nullable, the whole is nullable
- throw new UnboundException("not support");
+ return true;
}
@Override
public String toSql() {
- return "(" + queryPlan.toString() + ")";
+ return "(" + queryPlan + ")";
}
@Override
public String toString() {
- return "(" + queryPlan.toString() + ")";
+ return "(QueryPlan: " + queryPlan
+ + "), (CorrelatedSlots: " + correlateSlots + ")";
}
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
@@ -67,12 +77,6 @@ public abstract class SubqueryExpr extends Expression {
return queryPlan;
}
- @Override
- public Expression withChildren(List<Expression> children) {
- Preconditions.checkArgument(children.size() == 1);
- return children.get(0);
- }
-
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -82,7 +86,8 @@ public abstract class SubqueryExpr extends Expression {
return false;
}
SubqueryExpr other = (SubqueryExpr) o;
- return checkEquals(queryPlan, other.queryPlan);
+ return checkEquals(queryPlan, other.queryPlan)
+ && Objects.equals(correlateSlots, other.correlateSlots);
}
/**
@@ -118,6 +123,10 @@ public abstract class SubqueryExpr extends Expression {
@Override
public int hashCode() {
- return Objects.hash(queryPlan);
+ return Objects.hash(queryPlan, correlateSlots);
+ }
+
+ public List<Slot> getOutput() {
+ return queryPlan.getOutput();
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/DefaultSubExprRewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/DefaultSubExprRewriter.java
deleted file mode 100644
index 919e9095db..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/DefaultSubExprRewriter.java
+++ /dev/null
@@ -1,76 +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.doris.nereids.trees.expressions.visitor;
-
-import org.apache.doris.nereids.CascadesContext;
-import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.memo.Memo;
-import org.apache.doris.nereids.rules.analysis.Scope;
-import org.apache.doris.nereids.trees.expressions.Exists;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.InSubquery;
-import org.apache.doris.nereids.trees.expressions.ListQuery;
-import org.apache.doris.nereids.trees.expressions.ScalarSubquery;
-import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
-import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
-
-import java.util.Optional;
-
-/**
- * Use the visitor to iterate sub expression.
- */
-public class DefaultSubExprRewriter<C> extends DefaultExpressionRewriter<C> {
- private final Scope scope;
- private final CascadesContext cascadesContext;
-
- public DefaultSubExprRewriter(Scope scope, CascadesContext cascadesContext) {
- this.scope = scope;
- this.cascadesContext = cascadesContext;
- }
-
- @Override
- public Expression visitExistsSubquery(Exists exists, C context) {
- return new Exists(analyzeSubquery(exists));
- }
-
- @Override
- public Expression visitInSubquery(InSubquery expr, C context) {
- return new InSubquery(expr.getCompareExpr(), new ListQuery(analyzeSubquery(expr)));
- }
-
- @Override
- public Expression visitScalarSubquery(ScalarSubquery scalar, C context) {
- LogicalPlan analyzed = analyzeSubquery(scalar);
- if (analyzed.getOutput().size() != 1) {
- throw new AnalysisException("Multiple columns returned by subquery are not yet supported. Found "
- + analyzed.getOutput().size());
- }
- return new ScalarSubquery(analyzed);
- }
-
- private LogicalPlan analyzeSubquery(SubqueryExpr expr) {
- CascadesContext subqueryContext = new Memo(expr.getQueryPlan())
- .newCascadesContext(cascadesContext.getStatementContext());
- subqueryContext.newAnalyzer(Optional.ofNullable(getScope())).analyze();
- return (LogicalPlan) subqueryContext.getMemo().copyOut();
- }
-
- public Scope getScope() {
- return scope;
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
index d3f24956fb..08904725e5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
@@ -24,6 +24,7 @@ import org.apache.doris.nereids.analyzer.UnboundStar;
import org.apache.doris.nereids.trees.expressions.Add;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
import org.apache.doris.nereids.trees.expressions.Between;
import org.apache.doris.nereids.trees.expressions.BinaryArithmetic;
import org.apache.doris.nereids.trees.expressions.BinaryOperator;
@@ -300,6 +301,10 @@ public abstract class ExpressionVisitor<R, C> {
return visitSubqueryExpr(listQuery, context);
}
+ public R visitAssertNumRowsElement(AssertNumRowsElement assertNumRowsElement, C context) {
+ return visit(assertNumRowsElement, context);
+ }
+
/* ********************************************************************************************
* Unbound expressions
* ********************************************************************************************/
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
index 8361cfeeac..742d16dc28 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
@@ -36,9 +36,8 @@ public enum PlanType {
LOGICAL_LIMIT,
LOGICAL_OLAP_SCAN,
LOGICAL_APPLY,
- LOGICAL_CORRELATED_JOIN,
- LOGICAL_ENFORCE_SINGLE_ROW,
LOGICAL_SELECT_HINT,
+ LOGICAL_ASSERT_NUM_ROWS,
GROUP_PLAN,
// physical plan
@@ -53,5 +52,6 @@ public enum PlanType {
PHYSICAL_HASH_JOIN,
PHYSICAL_NESTED_LOOP_JOIN,
PHYSICAL_EXCHANGE,
- PHYSICAL_DISTRIBUTION;
+ PHYSICAL_DISTRIBUTION,
+ PHYSICAL_ASSERT_NUM_ROWS;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalApply.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalApply.java
index df9aaeeb34..bdc2e50580 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalApply.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalApply.java
@@ -19,8 +19,12 @@ package org.apache.doris.nereids.trees.plans.logical;
import org.apache.doris.nereids.memo.GroupExpression;
import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Exists;
import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.InSubquery;
+import org.apache.doris.nereids.trees.expressions.ScalarSubquery;
import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
@@ -34,43 +38,80 @@ import java.util.Objects;
import java.util.Optional;
/**
- * Apply Node for subquery.
* Use this node to display the subquery in the relational algebra tree.
- * refer to "Orthogonal Optimization of Subqueries and Aggregation"
- *
- * @param <LEFT_CHILD_TYPE> input
- * @param <RIGHT_CHILD_TYPE> subquery
+ * @param <LEFT_CHILD_TYPE> input.
+ * @param <RIGHT_CHILD_TYPE> subquery.
*/
public class LogicalApply<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
extends LogicalBinary<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
- // correlation : subqueries and outer associated columns
- private final List<Expression> correlation;
+ // correlation column
+ private final List<Expression> correlationSlot;
+ // original subquery
+ private final SubqueryExpr subqueryExpr;
+ // correlation Conjunction
+ private final Optional<Expression> correlationFilter;
+
+ /**
+ * Constructor.
+ */
+ public LogicalApply(Optional<GroupExpression> groupExpression,
+ Optional<LogicalProperties> logicalProperties,
+ List<Expression> correlationSlot,
+ SubqueryExpr subqueryExpr, Optional<Expression> correlationFilter,
+ LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+ super(PlanType.LOGICAL_APPLY, groupExpression, logicalProperties, leftChild, rightChild);
+ this.correlationSlot = correlationSlot == null ? ImmutableList.of() : ImmutableList.copyOf(correlationSlot);
+ this.subqueryExpr = Objects.requireNonNull(subqueryExpr, "subquery can not be null");
+ this.correlationFilter = correlationFilter;
+ }
- public LogicalApply(LEFT_CHILD_TYPE input, RIGHT_CHILD_TYPE subquery, List<Expression> correlation) {
- this(Optional.empty(), Optional.empty(), input, subquery, correlation);
+ public LogicalApply(List<Expression> correlationSlot, SubqueryExpr subqueryExpr,
+ Optional<Expression> correlationFilter,
+ LEFT_CHILD_TYPE input, RIGHT_CHILD_TYPE subquery) {
+ this(Optional.empty(), Optional.empty(), correlationSlot, subqueryExpr, correlationFilter,
+ input, subquery);
}
- public LogicalApply(Optional<GroupExpression> groupExpression, Optional<LogicalProperties> logicalProperties,
- LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild, List<Expression> correlation) {
- super(PlanType.LOGICAL_APPLY, groupExpression, logicalProperties, leftChild, rightChild);
- this.correlation = ImmutableList.copyOf(correlation);
+ public List<Expression> getCorrelationSlot() {
+ return correlationSlot;
+ }
+
+ public Optional<Expression> getCorrelationFilter() {
+ return correlationFilter;
+ }
+
+ public SubqueryExpr getSubqueryExpr() {
+ return subqueryExpr;
}
- public List<Expression> getCorrelation() {
- return correlation;
+ public boolean isScalar() {
+ return this.subqueryExpr instanceof ScalarSubquery;
+ }
+
+ public boolean isIn() {
+ return this.subqueryExpr instanceof InSubquery;
+ }
+
+ public boolean isExist() {
+ return this.subqueryExpr instanceof Exists;
+ }
+
+ public boolean isCorrelated() {
+ return !correlationSlot.isEmpty();
}
@Override
public List<Slot> computeOutput() {
return ImmutableList.<Slot>builder()
.addAll(left().getOutput())
- .addAll(right().getOutput())
.build();
}
@Override
public String toString() {
- return Utils.toSqlString("LogicalApply");
+ return Utils.toSqlString("LogicalApply",
+ "correlationSlot", correlationSlot,
+ "correlationFilter", correlationFilter);
}
@Override
@@ -82,12 +123,15 @@ public class LogicalApply<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends
return false;
}
LogicalApply that = (LogicalApply) o;
- return Objects.equals(correlation, that.getCorrelation());
+ return Objects.equals(correlationSlot, that.getCorrelationSlot())
+ && Objects.equals(subqueryExpr, that.getSubqueryExpr())
+ && Objects.equals(correlationFilter, that.getCorrelationFilter());
}
@Override
public int hashCode() {
- return Objects.hash(correlation);
+ return Objects.hash(
+ correlationSlot, subqueryExpr, correlationFilter);
}
@Override
@@ -97,22 +141,33 @@ public class LogicalApply<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends
@Override
public List<Expression> getExpressions() {
- return correlation;
+ if (correlationFilter.isPresent()) {
+ return new ImmutableList.Builder<Expression>()
+ .addAll(correlationSlot)
+ .add(correlationFilter.get())
+ .build();
+ }
+ return new ImmutableList.Builder<Expression>()
+ .addAll(correlationSlot)
+ .build();
}
@Override
public LogicalBinary<Plan, Plan> withChildren(List<Plan> children) {
Preconditions.checkArgument(children.size() == 2);
- return new LogicalApply<>(children.get(0), children.get(1), correlation);
+ return new LogicalApply<>(correlationSlot, subqueryExpr, correlationFilter,
+ children.get(0), children.get(1));
}
@Override
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
- return new LogicalApply<>(groupExpression, Optional.of(logicalProperties), left(), right(), correlation);
+ return new LogicalApply<>(groupExpression, Optional.of(logicalProperties),
+ correlationSlot, subqueryExpr, correlationFilter, left(), right());
}
@Override
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
- return new LogicalApply<>(Optional.empty(), logicalProperties, left(), right(), correlation);
+ return new LogicalApply<>(Optional.empty(), logicalProperties,
+ correlationSlot, subqueryExpr, correlationFilter, left(), right());
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEnforceSingleRow.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java
similarity index 52%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEnforceSingleRow.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java
index 4ab50449d3..9a43fb37e8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEnforceSingleRow.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java
@@ -19,48 +19,47 @@ package org.apache.doris.nereids.trees.plans.logical;
import org.apache.doris.nereids.memo.GroupExpression;
import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
-import org.apache.doris.nereids.util.Utils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
/**
- * Guaranteed to return a result of 1 row.
- * eg: select * from t1 where t1.a = (select sum(t2.b) from t2 where t1.b = t2.a);
- * filter()
- * +--enforceSingleRow()
- * +--apply()
- * @param <CHILD_TYPE> plan
+ * Assert num rows node is used to determine whether the number of rows is less than desired num of rows.
+ * The rows are the result of subqueryString.
+ * If the number of rows is more than the desired num of rows, the query will be cancelled.
+ * The cancelled reason will be reported by Backend and displayed back to the user.
*/
-public class LogicalEnforceSingleRow<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
+public class LogicalAssertNumRows<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
+ private final AssertNumRowsElement assertNumRowsElement;
- LogicalEnforceSingleRow(CHILD_TYPE plan) {
- this(Optional.empty(), Optional.empty(), plan);
+ public LogicalAssertNumRows(AssertNumRowsElement assertNumRowsElement, CHILD_TYPE child) {
+ this(assertNumRowsElement, Optional.empty(), Optional.empty(), child);
}
- LogicalEnforceSingleRow(Optional<GroupExpression> groupExpression, Optional<LogicalProperties> logicalProperties,
- CHILD_TYPE plan) {
- super(PlanType.LOGICAL_ENFORCE_SINGLE_ROW, groupExpression, logicalProperties, plan);
+ public LogicalAssertNumRows(AssertNumRowsElement assertNumRowsElement, Optional<GroupExpression> groupExpression,
+ Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
+ super(PlanType.LOGICAL_ASSERT_NUM_ROWS, groupExpression, logicalProperties, child);
+ this.assertNumRowsElement = Objects.requireNonNull(assertNumRowsElement,
+ "assertNumRowsElement can not be null");
}
- @Override
- public List<Slot> computeOutput() {
- return ImmutableList.<Slot>builder()
- .addAll(child().getOutput())
- .build();
+ public AssertNumRowsElement getAssertNumRowsElement() {
+ return assertNumRowsElement;
}
@Override
public String toString() {
- return Utils.toSqlString("LogicalEnforceSingleRow");
+ return "LogicalAssertNumRows (" + assertNumRowsElement + ")";
}
@Override
@@ -71,32 +70,46 @@ public class LogicalEnforceSingleRow<CHILD_TYPE extends Plan> extends LogicalUna
if (o == null || getClass() != o.getClass()) {
return false;
}
- return true;
+ LogicalAssertNumRows that = (LogicalAssertNumRows) o;
+ return assertNumRowsElement.equals(that.assertNumRowsElement);
}
@Override
- public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
- return visitor.visitLogicalEnforceSingleRow((LogicalEnforceSingleRow<Plan>) this, context);
+ public int hashCode() {
+ return Objects.hash(assertNumRowsElement);
}
@Override
- public Plan withChildren(List<Plan> children) {
- Preconditions.checkArgument(children.size() == 1);
- return new LogicalEnforceSingleRow<>(children.get(0));
+ public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+ return visitor.visitLogicalAssertNumRows((LogicalAssertNumRows<Plan>) this, context);
}
@Override
public List<Expression> getExpressions() {
- return ImmutableList.of();
+ return ImmutableList.of(assertNumRowsElement);
+ }
+
+ @Override
+ public LogicalUnary<Plan> withChildren(List<Plan> children) {
+ Preconditions.checkArgument(children.size() == 1);
+ return new LogicalAssertNumRows<>(assertNumRowsElement, children.get(0));
}
@Override
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
- return new LogicalEnforceSingleRow<>(groupExpression, Optional.of(logicalProperties), child());
+ return new LogicalAssertNumRows<>(assertNumRowsElement,
+ groupExpression, Optional.of(logicalProperties), child());
}
@Override
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
- return new LogicalEnforceSingleRow<>(Optional.empty(), logicalProperties, child());
+ return new LogicalAssertNumRows<>(assertNumRowsElement, Optional.empty(), logicalProperties, child());
+ }
+
+ @Override
+ public List<Slot> computeOutput() {
+ return ImmutableList.<Slot>builder()
+ .addAll(child().getOutput())
+ .build();
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCorrelatedJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCorrelatedJoin.java
deleted file mode 100644
index 93ed2b7083..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCorrelatedJoin.java
+++ /dev/null
@@ -1,136 +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.doris.nereids.trees.plans.logical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanType;
-import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
-import org.apache.doris.nereids.util.Utils;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * A relational algebra node with join type converted from apply node to subquery.
- *
- * @param <LEFT_CHILD_TYPE> input.
- * @param <RIGHT_CHILD_TYPE> subquery.
- */
-public class LogicalCorrelatedJoin<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
- extends LogicalBinary<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
-
- private final List<Expression> correlation;
- private final Optional<Expression> filter;
-
- public LogicalCorrelatedJoin(LEFT_CHILD_TYPE input, RIGHT_CHILD_TYPE subquery, List<Expression> correlation,
- Optional<Expression> filter) {
- this(Optional.empty(), Optional.empty(), input, subquery,
- correlation, filter);
- }
-
- public LogicalCorrelatedJoin(Optional<GroupExpression> groupExpression,
- Optional<LogicalProperties> logicalProperties,
- LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild, List<Expression> correlation,
- Optional<Expression> filter) {
- super(PlanType.LOGICAL_CORRELATED_JOIN, groupExpression, logicalProperties, leftChild, rightChild);
- this.correlation = ImmutableList.copyOf(correlation);
- this.filter = Objects.requireNonNull(filter, "filter can not be null");
- }
-
- public List<Expression> getCorrelation() {
- return correlation;
- }
-
- public Optional<Expression> getFilter() {
- return filter;
- }
-
- @Override
- public List<Slot> computeOutput() {
- return ImmutableList.<Slot>builder()
- .addAll(left().getOutput())
- .addAll(right().getOutput())
- .build();
- }
-
- @Override
- public String toString() {
- return Utils.toSqlString("LogicalCorrelated",
- "correlation", correlation,
- "filter", filter
- );
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- LogicalCorrelatedJoin that = (LogicalCorrelatedJoin) o;
- return Objects.equals(correlation, that.getCorrelation())
- && Objects.equals(filter, that.getFilter());
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(correlation, filter);
- }
-
- @Override
- public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
- return visitor.visitLogicalCorrelated((LogicalCorrelatedJoin<Plan, Plan>) this, context);
- }
-
- @Override
- public List<Expression> getExpressions() {
- return new ImmutableList.Builder<Expression>()
- .addAll(correlation)
- .add(filter.get())
- .build();
- }
-
- @Override
- public LogicalBinary<Plan, Plan> withChildren(List<Plan> children) {
- Preconditions.checkArgument(children.size() == 2);
- return new LogicalCorrelatedJoin<>(children.get(0), children.get(1),
- correlation, filter);
- }
-
- @Override
- public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
- return new LogicalCorrelatedJoin<>(groupExpression, Optional.of(logicalProperties),
- left(), right(), correlation, filter);
- }
-
- @Override
- public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
- return new LogicalCorrelatedJoin<>(Optional.empty(), logicalProperties,
- left(), right(), correlation, filter);
- }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEnforceSingleRow.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalAssertNumRows.java
similarity index 54%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEnforceSingleRow.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalAssertNumRows.java
index 4ab50449d3..6d06d08c8e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEnforceSingleRow.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalAssertNumRows.java
@@ -15,10 +15,11 @@
// specific language governing permissions and limitations
// under the License.
-package org.apache.doris.nereids.trees.plans.logical;
+package org.apache.doris.nereids.trees.plans.physical;
import org.apache.doris.nereids.memo.GroupExpression;
import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.plans.Plan;
@@ -30,25 +31,24 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
/**
- * Guaranteed to return a result of 1 row.
- * eg: select * from t1 where t1.a = (select sum(t2.b) from t2 where t1.b = t2.a);
- * filter()
- * +--enforceSingleRow()
- * +--apply()
- * @param <CHILD_TYPE> plan
+ * Physical assertNumRows.
*/
-public class LogicalEnforceSingleRow<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
+public class PhysicalAssertNumRows<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD_TYPE> {
+ private final AssertNumRowsElement assertNumRowsElement;
- LogicalEnforceSingleRow(CHILD_TYPE plan) {
- this(Optional.empty(), Optional.empty(), plan);
+ public PhysicalAssertNumRows(AssertNumRowsElement assertNumRowsElement,
+ LogicalProperties logicalProperties, CHILD_TYPE child) {
+ this(assertNumRowsElement, Optional.empty(), logicalProperties, child);
}
- LogicalEnforceSingleRow(Optional<GroupExpression> groupExpression, Optional<LogicalProperties> logicalProperties,
- CHILD_TYPE plan) {
- super(PlanType.LOGICAL_ENFORCE_SINGLE_ROW, groupExpression, logicalProperties, plan);
+ public PhysicalAssertNumRows(AssertNumRowsElement assertNumRowsElement, Optional<GroupExpression> groupExpression,
+ LogicalProperties logicalProperties, CHILD_TYPE child) {
+ super(PlanType.PHYSICAL_ASSERT_NUM_ROWS, groupExpression, logicalProperties, child);
+ this.assertNumRowsElement = assertNumRowsElement;
}
@Override
@@ -58,9 +58,14 @@ public class LogicalEnforceSingleRow<CHILD_TYPE extends Plan> extends LogicalUna
.build();
}
+ public AssertNumRowsElement getAssertNumRowsElement() {
+ return assertNumRowsElement;
+ }
+
@Override
public String toString() {
- return Utils.toSqlString("LogicalEnforceSingleRow");
+ return Utils.toSqlString("PhysicalAssertNumRows",
+ "assertNumRowsElement", assertNumRowsElement);
}
@Override
@@ -71,32 +76,39 @@ public class LogicalEnforceSingleRow<CHILD_TYPE extends Plan> extends LogicalUna
if (o == null || getClass() != o.getClass()) {
return false;
}
- return true;
+
+ PhysicalAssertNumRows that = (PhysicalAssertNumRows) o;
+ return assertNumRowsElement.equals(that.assertNumRowsElement);
}
@Override
- public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
- return visitor.visitLogicalEnforceSingleRow((LogicalEnforceSingleRow<Plan>) this, context);
+ public int hashCode() {
+ return Objects.hash(assertNumRowsElement);
}
@Override
- public Plan withChildren(List<Plan> children) {
- Preconditions.checkArgument(children.size() == 1);
- return new LogicalEnforceSingleRow<>(children.get(0));
+ public List<Expression> getExpressions() {
+ return ImmutableList.of(assertNumRowsElement);
}
@Override
- public List<Expression> getExpressions() {
- return ImmutableList.of();
+ public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+ return visitor.visitPhysicalAssertNumRows((PhysicalAssertNumRows<Plan>) this, context);
+ }
+
+ @Override
+ public PhysicalUnary<Plan> withChildren(List<Plan> children) {
+ Preconditions.checkArgument(children.size() == 1);
+ return new PhysicalAssertNumRows<>(assertNumRowsElement, logicalProperties, children.get(0));
}
@Override
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
- return new LogicalEnforceSingleRow<>(groupExpression, Optional.of(logicalProperties), child());
+ return new PhysicalAssertNumRows<>(assertNumRowsElement, groupExpression, logicalProperties, child());
}
@Override
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
- return new LogicalEnforceSingleRow<>(Optional.empty(), logicalProperties, child());
+ return new PhysicalAssertNumRows<>(assertNumRowsElement, Optional.empty(), logicalProperties.get(), child());
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
index cc5d914c1a..965fda7def 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
@@ -24,8 +24,7 @@ import org.apache.doris.nereids.trees.plans.commands.Command;
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
-import org.apache.doris.nereids.trees.plans.logical.LogicalCorrelatedJoin;
-import org.apache.doris.nereids.trees.plans.logical.LogicalEnforceSingleRow;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAssertNumRows;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
@@ -38,6 +37,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalSubQueryAlias;
import org.apache.doris.nereids.trees.plans.logical.LogicalTopN;
import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort;
import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribution;
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
@@ -132,12 +132,8 @@ public abstract class PlanVisitor<R, C> {
return visit(apply, context);
}
- public R visitLogicalCorrelated(LogicalCorrelatedJoin<Plan, Plan> correlatedJoin, C context) {
- return visit(correlatedJoin, context);
- }
-
- public R visitLogicalEnforceSingleRow(LogicalEnforceSingleRow<Plan> enforceSingleRow, C context) {
- return visit(enforceSingleRow, context);
+ public R visitLogicalAssertNumRows(LogicalAssertNumRows<Plan> assertNumRows, C context) {
+ return visit(assertNumRows, context);
}
// *******************************
@@ -191,4 +187,8 @@ public abstract class PlanVisitor<R, C> {
public R visitPhysicalDistribution(PhysicalDistribution<Plan> distribution, C context) {
return visit(distribution, context);
}
+
+ public R visitPhysicalAssertNumRows(PhysicalAssertNumRows<Plan> assertNumRows, C context) {
+ return visit(assertNumRows, context);
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
index 02033f4e28..049619562b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
@@ -17,15 +17,20 @@
package org.apache.doris.nereids.util;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
import org.apache.doris.nereids.trees.plans.Plan;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang3.StringUtils;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -146,4 +151,58 @@ public class Utils {
return stringBuilder.append(" )").toString();
}
+
+ /**
+ * See if there are correlated columns in a subquery expression.
+ */
+ public static boolean containCorrelatedSlot(List<Expression> correlatedSlots, Expression expr) {
+ if (correlatedSlots.isEmpty() || expr == null) {
+ return false;
+ }
+ if (expr instanceof SlotReference) {
+ return correlatedSlots.contains(expr);
+ }
+ for (Expression child : expr.children()) {
+ if (containCorrelatedSlot(correlatedSlots, child)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the correlated columns that belong to the subquery,
+ * that is, the correlated columns that can be resolved within the subquery.
+ * eg:
+ * select * from t1 where t1.a = (select sum(t2.b) from t2 where t1.c = t2.d));
+ * correlatedPredicates : t1.c = t2.d
+ * correlatedSlots : t1.c
+ * return t2.d
+ */
+ public static List<Expression> getCorrelatedSlots(List<Expression> correlatedPredicates,
+ List<Expression> correlatedSlots) {
+ List<Expression> slots = new ArrayList<>();
+ correlatedPredicates.stream().forEach(predicate -> {
+ if (!(predicate instanceof BinaryExpression)) {
+ throw new AnalysisException("UnSupported expr type: " + correlatedPredicates);
+ }
+ BinaryExpression binaryExpression = (BinaryExpression) predicate;
+ if (binaryExpression.left().anyMatch(correlatedSlots::contains)) {
+ if (binaryExpression.right() instanceof SlotReference) {
+ slots.add(binaryExpression.right());
+ }
+ } else {
+ if (binaryExpression.left() instanceof SlotReference) {
+ slots.add(binaryExpression.left());
+ }
+ }
+ });
+ return slots;
+ }
+
+ public static Map<Boolean, List<Expression>> splitCorrelatedConjuncts(
+ List<Expression> conjuncts, List<Expression> slots) {
+ return conjuncts.stream().collect(Collectors.partitioningBy(
+ expr -> expr.anyMatch(slots::contains)));
+ }
}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/SubqueryTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/SubqueryTest.java
index ef082e48c5..1d4053bb58 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/SubqueryTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/SubqueryTest.java
@@ -82,7 +82,7 @@ public class SubqueryTest extends AnalyzeCheckTestBase {
}
@Test
- public void scalarTest() {
+ public void filterScalarTest() {
// min will be rewritten as as, it needs to be bound
/*String sql = "select * from t0 where t0.id = "
+ "(select min(t1.id) from t1 where t0.k1 = t1.k1)";
@@ -94,19 +94,35 @@ public class SubqueryTest extends AnalyzeCheckTestBase {
assert sql1 != null;
String sql2 = "select * from t0 where t0.id = "
- + "(select t1.k1 from t1 where t0.k1 = t1.k1)";
+ + "(select sum(t1.k1) from t1 where t0.k1 = t1.k1)";
checkAnalyze(sql2);
String sql3 = "select * from t0 where t0.id = "
+ "(select * from t1 where t0.k1 = t1.k1)";
assert sql3 != null;
+
+ String sql4 = "select * from t0 where t0.id = "
+ + "(select sum(t1.k1) from t1 where t0.k1 = t1.k1) "
+ + "and t0.k2 = (select sum(t2.k1) from t2 where t0.v1 = t2.v2)";
+ checkAnalyze(sql4);
+
+ String sql5 = "select * from t0 where t0.id = "
+ + "(select sum(t1.k1) from t1 where t0.k1 = "
+ + "(select sum(t2.k1) from t2 where t1.id = t2.v2))";
+ checkAnalyze(sql5);
}
@Test
public void inScalarTest() {
String sql = "select * from t0 where t0.id in "
- + "(select * from t1 where t1.k1 = "
- + "(select t2.id from t2 where t0.id = t2.id));";
+ + "(select t1.k1 from t1 where t1.k1 = "
+ + "(select sum(t2.id) from t2 where t1.id = t2.id));";
checkAnalyze(sql);
+
+ String sql1 = "select * from t0 where t0.k1 in ("
+ + "select t1.k1 from t1 where "
+ + "t1.v2 in (select t2.k1 from t2 where t2.k1 = 3) "
+ + "and t1.v2 > (select sum(t2.k1) from t2 where t2.v2 = t1.id))";
+ checkAnalyze(sql1);
}
}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/AnalyzeWhereSubqueryTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/AnalyzeWhereSubqueryTest.java
new file mode 100644
index 0000000000..ba90de7c94
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/AnalyzeWhereSubqueryTest.java
@@ -0,0 +1,451 @@
+// 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.doris.nereids.util;
+
+import org.apache.doris.nereids.NereidsPlanner;
+import org.apache.doris.nereids.StatementContext;
+import org.apache.doris.nereids.glue.translator.PhysicalPlanTranslator;
+import org.apache.doris.nereids.glue.translator.PlanTranslatorContext;
+import org.apache.doris.nereids.parser.NereidsParser;
+import org.apache.doris.nereids.properties.PhysicalProperties;
+import org.apache.doris.nereids.rules.rewrite.logical.ApplyPullFilterOnAgg;
+import org.apache.doris.nereids.rules.rewrite.logical.ApplyPullFilterOnProjectUnderAgg;
+import org.apache.doris.nereids.rules.rewrite.logical.ExistsApplyToJoin;
+import org.apache.doris.nereids.rules.rewrite.logical.InApplyToJoin;
+import org.apache.doris.nereids.rules.rewrite.logical.PushApplyUnderFilter;
+import org.apache.doris.nereids.rules.rewrite.logical.PushApplyUnderProject;
+import org.apache.doris.nereids.rules.rewrite.logical.ScalarApplyToJoin;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.expressions.NamedExpressionUtil;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.Max;
+import org.apache.doris.nereids.trees.expressions.functions.Sum;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
+import org.apache.doris.nereids.types.BigIntType;
+import org.apache.doris.nereids.types.VarcharType;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Optional;
+
+public class AnalyzeWhereSubqueryTest extends TestWithFeService implements PatternMatchSupported {
+ private final NereidsParser parser = new NereidsParser();
+
+ // scalar
+ private final String sql1 = "select * from t6 where t6.k1 < (select sum(t7.k3) from t7 where t7.v2 = t6.k2)";
+ private final String sql2 = "select * from t6 where t6.k1 = (select sum(t7.k3) from t7 where t7.v2 = t6.k2)";
+ private final String sql3 = "select * from t6 where t6.k1 != (select sum(t7.k3) from t7 where t7.v2 = t6.k2)";
+ // in
+ private final String sql4 = "select * from t6 where t6.k1 in (select t7.k3 from t7 where t7.v2 = t6.k2)";
+ private final String sql5 = "select * from t6 where t6.k1 not in (select t7.k3 from t7 where t7.v2 = t6.k2)";
+ // exists
+ private final String sql6 = "select * from t6 where exists (select t7.k3 from t7 where t6.k2 = t7.v2)";
+ private final String sql7 = "select * from t6 where not exists (select t7.k3 from t7 where t6.k2 = t7.v2)";
+ // in (in and scalar)
+ private final String sql8 = "select * from t6 where t6.k1 in ("
+ + "select t7.k3 from t7 where "
+ + "t7.k3 in (select t8.k1 from t8 where t8.k1 = 3) "
+ + "and t7.v2 > (select sum(t9.k2) from t9 where t9.k2 = t7.v1))";
+ // exists and not exists
+ private final String sql9 = "select * from t6 where exists (select t7.k3 from t7 where t6.k2 = t7.v2) and not exists (select t8.k2 from t8 where t6.k2 = t8.k2)";
+ // with subquery alias
+ private final String sql10 = "select * from t6 where t6.k1 < (select max(aa) from (select v1 as aa from t7 where t6.k2=t7.v2) t2 )";
+ private final List<String> testSql = ImmutableList.of(
+ sql1, sql2, sql3, sql4, sql5, sql6, sql7, sql8, sql9, sql10
+
+ );
+
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("test");
+ connectContext.setDatabase("default_cluster:test");
+
+ createTables(
+ "create table test.t6\n"
+ + "(k1 bigint, k2 bigint)\n"
+ + "duplicate key(k1)\n"
+ + "distributed by hash(k2) buckets 1\n"
+ + "properties('replication_num' = '1');",
+ "create table test.t7\n"
+ + "(k1 bigint not null, k2 varchar(128), k3 bigint, v1 bigint, v2 bigint)\n"
+ + "distributed by hash(k2) buckets 1\n"
+ + "properties('replication_num' = '1');",
+ "create table test.t8\n"
+ + "(k1 int, k2 int)\n"
+ + "duplicate key(k1)\n"
+ + "distributed by hash(k2) buckets 1\n"
+ + "properties('replication_num' = '1');",
+ "create table test.t9\n"
+ + "(k1 varchar(40), k2 bigint)\n"
+ + "partition by range(k2)\n"
+ + "(partition p1 values less than(\"10\"))\n"
+ + "distributed by hash(k2) buckets 1\n"
+ + "properties('replication_num' = '1');");
+ }
+
+ @Override
+ protected void runBeforeEach() throws Exception {
+ NamedExpressionUtil.clear();
+ }
+
+ @Test
+ public void testTranslateCase() throws Exception {
+ for (String sql : testSql) {
+ NamedExpressionUtil.clear();
+ StatementContext statementContext = MemoTestUtils.createStatementContext(connectContext, sql);
+ PhysicalPlan plan = new NereidsPlanner(statementContext).plan(
+ parser.parseSingle(sql),
+ PhysicalProperties.ANY
+ );
+ // Just to check whether translate will throw exception
+ new PhysicalPlanTranslator().translatePlan(plan, new PlanTranslatorContext());
+ }
+ }
+
+ @Test
+ public void testWhereSql2AfterAnalyzed() {
+ //after analyze
+ PlanChecker.from(connectContext)
+ .analyze(sql2)
+ .matches(
+ logicalApply(
+ any(),
+ logicalAggregate(
+ logicalFilter()
+ ).when(FieldChecker.check("outputExpressions", ImmutableList.of(
+ new Alias(new ExprId(7),
+ new Sum(
+ new SlotReference(new ExprId(4), "k3",
+ BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))),
+ "sum(k3)"))))
+ ).when(FieldChecker.check("correlationSlot", ImmutableList.of(
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6"))
+ )))
+ );
+ }
+
+ @Test
+ public void testWhereSql2AfterAggFilterRule() {
+ //after aggFilter rule
+ PlanChecker.from(connectContext)
+ .analyze(sql2)
+ .applyBottomUp(new ApplyPullFilterOnAgg())
+ .matches(
+ logicalApply(
+ any(),
+ logicalAggregate().when(FieldChecker.check("outputExpressions", ImmutableList.of(
+ new Alias(new ExprId(7), new Sum(
+ new SlotReference(new ExprId(4), "k3", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))),
+ "sum(k3)"),
+ new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))
+ )))
+ .when(FieldChecker.check("groupByExpressions", ImmutableList.of(
+ new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))
+ )))
+ ).when(FieldChecker.check("correlationSlot", ImmutableList.of(
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6"))
+ )))
+ .when(FieldChecker.check("correlationFilter", Optional.of(
+ new EqualTo(new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6"))
+ ))))
+ );
+ }
+
+ @Test
+ public void testWhereSql2AfterScalarToJoin() {
+ //after Scalar CorrelatedJoin to join
+ PlanChecker.from(connectContext)
+ .analyze(sql2)
+ .applyBottomUp(new ApplyPullFilterOnAgg())
+ .applyBottomUp(new ScalarApplyToJoin())
+ .matches(
+ logicalJoin(
+ any(),
+ logicalAggregate()
+ ).when(FieldChecker.check("joinType", JoinType.LEFT_OUTER_JOIN))
+ .when(FieldChecker.check("otherJoinCondition",
+ Optional.of(new EqualTo(new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6"))))))
+ );
+ }
+
+ @Test
+ public void testInSql4AfterAnalyze() {
+ //"select * from t6 where t6.k1 in (select t7.k3 from t7 where t7.v2 = t6.k2)"
+ PlanChecker.from(connectContext)
+ .analyze(sql4)
+ .matches(
+ logicalApply(
+ any(),
+ logicalProject().when(FieldChecker.check("projects", ImmutableList.of(
+ new SlotReference(new ExprId(4), "k3", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))))
+ )
+ ).when(FieldChecker.check("correlationSlot", ImmutableList.of(
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")))))
+ );
+ }
+
+ @Test
+ public void testInSql4AfterPushProjectRule() {
+ PlanChecker.from(connectContext)
+ .analyze(sql4)
+ .applyBottomUp(new PushApplyUnderProject())
+ .matches(
+ logicalProject(
+ logicalApply().when(FieldChecker.check("correlationFilter", Optional.empty()))
+ .when(FieldChecker.check("correlationSlot", ImmutableList.of(
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")))))
+ ).when(FieldChecker.check("projects", ImmutableList.of(
+ new SlotReference(new ExprId(0), "k1", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")),
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")))))
+ );
+ }
+
+ @Test
+ public void testInSql4AfterPushFilterRule() {
+ PlanChecker.from(connectContext)
+ .analyze(sql4)
+ .applyBottomUp(new PushApplyUnderProject())
+ .applyBottomUp(new PushApplyUnderFilter())
+ .matches(
+ logicalApply().when(FieldChecker.check("correlationFilter", Optional.of(
+ new EqualTo(new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6"))))))
+ );
+ }
+
+ @Test
+ public void testInSql4AfterInToJoin() {
+ PlanChecker.from(connectContext)
+ .analyze(sql4)
+ .applyBottomUp(new PushApplyUnderProject())
+ .applyBottomUp(new PushApplyUnderFilter())
+ .applyBottomUp(new InApplyToJoin())
+ .matches(
+ logicalJoin().when(FieldChecker.check("joinType", JoinType.LEFT_SEMI_JOIN))
+ .when(FieldChecker.check("otherJoinCondition", Optional.of(
+ new And(new EqualTo(new SlotReference(new ExprId(0), "k1", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")),
+ new SlotReference(new ExprId(2), "k1", BigIntType.INSTANCE, false,
+ ImmutableList.of("default_cluster:test", "t7"))),
+ new EqualTo(new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6"))))
+ )))
+ );
+ }
+
+ @Test
+ public void testExistSql6AfterAnalyze() {
+ //"select * from t6 where exists (select t7.k3 from t7 where t6.k2 = t7.v2)"
+ PlanChecker.from(connectContext)
+ .analyze(sql6)
+ .matches(
+ logicalApply(
+ any(),
+ logicalProject().when(FieldChecker.check("projects", ImmutableList.of(
+ new SlotReference(new ExprId(4), "k3", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))))
+ )
+ ).when(FieldChecker.check("correlationSlot", ImmutableList.of(
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")))))
+ );
+ }
+
+ @Test
+ public void testExistSql6AfterPushProjectRule() {
+ PlanChecker.from(connectContext)
+ .analyze(sql6)
+ .applyBottomUp(new PushApplyUnderProject())
+ .matches(
+ logicalProject(
+ logicalApply().when(FieldChecker.check("correlationFilter", Optional.empty()))
+ .when(FieldChecker.check("correlationSlot", ImmutableList.of(
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")))))
+ ).when(FieldChecker.check("projects", ImmutableList.of(
+ new SlotReference(new ExprId(0), "k1", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")),
+ new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")))))
+ );
+ }
+
+ @Test
+ public void testExistSql6AfterPushFilterRule() {
+ PlanChecker.from(connectContext)
+ .analyze(sql6)
+ .applyBottomUp(new PushApplyUnderProject())
+ .applyBottomUp(new PushApplyUnderFilter())
+ .matches(
+ logicalApply().when(FieldChecker.check("correlationFilter", Optional.of(
+ new EqualTo(new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")),
+ new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))))))
+ );
+ }
+
+ @Test
+ public void testExistSql6AfterInToJoin() {
+ PlanChecker.from(connectContext)
+ .analyze(sql6)
+ .applyBottomUp(new PushApplyUnderProject())
+ .applyBottomUp(new PushApplyUnderFilter())
+ .applyBottomUp(new ExistsApplyToJoin())
+ .matches(
+ logicalJoin().when(FieldChecker.check("joinType", JoinType.LEFT_SEMI_JOIN))
+ .when(FieldChecker.check("otherJoinCondition", Optional.of(
+ new EqualTo(new SlotReference(new ExprId(1), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")),
+ new SlotReference(new ExprId(6), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")))
+ )))
+ );
+ }
+
+ @Test
+ public void testSql10AfterAnalyze() {
+ //select * from t6 where t6.k1 < (select max(aa) from (select v1 as aa from t7 where t6.k2=t7.v2) t2 )
+ PlanChecker.from(connectContext)
+ .analyze(sql10)
+ .matches(
+ logicalApply(
+ any(),
+ logicalAggregate(
+ logicalProject(
+ logicalFilter()
+ ).when(FieldChecker.check("projects", ImmutableList.of(
+ new Alias(new ExprId(0), new SlotReference(new ExprId(6), "v1", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")), "aa")
+ )))
+ ).when(FieldChecker.check("outputExpressions", ImmutableList.of(
+ new Alias(new ExprId(8), new Max(new SlotReference(new ExprId(0), "aa", BigIntType.INSTANCE, true,
+ ImmutableList.of("t2"))), "max(aa)")
+ )))
+ .when(FieldChecker.check("groupByExpressions", ImmutableList.of()))
+ ).when(FieldChecker.check("correlationSlot", ImmutableList.of(
+ new SlotReference(new ExprId(2), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")))))
+ );
+ }
+
+ @Test
+ public void testSql10AfterChangeProjectFilter() {
+ PlanChecker.from(connectContext)
+ .analyze(sql10)
+ .applyBottomUp(new ApplyPullFilterOnProjectUnderAgg())
+ .matches(
+ logicalApply(
+ any(),
+ logicalAggregate(
+ logicalFilter(
+ logicalProject().when(FieldChecker.check("projects", ImmutableList.of(
+ new Alias(new ExprId(0), new SlotReference(new ExprId(6), "v1", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")), "aa"),
+ new SlotReference(new ExprId(3), "k1", BigIntType.INSTANCE, false,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(4), "k2", new VarcharType(128), true,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(5), "k3", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(6), "v1", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")),
+ new SlotReference(new ExprId(7), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))
+ )))
+ )
+ )
+ )
+ );
+ }
+
+ @Test
+ public void testSql10AfterChangeAggFilter() {
+ PlanChecker.from(connectContext)
+ .analyze(sql10)
+ .applyBottomUp(new ApplyPullFilterOnProjectUnderAgg())
+ .applyBottomUp(new ApplyPullFilterOnAgg())
+ .matches(
+ logicalApply(
+ any(),
+ logicalAggregate(
+ logicalProject()
+ ).when(FieldChecker.check("outputExpressions", ImmutableList.of(
+ new Alias(new ExprId(8), new Max(new SlotReference(new ExprId(0), "aa", BigIntType.INSTANCE, true,
+ ImmutableList.of("t2"))), "max(aa)"),
+ new SlotReference(new ExprId(7), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")))))
+ .when(FieldChecker.check("groupByExpressions", ImmutableList.of(
+ new SlotReference(new ExprId(7), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7"))
+ )))
+ )
+ );
+ }
+
+ @Test
+ public void testSql10AfterScalarTOJoin() {
+ PlanChecker.from(connectContext)
+ .analyze(sql10)
+ .applyBottomUp(new ApplyPullFilterOnProjectUnderAgg())
+ .applyBottomUp(new ApplyPullFilterOnAgg())
+ .applyBottomUp(new ScalarApplyToJoin())
+ .matches(
+ logicalJoin(
+ any(),
+ logicalAggregate(
+ logicalProject()
+ )
+ ).when(FieldChecker.check("joinType", JoinType.LEFT_OUTER_JOIN))
+ .when(FieldChecker.check("otherJoinCondition", Optional.of(
+ new EqualTo(new SlotReference(new ExprId(2), "k2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t6")),
+ new SlotReference(new ExprId(7), "v2", BigIntType.INSTANCE, true,
+ ImmutableList.of("default_cluster:test", "t7")))
+ )))
+ );
+ }
+}
diff --git a/regression-test/suites/nereids_syntax_p0/sub_query_correlated.groovy b/regression-test/suites/nereids_syntax_p0/sub_query_correlated.groovy
new file mode 100644
index 0000000000..038c47c53d
--- /dev/null
+++ b/regression-test/suites/nereids_syntax_p0/sub_query_correlated.groovy
@@ -0,0 +1,243 @@
+// 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.
+
+suite ("sub_query_correlated") {
+ // enable nereids and vectorized engine
+ sql """
+ SET enable_vectorized_engine=true
+ """
+ sql """
+ SET enable_nereids_planner=true
+ """
+
+ sql """
+ DROP TABLE IF EXISTS `subquery1`
+ """
+
+ sql """
+ DROP TABLE IF EXISTS `subquery2`
+ """
+
+ sql """
+ DROP TABLE IF EXISTS `subquery3`
+ """
+
+ sql """
+ DROP TABLE IF EXISTS `subquery4`
+ """
+
+ sql """
+ create table subquery1
+ (k1 bigint, k2 bigint)
+ duplicate key(k1)
+ distributed by hash(k2) buckets 1
+ properties('replication_num' = '1')
+ """
+
+ sql """
+ create table subquery2
+ (k1 varchar(10), k2 bigint)
+ partition by range(k2)
+ (partition p1 values less than("10"))
+ distributed by hash(k2) buckets 1
+ properties('replication_num' = '1')
+ """
+
+ sql """
+ create table subquery3
+ (k1 int not null, k2 varchar(128), k3 bigint, v1 bigint, v2 bigint)
+ distributed by hash(k2) buckets 1
+ properties('replication_num' = '1')
+ """
+
+ sql """
+ create table subquery4
+ (k1 bigint, k2 bigint)
+ duplicate key(k1)
+ distributed by hash(k2) buckets 1
+ properties('replication_num' = '1')
+ """
+
+ sql """
+ insert into subquery1 values (1,2), (1,3), (2,4), (2,5), (3,3), (3,4), (20,2), (22,3), (24,4)
+ """
+
+ sql """
+ insert into subquery2 values ("abc",2),("abc",3),("abcd",2),("abcde",4),("abcdef",5)
+ """
+
+ sql """
+ insert into subquery3 values (1,"abc",2,3,4), (1,"abcd",3,3,4), (2,"xyz",2,4,2),
+ (2,"uvw",3,4,2), (2,"uvw",3,4,2), (3,"abc",4,5,3), (3,"abc",4,5,3)
+ """
+
+ sql """
+ insert into subquery4 values (5,4), (5,2), (8,3), (5,4), (6,7), (8,9)
+ """
+
+ // The query result is not necessarily correct, because there are some problems in the current new optimizer,
+ // just verify that the subquery de-nesting function can support the following scenarios,
+ // and the out file will be updated later
+
+ // unstable
+ //qt_scalar_less_than_corr """
+ sql """
+ select * from subquery1 where subquery1.k1 < (select sum(subquery3.k3) from subquery3 where subquery3.v2 = subquery1.k2)
+ """
+
+ //qt_scalar_not_equal_corr """
+ sql """
+ select * from subquery1 where subquery1.k1 != (select sum(subquery3.k3) from subquery3 where subquery3.v2 = subquery1.k2)
+ """
+
+ //qt_scalar_not_equal_uncorr """
+ /*sql """
+ select * from subquery1 where subquery1.k1 != (select sum(subquery3.k3) from subquery3)
+ """*/
+
+ //qt_scalar_equal_to_corr """
+ sql """
+ select * from subquery1 where subquery1.k1 = (select sum(subquery3.k3) from subquery3 where subquery3.v2 = subquery1.k2)
+ """
+
+ //qt_scalar_equal_to_uncorr """
+ /*sql """
+ select * from subquery1 where subquery1.k1 = (select sum(subquery3.k3) from subquery3)
+ """*/
+
+ //qt_not_in_corr """
+ sql """
+ select * from subquery1 where subquery1.k1 not in (select subquery3.k3 from subquery3 where subquery3.v2 = subquery1.k2)
+ """
+
+ //qt_not_in_uncorr """
+ sql """
+ select * from subquery1 where subquery1.k1 not in (select subquery3.k3 from subquery3)
+ """
+
+ //qt_in_subquery_corr """
+ sql """
+ select * from subquery1 where subquery1.k1 in (select subquery3.k3 from subquery3 where subquery3.v2 = subquery1.k2)
+ """
+
+ //qt_in_subquery_uncorr """
+ sql """
+ select * from subquery1 where subquery1.k1 in (select subquery3.k3 from subquery3)
+ """
+
+ //qt_not_exist_corr """
+ sql """
+ select * from subquery1 where not exists (select subquery3.k3 from subquery3 where subquery1.k2 = subquery3.v2)
+ """
+
+ //qt_not_exist_uncorr """
+ /*sql """
+ select * from subquery1 where not exists (select subquery3.k3 from subquery3)
+ """*/
+
+ //qt_exist_corr """
+ sql """
+ select * from subquery1 where exists (select subquery3.k3 from subquery3 where subquery1.k2 = subquery3.v2)
+ """
+
+ //qt_exist_uncorr """
+ /*sql """
+ select * from subquery1 where exists (select subquery3.k3 from subquery3)
+ """*/
+
+ //qt_in_with_in_and_scalar """
+ sql """
+ select * from subquery1 where subquery1.k1 in (
+ select subquery3.k3 from subquery3 where
+ subquery3.k3 in (select subquery4.k1 from subquery4 where subquery4.k1 = 3)
+ and subquery3.v2 > (select sum(subquery2.k2) from subquery2 where subquery2.k2 = subquery3.v1))
+ """
+
+ //qt_exist_and_not_exist """
+ sql """
+ select * from subquery1 where exists (select subquery3.k3 from subquery3 where subquery1.k2 = subquery3.v2)
+ and not exists (select subquery4.k2 from subquery4 where subquery1.k2 = subquery4.k2)
+ """
+
+ //------------------unCorrelated-----------------
+ //qt_scalar_unCorrelated
+ /*sql """
+ select * from subquery1 where subquery1.k1 < (select sum(subquery3.k3) from subquery3 where subquery3.v2 = 2)
+ """*/
+
+ //qt_not_scalar_unCorrelated
+ /*sql """
+ select * from subquery1 where subquery1.k1 != (select sum(subquery3.k3) from subquery3 where subquery3.v2 = 2);
+ """*/
+
+ //qt_in_unCorrelated
+ sql """
+ select * from subquery1 where subquery1.k1 in (select subquery3.k3 from subquery3 where subquery3.v2 = 2);
+ """
+
+ //qt_not_in_unCorrelated
+ sql """
+ select * from subquery1 where subquery1.k1 not in (select subquery3.k3 from subquery3 where subquery3.v2 = 2);
+ """
+
+ //qt_exist_unCorrelated
+ /*sql """
+ select * from subquery1 where exists (select subquery3.k3 from subquery3 where subquery3.v2 = 2);
+ """*/
+
+ //qt_not_exists_unCorrelated
+ /*sql """
+ select * from subquery1 where not exists (select subquery3.k3 from subquery3 where subquery3.v2 = 2);
+ """*/
+
+ //----------with subquery alias----------
+ //qt_scalar
+ //Open after the project is completed
+ //sql """
+ // select * from subquery1
+ // where subquery1.k1 < (select max(aa) from
+ // (select k1 as aa from subquery3 where subquery1.k2 = subquery3.v2) subquery3)
+ //"""
+
+ //qt_in
+ sql """
+ select * from subquery1
+ where subquery1.k1 in (select aa from
+ (select k1 as aa from subquery3 where subquery1.k2 = subquery3.v2) subquery3)
+ """
+
+ //qt_not_in
+ sql """
+ select * from subquery1
+ where subquery1.k1 not in (select aa from
+ (select k1 as aa from subquery3 where subquery1.k2 = subquery3.v2) subquery3)
+ """
+
+ //qt_exist
+ sql """
+ select * from subquery1
+ where exists (select aa from
+ (select k1 as aa from subquery3 where subquery1.k2 = subquery3.v2) subquery3)
+ """
+
+ //qt_not_exist
+ sql """
+ select * from subquery1
+ where not exists (select aa from
+ (select k1 as aa from subquery3 where subquery1.k2 = subquery3.v2) subquery3)
+ """
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org