[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:
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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())
|
||||
)));
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
// *******************************
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user