[feature](nereids) explain shape plan (#18296)

`explain shape plan select ...`
only print plan shape related information, including
- node name
- join type, join condition
- filter condition
- agg phase

It is painful to maintain regression cases using explain since there are a lot of mutable information, like slot id.
By this pr, we could use explain shape plan in regression cases.

for exmaple:
this is tpch q2
+-----------------------------------------------------------------------------------------------------------+
| Explain String |
+-----------------------------------------------------------------------------------------------------------+
| PhysicalTopN |
| --PhysicalDistribute |
| ----PhysicalTopN |
| ------PhysicalProject |
| --------filter((cast(ps_supplycost as DECIMAL(27, 9)) = min(ps_supplycost) OVER(PARTITION BY p_partkey))) |
| ----------PhysicalWindow |
| ------------PhysicalQuickSort |
| --------------PhysicalProject |
| ----------------hashJoin[INNER_JOIN](supplier.s_suppkey = partsupp.ps_suppkey) |
| ------------------PhysicalProject |
| --------------------hashJoin[INNER_JOIN](part.p_partkey = partsupp.ps_partkey) |
| ----------------------PhysicalProject |
| ------------------------PhysicalOlapScan[partsupp] |
| ----------------------PhysicalProject |
| ------------------------filter((part.p_size = 15)(p_type like '%BRASS')) |
| --------------------------PhysicalOlapScan[part] |
| ------------------PhysicalDistribute |
| --------------------hashJoin[INNER_JOIN](supplier.s_nationkey = nation.n_nationkey) |
| ----------------------PhysicalOlapScan[supplier] |
| ----------------------PhysicalDistribute |
| ------------------------hashJoin[INNER_JOIN](nation.n_regionkey = region.r_regionkey) |
| --------------------------PhysicalProject |
| ----------------------------PhysicalOlapScan[nation] |
| --------------------------PhysicalDistribute |
| ----------------------------PhysicalProject |
| ------------------------------filter((region.r_name = 'EUROPE')) |
| --------------------------------PhysicalOlapScan[region] |
+-----------------------------------------------------------------------------------------------------------+
This commit is contained in:
minghong
2023-04-04 09:44:15 +08:00
committed by GitHub
parent 798d2e5160
commit 3e7a9424e4
14 changed files with 97 additions and 2 deletions

View File

@ -330,6 +330,7 @@ SESSION_USER: 'SESSION_USER';
SET: 'SET';
SETMINUS: 'MINUS';
SETS: 'SETS';
SHAPE: 'SHAPE';
SHOW: 'SHOW';
SKEWED: 'SKEWED';
SOME: 'SOME';

View File

@ -64,6 +64,7 @@ planType
| ANALYZED
| REWRITTEN | LOGICAL // same type
| OPTIMIZED | PHYSICAL // same type
| SHAPE
| ALL // default type
;

View File

@ -219,7 +219,10 @@ public class NereidsPlanner extends Planner {
LOG.info(tree);
}
if (explainLevel == ExplainLevel.OPTIMIZED_PLAN || explainLevel == ExplainLevel.ALL_PLAN) {
System.out.println(physicalPlan.shape(" "));
if (explainLevel == ExplainLevel.OPTIMIZED_PLAN
|| explainLevel == ExplainLevel.ALL_PLAN
|| explainLevel == ExplainLevel.SHAPE_PLAN) {
optimizedPlan = physicalPlan;
}
@ -354,6 +357,8 @@ public class NereidsPlanner extends Planner {
return rewrittenPlan.treeString();
case OPTIMIZED_PLAN:
return "cost = " + cost + "\n" + optimizedPlan.treeString();
case SHAPE_PLAN:
return optimizedPlan.shape("");
case ALL_PLAN:
return "========== PARSED PLAN ==========\n"
+ parsedPlan.treeString() + "\n\n"

View File

@ -55,6 +55,11 @@ public abstract class BinaryOperator extends Expression implements BinaryExpress
return "(" + left().toString() + " " + symbol + " " + right().toString() + ")";
}
@Override
public String shapeInfo() {
return "(" + left().shapeInfo() + " " + symbol + " " + right().shapeInfo() + ")";
}
@Override
public int hashCode() {
return Objects.hash(symbol, left(), right());

View File

@ -210,4 +210,8 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
return false;
}
public String shapeInfo() {
return toSql();
}
}

View File

@ -124,6 +124,15 @@ public class SlotReference extends Slot {
return name + "#" + exprId;
}
@Override
public String shapeInfo() {
if (qualifier.isEmpty()) {
return name;
} else {
return qualifier.get(qualifier.size() - 1) + "." + name;
}
}
@Override
public boolean equals(Object o) {
if (this == o) {

View File

@ -145,4 +145,29 @@ public interface Plan extends TreeNode<Plan> {
}
void setMutableState(String key, Object value);
/**
* a simple version of explain, used to verify plan shape
* @param prefix " "
* @return string format of plan shape
*/
default String shape(String prefix) {
StringBuilder builder = new StringBuilder();
builder.append(prefix).append(shapeInfo()).append("\n");
String childPrefix = prefix + "--";
children().forEach(
child -> {
builder.append(child.shape(childPrefix));
}
);
return builder.toString();
}
/**
* used in shape()
* @return default value is its class name
*/
default String shapeInfo() {
return this.getClass().getSimpleName();
}
}

View File

@ -43,6 +43,7 @@ public class ExplainCommand extends Command implements NoForward {
ANALYZED_PLAN(true),
REWRITTEN_PLAN(true),
OPTIMIZED_PLAN(true),
SHAPE_PLAN(true),
ALL_PLAN(true)
;

View File

@ -54,5 +54,4 @@ public abstract class AbstractPhysicalPlan extends AbstractPlan implements Physi
public PhysicalProperties getPhysicalProperties() {
return physicalProperties;
}
}

View File

@ -123,4 +123,13 @@ public class PhysicalFilter<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD
return new PhysicalFilter<>(conjuncts, groupExpression, getLogicalProperties(), physicalProperties,
statistics, child());
}
@Override
public String shapeInfo() {
StringBuilder builder = new StringBuilder();
builder.append("filter(");
conjuncts.forEach(conjunct -> builder.append(conjunct.shapeInfo()));
builder.append(")");
return builder.toString();
}
}

View File

@ -272,4 +272,11 @@ public class PhysicalHashAggregate<CHILD_TYPE extends Plan> extends PhysicalUnar
aggregateParam, maybeUsingStream, Optional.empty(), getLogicalProperties(),
requireProperties, physicalProperties, statistics, newChild);
}
@Override
public String shapeInfo() {
StringBuilder builder = new StringBuilder("hashAgg[");
builder.append(getAggPhase()).append("]");
return builder.toString();
}
}

View File

@ -152,4 +152,17 @@ public class PhysicalHashJoin<
return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, otherJoinConjuncts, hint, markJoinSlotReference,
groupExpression, getLogicalProperties(), physicalProperties, statistics, left(), right());
}
@Override
public String shapeInfo() {
StringBuilder builder = new StringBuilder();
builder.append("hashJoin[").append(joinType).append("]");
hashJoinConjuncts.forEach(expr -> {
builder.append(expr.shapeInfo());
});
otherJoinConjuncts.forEach(expr -> {
builder.append(expr.shapeInfo());
});
return builder.toString();
}
}

View File

@ -167,4 +167,12 @@ public class PhysicalNestedLoopJoin<
public boolean isBitMapRuntimeFilterConditionsEmpty() {
return bitMapRuntimeFilterConditions.isEmpty();
}
@Override
public String shapeInfo() {
StringBuilder builder = new StringBuilder("NestedLoopJoin");
builder.append("[").append(joinType).append("]");
otherJoinConjuncts.forEach(expr -> builder.append(expr.shapeInfo()));
return builder.toString();
}
}

View File

@ -155,4 +155,12 @@ public class PhysicalOlapScan extends PhysicalRelation implements OlapScan {
selectedPartitionIds, distributionSpec, preAggStatus, groupExpression,
getLogicalProperties(), physicalProperties, statistics);
}
@Override
public String shapeInfo() {
StringBuilder builder = new StringBuilder();
builder.append(this.getClass().getSimpleName()).append("[").append(olapTable.getName()).append("]");
return builder.toString();
}
}