[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:
xiaojunjie
2022-12-06 12:28:40 +08:00
committed by GitHub
parent 1484de9f4f
commit e578e2cd98
7 changed files with 108 additions and 19 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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.
*/

View File

@ -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) {

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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"
}
}