diff --git a/planner/core/logical_plan_trace_test.go b/planner/core/logical_plan_trace_test.go index 4ed6960409279..be97ff1624d46 100644 --- a/planner/core/logical_plan_trace_test.go +++ b/planner/core/logical_plan_trace_test.go @@ -101,6 +101,17 @@ func (s *testPlanSuite) TestSingleRuleTraceStep(c *C) { }, }, }, + { + sql: "select 1+num from (select 1+a as num from t) t1;", + flags: []uint64{flagEliminateProjection}, + assertRuleName: "projection_eliminate", + assertRuleSteps: []assertTraceStep{ + { + assertAction: "Proj[2] is eliminated, Proj[3]'s expressions changed into[plus(1, plus(1, test.t.a))]", + assertReason: "Proj[3]'s child proj[2] is redundant", + }, + }, + }, } for i, tc := range tt { diff --git a/planner/core/rule_eliminate_projection.go b/planner/core/rule_eliminate_projection.go index f641ceb479cbf..f16b9695d5dbb 100644 --- a/planner/core/rule_eliminate_projection.go +++ b/planner/core/rule_eliminate_projection.go @@ -15,7 +15,9 @@ package core import ( + "bytes" "context" + "fmt" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/kv" @@ -146,12 +148,12 @@ type projectionEliminator struct { // optimize implements the logicalOptRule interface. func (pe *projectionEliminator) optimize(ctx context.Context, lp LogicalPlan, opt *logicalOptimizeOp) (LogicalPlan, error) { - root := pe.eliminate(lp, make(map[string]*expression.Column), false) + root := pe.eliminate(lp, make(map[string]*expression.Column), false, opt) return root, nil } // eliminate eliminates the redundant projection in a logical plan. -func (pe *projectionEliminator) eliminate(p LogicalPlan, replace map[string]*expression.Column, canEliminate bool) LogicalPlan { +func (pe *projectionEliminator) eliminate(p LogicalPlan, replace map[string]*expression.Column, canEliminate bool, opt *logicalOptimizeOp) LogicalPlan { proj, isProj := p.(*LogicalProjection) childFlag := canEliminate if _, isUnion := p.(*LogicalUnionAll); isUnion { @@ -162,7 +164,7 @@ func (pe *projectionEliminator) eliminate(p LogicalPlan, replace map[string]*exp childFlag = true } for i, child := range p.Children() { - p.Children()[i] = pe.eliminate(child, replace, childFlag) + p.Children()[i] = pe.eliminate(child, replace, childFlag, opt) } switch x := p.(type) { @@ -186,6 +188,7 @@ func (pe *projectionEliminator) eliminate(p LogicalPlan, replace map[string]*exp proj.Exprs[i] = foldedExpr } p.Children()[0] = child.Children()[0] + appendProjEliminateTraceStep(proj, child, opt) } } @@ -292,3 +295,20 @@ func (p *LogicalWindow) replaceExprColumns(replace map[string]*expression.Column func (*projectionEliminator) name() string { return "projection_eliminate" } + +func appendProjEliminateTraceStep(parent, child *LogicalProjection, opt *logicalOptimizeOp) { + action := func() string { + buffer := bytes.NewBufferString( + fmt.Sprintf("Proj[%v] is eliminated, Proj[%v]'s expressions changed into[", child.ID(), parent.ID())) + for i, expr := range parent.Exprs { + if i > 0 { + buffer.WriteString(",") + } + buffer.WriteString(expr.String()) + } + buffer.WriteString("]") + return buffer.String() + }() + reason := fmt.Sprintf("Proj[%v]'s child proj[%v] is redundant", parent.ID(), child.ID()) + opt.appendStepToCurrent(child.ID(), child.TP(), reason, action) +}