[feature](Nereids) Add subquery analyze (#11300)

Increase the parsing of subquery.

Add LogicalApply and LogicalCorrelatedJoin and LogicalEnforceSingleRow.
(These structures are temporarily in use, in preparation for the follow-up)

LogicalApply:
Apply Node for subquery.
Use this node to display the subquery in the relational algebra tree.
refer to "Orthogonal Optimization of Subqueries and Aggregation"

LogicalCorrelatedJoin:
A relational algebra node with join type converted from apply node to subquery.

LogicalEnforceSingleRow:
Guaranteed to return a result of 1 row.
This commit is contained in:
zhengshiJ
2022-07-29 12:44:04 +08:00
committed by GitHub
parent f70bbd274e
commit a60cfab844
11 changed files with 596 additions and 26 deletions

View File

@ -21,10 +21,13 @@ import org.apache.doris.nereids.PlannerContext;
import org.apache.doris.nereids.jobs.batch.AnalyzeRulesJob;
import org.apache.doris.nereids.memo.Memo;
import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.rules.analysis.Scope;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.qe.ConnectContext;
import java.util.Optional;
/**
* Bind symbols according to metadata in the catalog, perform semantic analysis, etc.
* TODO: revisit the interface after subquery analysis is supported.
@ -40,14 +43,21 @@ public class NereidsAnalyzer {
* Analyze plan.
*/
public LogicalPlan analyze(Plan plan) {
return (LogicalPlan) analyzeWithPlannerContext(plan).getMemo().copyOut();
return analyze(plan, Optional.empty());
}
/**
* Analyze plan with scope.
*/
public LogicalPlan analyze(Plan plan, Optional<Scope> scope) {
return (LogicalPlan) analyzeWithPlannerContext(plan, scope).getMemo().copyOut();
}
/**
* Convert SQL String to analyzed plan.
*/
public LogicalPlan analyze(String sql) {
return analyze(parse(sql));
return analyze(parse(sql), Optional.empty());
}
/**
@ -56,11 +66,18 @@ public class NereidsAnalyzer {
* further plan optimization without creating new {@link Memo} and {@link PlannerContext}.
*/
public PlannerContext analyzeWithPlannerContext(Plan plan) {
return analyzeWithPlannerContext(plan, Optional.empty());
}
/**
* Analyze plan with scope.
*/
public PlannerContext analyzeWithPlannerContext(Plan plan, Optional<Scope> scope) {
PlannerContext plannerContext = new Memo(plan)
.newPlannerContext(connectContext)
.setDefaultJobContext();
new AnalyzeRulesJob(plannerContext).execute();
new AnalyzeRulesJob(plannerContext, scope).execute();
return plannerContext;
}

View File

@ -22,24 +22,28 @@ import org.apache.doris.nereids.rules.analysis.BindFunction;
import org.apache.doris.nereids.rules.analysis.BindRelation;
import org.apache.doris.nereids.rules.analysis.BindSlotReference;
import org.apache.doris.nereids.rules.analysis.ProjectToGlobalAggregate;
import org.apache.doris.nereids.rules.analysis.Scope;
import com.google.common.collect.ImmutableList;
import java.util.Optional;
/**
* Execute the analysis rules.
*/
public class AnalyzeRulesJob extends BatchRulesJob {
/**
* Execute the analysis job
* Execute the analysis job with scope.
* @param plannerContext planner context for execute job
* @param scope Parse the symbolic scope of the field
*/
public AnalyzeRulesJob(PlannerContext plannerContext) {
public AnalyzeRulesJob(PlannerContext plannerContext, Optional<Scope> scope) {
super(plannerContext);
rulesJob.addAll(ImmutableList.of(
bottomUpBatch(ImmutableList.of(
new BindRelation(),
new BindSlotReference(),
new BindSlotReference(scope),
new BindFunction(),
new ProjectToGlobalAggregate())
)));

View File

@ -28,7 +28,7 @@ import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
import org.apache.doris.nereids.trees.expressions.visitor.DefaultSubExprRewriter;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@ -48,6 +48,20 @@ import java.util.stream.Stream;
* BindSlotReference.
*/
public class BindSlotReference implements AnalysisRuleFactory {
private final Optional<Scope> outerScope;
public BindSlotReference(Optional<Scope> outputScope) {
this.outerScope = outputScope;
}
private Scope toScope(List<Slot> slots) {
if (outerScope.isPresent()) {
return new Scope(outerScope, slots);
} else {
return new Scope(slots);
}
}
@Override
public List<Rule> buildRules() {
return ImmutableList.of(
@ -115,15 +129,14 @@ public class BindSlotReference implements AnalysisRuleFactory {
List<Slot> boundedSlots = inputs.stream()
.flatMap(input -> input.getOutput().stream())
.collect(Collectors.toList());
return (E) new SlotBinder(boundedSlots, plan).bind(expr);
return (E) new SlotBinder(toScope(boundedSlots), plan).bind(expr);
}
private class SlotBinder extends DefaultExpressionRewriter<Void> {
private final List<Slot> boundSlots;
private class SlotBinder extends DefaultSubExprRewriter<Void> {
private final Plan plan;
public SlotBinder(List<Slot> boundSlots, Plan plan) {
this.boundSlots = boundSlots;
public SlotBinder(Scope scope, Plan plan) {
super(scope);
this.plan = plan;
}
@ -144,15 +157,22 @@ public class BindSlotReference implements AnalysisRuleFactory {
@Override
public Slot visitUnboundSlot(UnboundSlot unboundSlot, Void context) {
List<Slot> bounded = bindSlot(unboundSlot, boundSlots);
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();
if (!boundedOpt.isPresent()) {
throw new AnalysisException("Cannot resolve " + unboundSlot.toString());
}
List<Slot> bounded = boundedOpt.get();
switch (bounded.size()) {
case 0:
throw new AnalysisException("Cannot resolve " + unboundSlot.toString());
case 1:
return bounded.get(0);
default:
throw new AnalysisException(unboundSlot + " is ambiguous: "
+ bounded.stream()
+ bounded.stream()
.map(Slot::toString)
.collect(Collectors.joining(", ")));
}
@ -166,7 +186,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
List<String> qualifier = unboundStar.getQualifier();
switch (qualifier.size()) {
case 0: // select *
return new BoundStar(boundSlots);
return new BoundStar(getScope().getSlots());
case 1: // select table.*
case 2: // select db.table.*
return bindQualifiedStar(qualifier, context);
@ -179,7 +199,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
private BoundStar bindQualifiedStar(List<String> qualifierStar, Void context) {
// FIXME: compatible with previous behavior:
// https://github.com/apache/doris/pull/10415/files/3fe9cb0c3f805ab3a9678033b281b16ad93ec60a#r910239452
List<Slot> slots = boundSlots.stream().filter(boundSlot -> {
List<Slot> slots = getScope().getSlots().stream().filter(boundSlot -> {
switch (qualifierStar.size()) {
// table.*
case 1:

View File

@ -0,0 +1,65 @@
// 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.trees.expressions.Slot;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import java.util.List;
import java.util.Optional;
/**
* The slot range required for expression analyze.
*/
public class Scope {
private final Optional<Scope> outerScope;
private final List<Slot> slots;
public Scope(Optional<Scope> outerScope, List<Slot> slots) {
this.outerScope = outerScope;
this.slots = slots;
}
public Scope(List<Slot> slots) {
this.outerScope = Optional.empty();
this.slots = slots;
}
public List<Slot> getSlots() {
return slots;
}
public Optional<Scope> getOuterScope() {
return outerScope;
}
/**
* generate scope link from inner to outer.
*/
public List<Scope> toScopeLink() {
Scope scope = this;
Builder<Scope> builder = ImmutableList.<Scope>builder().add(scope);
while (scope.getOuterScope().isPresent()) {
scope = scope.getOuterScope().get();
builder.add(scope);
}
return builder.build();
}
}

View File

@ -0,0 +1,60 @@
// 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.analyzer.NereidsAnalyzer;
import org.apache.doris.nereids.rules.analysis.Scope;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.InSubquery;
import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.qe.ConnectContext;
import java.util.Optional;
/**
* Use the visitor to iterate sub expression.
*/
public class DefaultSubExprRewriter<C> extends DefaultExpressionRewriter<C> {
private final Scope scope;
public DefaultSubExprRewriter(Scope scope) {
this.scope = scope;
}
@Override
public Expression visitSubqueryExpr(SubqueryExpr expr, C context) {
return new SubqueryExpr(analyzeSubquery(expr));
}
@Override
public Expression visitInSubquery(InSubquery expr, C context) {
return new InSubquery(expr.getCompareExpr(), analyzeSubquery(expr));
}
private LogicalPlan analyzeSubquery(SubqueryExpr expr) {
NereidsAnalyzer subAnalyzer = new NereidsAnalyzer(ConnectContext.get());
LogicalPlan analyzed = subAnalyzer.analyze(
expr.getQueryPlan(), Optional.ofNullable(scope));
return analyzed;
}
public Scope getScope() {
return scope;
}
}

View File

@ -32,6 +32,9 @@ public enum PlanType {
LOGICAL_AGGREGATE,
LOGICAL_SORT,
LOGICAL_OLAP_SCAN,
LOGICAL_APPLY,
LOGICAL_CORRELATED_JOIN,
LOGICAL_ENFORCE_SINGLE_ROW,
GROUP_PLAN,
// physical plan

View File

@ -0,0 +1,117 @@
// 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 com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
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
*/
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;
public LogicalApply(LEFT_CHILD_TYPE input, RIGHT_CHILD_TYPE subquery, List<Expression> correlation) {
this(Optional.empty(), Optional.empty(), input, subquery, correlation);
}
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> getCorrelation() {
return correlation;
}
@Override
public List<Slot> computeOutput(Plan left, Plan right) {
return ImmutableList.<Slot>builder()
.addAll(left.getOutput())
.addAll(right.getOutput())
.build();
}
@Override
public String toString() {
return "LogicalApply (" + this.child(1).toString() + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LogicalApply that = (LogicalApply) o;
return Objects.equals(correlation, that.getCorrelation());
}
@Override
public int hashCode() {
return Objects.hash(correlation);
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitLogicalApply((LogicalApply<Plan, Plan>) this, context);
}
@Override
public List<Expression> getExpressions() {
return correlation;
}
@Override
public LogicalBinary<Plan, Plan> withChildren(List<Plan> children) {
Preconditions.checkArgument(children.size() == 2);
return new LogicalApply<>(children.get(0), children.get(1), correlation);
}
@Override
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
return new LogicalApply<>(groupExpression, Optional.of(logicalProperties), left(), right(), correlation);
}
@Override
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
return new LogicalApply<>(Optional.empty(), logicalProperties, left(), right(), correlation);
}
}

View File

@ -0,0 +1,135 @@
// 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 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(Plan left, Plan right) {
return ImmutableList.<Slot>builder()
.addAll(left.getOutput())
.addAll(right.getOutput())
.build();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("LogicalCorrelated ((correlation: ").append(type);
correlation.stream().map(c -> sb.append(", ").append(c));
sb.append("), (filter: ");
filter.ifPresent(expression -> sb.append(", ").append(expression));
return sb.append("))").toString();
}
@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);
}
}

View File

@ -0,0 +1,98 @@
// 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 com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
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
*/
public class LogicalEnforceSingleRow<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
LogicalEnforceSingleRow(CHILD_TYPE plan) {
this(Optional.empty(), Optional.empty(), plan);
}
LogicalEnforceSingleRow(Optional<GroupExpression> groupExpression, Optional<LogicalProperties> logicalProperties,
CHILD_TYPE plan) {
super(PlanType.LOGICAL_ENFORCE_SINGLE_ROW, groupExpression, logicalProperties, plan);
}
@Override
public List<Slot> computeOutput(Plan child) {
return ImmutableList.<Slot>builder()
.addAll(child.getOutput())
.build();
}
@Override
public String toString() {
return "LogicalEnforceSingleRow (" + this.child(0).toString() + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
return false;
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitLogicalEnforceSingleRow((LogicalEnforceSingleRow<Plan>) this, context);
}
@Override
public Plan withChildren(List<Plan> children) {
Preconditions.checkArgument(children.size() == 1);
return new LogicalEnforceSingleRow<>(children.get(0));
}
@Override
public List<Expression> getExpressions() {
return ImmutableList.of();
}
@Override
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
return new LogicalEnforceSingleRow<>(groupExpression, Optional.of(logicalProperties), child());
}
@Override
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
return new LogicalEnforceSingleRow<>(Optional.empty(), logicalProperties, child());
}
}

View File

@ -21,6 +21,9 @@ import org.apache.doris.nereids.analyzer.UnboundRelation;
import org.apache.doris.nereids.trees.plans.GroupPlan;
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.LogicalCorrelatedJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalEnforceSingleRow;
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.LogicalOlapScan;
@ -87,6 +90,18 @@ public abstract class PlanVisitor<R, C> {
return visit(groupPlan, context);
}
public R visitLogicalApply(LogicalApply<Plan, Plan> apply, C context) {
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);
}
// *******************************
// Physical plans
// *******************************

View File

@ -17,9 +17,7 @@
package org.apache.doris.nereids.trees.expressions;
import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.tpch.AnalyzeCheckTestBase;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.junit.jupiter.api.Test;
@ -43,20 +41,58 @@ public class SubqueryTest extends AnalyzeCheckTestBase {
+ "v2 int)\n"
+ "distributed by hash(k1) buckets 1\n"
+ "properties('replication_num' = '1');";
createTables(t0, t1);
String t2 = "create table t2("
+ "id int, \n"
+ "k1 int, \n"
+ "v2 int)\n"
+ "distributed by hash(k1) buckets 1\n"
+ "properties('replication_num' = '1');";
createTables(t0, t1, t2);
}
@Test
public void test() {
public void inTest() {
String sql = "select t0.k1\n"
+ "from t0\n"
+ "where t0.k2 in\n"
+ " (select id\n"
+ " from t1\n"
+ " where t0.k2=t1.k1)";
NereidsParser parser = new NereidsParser();
LogicalPlan parsed = parser.parseSingle(sql);
assert parsed != null;
//checkAnalyze(sql);
checkAnalyze(sql);
}
@Test
public void existTest() {
String sql1 = "select * from t0 where exists (select * from t1 where t0.k1 = t1.k1);";
checkAnalyze(sql1);
}
@Test
public void existAndExistTest() {
String sql1 = "select * from t0 where exists (select * from t1 where t0.k1 = t1.k1) "
+ "and not exists (select * from t2 where t0.id != t2.id);";
checkAnalyze(sql1);
// This type of sql cannot be parsed and cannot recognize t1.id.
// Other systems also cannot resolve such as presto.
String sql2 = "select * from t0 where exists (select * from t1 where t0.k1 = t1.k1) "
+ "and not exists (select * from t2 where t1.id != t2.id);";
assert sql2 != null;
}
@Test
public void scalarTest() {
String sql = "select * from t0 where t0.id = "
+ "(select min(t1.id) from t1 where t0.k1 = t1.k1)";
checkAnalyze(sql);
}
@Test
public void inScalarTest() {
String sql = "select * from t0 where t0.id in "
+ "(select * from t1 where t1.k1 = "
+ "(select * from t2 where t0.id = t2.id));";
checkAnalyze(sql);
}
}