[Enhancement](Nereids) Explain display extra information (#14802)
# Proposed changes Issue Number: close #14554 ## Problem summary 1. provide a function **Plan.extraPlans** that returns extra plans, eg: LogicalSubQueryAlias in LogicalCTE. 2. combine the extra plans and the children in the AbstractPlan.treeString(), distinguished by the * at the beginning. ``` ========== PARSED PLAN ========== LogicalCTE ( aliasQueries=[LogicalSubQueryAlias ( alias=s )] ) |-*LogicalSubQueryAlias ( alias=s ) | +--LogicalProject ( projects=['s_suppkey] ) | +--LogicalFilter ( predicates=('s_suppkey = '') ) | +--LogicalCheckPolicy ( child=UnboundRelation ( nameParts=supplier ) ) | +--UnboundRelation ( nameParts=supplier ) +--LogicalProject ( projects=[*] ) +--LogicalJoin ( type=CROSS_JOIN, hashJoinConjuncts=[], otherJoinConjuncts=[] ) |--LogicalSubQueryAlias ( alias=t1 ) | +--LogicalCheckPolicy ( child=UnboundRelation ( nameParts=s ) ) | +--UnboundRelation ( nameParts=s ) +--LogicalSubQueryAlias ( alias=t2 ) +--LogicalCheckPolicy ( child=UnboundRelation ( nameParts=s ) ) +--UnboundRelation ( nameParts=s ) ```
This commit is contained in:
@ -20,6 +20,7 @@ package org.apache.doris.nereids.memo;
|
||||
import org.apache.doris.common.Pair;
|
||||
import org.apache.doris.nereids.properties.LogicalProperties;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
|
||||
import org.apache.doris.nereids.util.TreeStringUtils;
|
||||
@ -384,6 +385,23 @@ public class Group {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
};
|
||||
return TreeStringUtils.treeString(this, toString, getChildren);
|
||||
|
||||
Function<Object, List<Object>> getExtraPlans = obj -> {
|
||||
if (obj instanceof Plan) {
|
||||
return (List) ((Plan) obj).extraPlans();
|
||||
} else {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
};
|
||||
|
||||
Function<Object, Boolean> displayExtraPlan = obj -> {
|
||||
if (obj instanceof Plan) {
|
||||
return ((Plan) obj).displayExtraPlanFirst();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return TreeStringUtils.treeString(this, toString, getChildren, getExtraPlans, displayExtraPlan);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,9 @@ public abstract class AbstractPlan extends AbstractTreeNode<Plan> implements Pla
|
||||
public String treeString() {
|
||||
return TreeStringUtils.treeString(this,
|
||||
plan -> plan.toString(),
|
||||
plan -> (List) ((Plan) plan).children());
|
||||
plan -> (List) ((Plan) plan).children(),
|
||||
plan -> (List) ((Plan) plan).extraPlans(),
|
||||
plan -> ((Plan) plan).displayExtraPlanFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -27,6 +27,7 @@ import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import java.util.List;
|
||||
@ -70,6 +71,17 @@ public interface Plan extends TreeNode<Plan> {
|
||||
throw new IllegalStateException("Not support compute logical properties for " + getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra plans.
|
||||
*/
|
||||
default List<Plan> extraPlans() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
default boolean displayExtraPlanFirst() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output slot list of the plan.
|
||||
*/
|
||||
|
||||
@ -54,6 +54,11 @@ public class LogicalCTE<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE
|
||||
return aliasQueries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Plan> extraPlans() {
|
||||
return (List) aliasQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* In fact, the action of LogicalCTE is to store and register with clauses, and this logical node will be
|
||||
* eliminated immediately after finishing the process of with-clause registry; This process is executed before
|
||||
@ -71,6 +76,11 @@ public class LogicalCTE<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean displayExtraPlanFirst() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@ -21,6 +21,7 @@ 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.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.algebra.Filter;
|
||||
@ -33,6 +34,8 @@ import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Logical filter plan.
|
||||
@ -70,6 +73,22 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T
|
||||
return predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Plan> extraPlans() {
|
||||
if (predicates != null) {
|
||||
return predicates.children().stream()
|
||||
.flatMap(m -> {
|
||||
if (m instanceof SubqueryExpr) {
|
||||
return Stream.of(
|
||||
new LogicalSubQueryAlias<>(m.toSql(), ((SubqueryExpr) m).getQueryPlan()));
|
||||
} else {
|
||||
return new LogicalFilter<Plan>(m, child()).extraPlans().stream();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Slot> computeOutput() {
|
||||
return child().getOutput();
|
||||
|
||||
@ -29,34 +29,52 @@ import java.util.function.Function;
|
||||
public class TreeStringUtils {
|
||||
|
||||
public static String treeString(Object object, Function<Object, String> objectToString,
|
||||
Function<Object, List<Object>> childSupplier) {
|
||||
Function<Object, List<Object>> childSupplier,
|
||||
Function<Object, List<Object>> extraPlansSupplier,
|
||||
Function<Object, Boolean> displaySupplier) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
treeString(lines, 0, new ArrayList<>(), object, objectToString, childSupplier);
|
||||
treeString(lines, new ArrayList<>(), object,
|
||||
objectToString, childSupplier, extraPlansSupplier, displaySupplier, false);
|
||||
return StringUtils.join(lines, "\n");
|
||||
}
|
||||
|
||||
private static void treeString(List<String> lines, int depth, List<Boolean> lastChildren, Object object,
|
||||
Function<Object, String> objectToString, Function<Object, List<Object>> childrenSupplier) {
|
||||
private static void treeString(List<String> lines, List<Boolean> lastChildren, Object object,
|
||||
Function<Object, String> objectToString, Function<Object, List<Object>> childrenSupplier,
|
||||
Function<Object, List<Object>> extraPlansSupplier,
|
||||
Function<Object, Boolean> displaySupplier,
|
||||
boolean isExtraPlan) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (depth > 0) {
|
||||
if (lastChildren.size() > 1) {
|
||||
for (int i = 0; i < lastChildren.size() - 1; i++) {
|
||||
sb.append(lastChildren.get(i) ? " " : "| ");
|
||||
}
|
||||
}
|
||||
if (lastChildren.size() > 0) {
|
||||
Boolean last = lastChildren.get(lastChildren.size() - 1);
|
||||
sb.append(last ? "+--" : "|--");
|
||||
}
|
||||
|
||||
for (int i = 0; i < lastChildren.size() - 1; i++) {
|
||||
sb.append(lastChildren.get(i) ? " " : "| ");
|
||||
}
|
||||
|
||||
if (lastChildren.size() > 0) {
|
||||
Boolean last = lastChildren.get(lastChildren.size() - 1);
|
||||
sb.append(last ? "+-" : "|-");
|
||||
sb.append(isExtraPlan ? "*" : "-");
|
||||
}
|
||||
|
||||
sb.append(objectToString.apply(object));
|
||||
lines.add(sb.toString());
|
||||
|
||||
List<Object> allObjects = new ArrayList<>();
|
||||
List<Object> children = childrenSupplier.apply(object);
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
List<Object> extraPlans = extraPlansSupplier.apply(object);
|
||||
boolean displayExtraPlanFirst = displaySupplier.apply(object);
|
||||
if (displayExtraPlanFirst) {
|
||||
allObjects.addAll(extraPlans);
|
||||
allObjects.addAll(children);
|
||||
} else {
|
||||
allObjects.addAll(children);
|
||||
allObjects.addAll(extraPlans);
|
||||
}
|
||||
for (int i = 0; i < allObjects.size(); i++) {
|
||||
List<Boolean> newLasts = new ArrayList<>(lastChildren);
|
||||
newLasts.add(i + 1 == children.size());
|
||||
treeString(lines, depth + 1, newLasts, children.get(i), objectToString, childrenSupplier);
|
||||
newLasts.add(i + 1 == allObjects.size());
|
||||
boolean isSubExtraPlan = displayExtraPlanFirst ? i < extraPlans.size() : i >= children.size();
|
||||
treeString(lines, newLasts, allObjects.get(i),
|
||||
objectToString, childrenSupplier, extraPlansSupplier, displaySupplier, isSubExtraPlan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,4 +47,14 @@ suite("nereids_explain") {
|
||||
sql("parsed plan select 100")
|
||||
contains "UnboundOneRowRelation"
|
||||
}
|
||||
|
||||
explain {
|
||||
sql("plan select * from lineorder where lo_orderkey > (select avg(lo_orderkey) from lineorder)")
|
||||
contains "*LogicalSubQueryAlias"
|
||||
}
|
||||
|
||||
explain {
|
||||
sql("plan with s as (select * from supplier) select * from s as s1, s as s2")
|
||||
contains "*LogicalSubQueryAlias"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user