diff --git a/executor/executor.go b/executor/executor.go index 44649b7653dc3..c7bd9e4bd7e5a 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -1705,7 +1705,7 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) { sc.IsStaleness = false sc.LockTableIDs = make(map[int64]struct{}) sc.EnableOptimizeTrace = false - sc.LogicalOptimizeTrace = nil + sc.OptimizeTracer = nil sc.OptimizerCETrace = nil sc.InitMemTracker(memory.LabelForSQLText, vars.MemQuotaQuery) diff --git a/executor/trace.go b/executor/trace.go index 492324f6dd5e7..bc842802feee6 100644 --- a/executor/trace.go +++ b/executor/trace.go @@ -41,7 +41,6 @@ import ( "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/tracing" "go.uber.org/zap" "sourcegraph.com/sourcegraph/appdash" traceImpl "sourcegraph.com/sourcegraph/appdash/opentracing" @@ -162,9 +161,7 @@ func (e *TraceExec) nextOptimizerPlanTrace(ctx context.Context, se sessionctx.Co jsonEncoder := json.NewEncoder(&writer) // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... jsonEncoder.SetEscapeHTML(false) - logical := se.GetSessionVars().StmtCtx.LogicalOptimizeTrace - physical := se.GetSessionVars().StmtCtx.PhysicalOptimizeTrace - err = jsonEncoder.Encode(&tracing.OptimizeTracer{Logical: logical, Physical: physical}) + err = jsonEncoder.Encode(se.GetSessionVars().StmtCtx.OptimizeTracer) if err != nil { return errors.AddStack(err) } diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index c49dfb6f316ba..35cce8abfda3b 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -207,7 +207,6 @@ func (p *baseLogicalPlan) rebuildChildTasks(childTasks *[]task, pp PhysicalPlan, func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPlan, prop *property.PhysicalProperty, addEnforcer bool, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (task, int64, error) { var bestTask task = invalidTask var curCntPlan, cntPlan int64 - childProps := make(map[task]*property.PhysicalProperty) childTasks := make([]task, 0, len(p.children)) childCnts := make([]int64, len(p.children)) cntPlan = 0 @@ -231,7 +230,6 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl break } childTasks = append(childTasks, childTask) - childProps[childTask] = childProp } // This check makes sure that there is no invalid child task. @@ -279,15 +277,7 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl bestTask = curTask break } - c := opt.appendCandidate(p, curTask.plan(), prop) - if c != nil { - for _, childTask := range childTasks { - if childTask == nil { - continue - } - opt.appendChildToCandidate(c, childTask.plan(), childProps[childTask]) - } - } + opt.appendCandidate(p, curTask.plan(), prop) // Get the most efficient one. if curTask.cost() < bestTask.cost() || (bestTask.invalid() && !curTask.invalid()) { bestTask = curTask @@ -310,44 +300,30 @@ func (op *physicalOptimizeOp) withEnableOptimizeTracer(tracer *tracing.PhysicalO return op } -func (op *physicalOptimizeOp) buildPhysicalOptimizeTraceInfo(p LogicalPlan, prop string) *tracing.PhysicalOptimizeTraceInfo { +func (op *physicalOptimizeOp) buildPhysicalOptimizeTraceInfo(p LogicalPlan) { if op == nil || op.tracer == nil { - return nil + return } name := tracing.CodecPlanName(p.TP(), p.ID()) if _, ok := op.tracer.State[name]; !ok { - op.tracer.State[name] = make(map[string]*tracing.PhysicalOptimizeTraceInfo) - } - if info, ok := op.tracer.State[name][prop]; ok { - return info + op.tracer.State[name] = make(map[string]*tracing.PlanTrace) } - traceInfo := &tracing.PhysicalOptimizeTraceInfo{Property: prop} - op.tracer.State[name][prop] = traceInfo - return traceInfo } -func (op *physicalOptimizeOp) appendChildToCandidate(candidateInfo *tracing.PlanTrace, plan PhysicalPlan, prop *property.PhysicalProperty) { - if op == nil || op.tracer == nil || candidateInfo == nil { +func (op *physicalOptimizeOp) appendCandidate(lp LogicalPlan, pp PhysicalPlan, prop *property.PhysicalProperty) { + if op == nil || op.tracer == nil || pp == nil { return } - childPhysicalPlanTrace := &tracing.PlanTrace{TP: plan.TP(), ID: plan.ID(), ExplainInfo: plan.ExplainInfo(), Cost: plan.Cost(), ProperType: prop.String()} - candidateInfo.Children = append(candidateInfo.Children, childPhysicalPlanTrace) -} - -func (op *physicalOptimizeOp) appendCandidate(logicalPlan LogicalPlan, physicalPlan PhysicalPlan, prop *property.PhysicalProperty) *tracing.PlanTrace { - if op == nil || op.tracer == nil { - return nil - } - key := string(prop.HashCode()) - PhysicalPlanTrace := &tracing.PlanTrace{TP: physicalPlan.TP(), ID: physicalPlan.ID(), - ExplainInfo: physicalPlan.ExplainInfo(), Cost: physicalPlan.Cost(), ProperType: prop.String()} - name := tracing.CodecPlanName(logicalPlan.TP(), logicalPlan.ID()) - traceInfo := op.tracer.State[name][key] - if traceInfo == nil { - traceInfo = op.buildPhysicalOptimizeTraceInfo(logicalPlan, key) + PhysicalPlanTrace := &tracing.PlanTrace{TP: pp.TP(), ID: pp.ID(), + ExplainInfo: pp.ExplainInfo(), Cost: pp.Cost(), ProperType: prop.String()} + name := tracing.CodecPlanName(lp.TP(), lp.ID()) + key := tracing.CodecPlanName(pp.TP(), pp.ID()) + pps := op.tracer.State[name] + if pps == nil { + op.buildPhysicalOptimizeTraceInfo(lp) } - traceInfo.Candidates = append(traceInfo.Candidates, PhysicalPlanTrace) - return PhysicalPlanTrace + pps[key] = PhysicalPlanTrace + op.tracer.State[name] = pps } // findBestTask implements LogicalPlan interface. @@ -421,7 +397,7 @@ func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCoun var cnt int64 var curTask task - opt.buildPhysicalOptimizeTraceInfo(p, newProp.String()) + opt.buildPhysicalOptimizeTraceInfo(p) if bestTask, cnt, err = p.enumeratePhysicalPlans4Task(plansFitsProp, newProp, false, planCounter, opt); err != nil { return nil, 0, err } @@ -439,6 +415,7 @@ func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCoun bestTask = curTask goto END } + opt.appendCandidate(p, curTask.plan(), prop) if curTask.cost() < bestTask.cost() || (bestTask.invalid() && !curTask.invalid()) { bestTask = curTask } @@ -831,11 +808,12 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter } }() + opt.buildPhysicalOptimizeTraceInfo(ds) cntPlan = 0 for _, candidate := range candidates { path := candidate.path if path.PartialIndexPaths != nil { - idxMergeTask, err := ds.convertToIndexMergeScan(prop, candidate) + idxMergeTask, err := ds.convertToIndexMergeScan(prop, candidate, opt) if err != nil { return nil, 0, err } @@ -843,6 +821,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter cntPlan += 1 planCounter.Dec(1) } + appendCandidate(ds, idxMergeTask, prop, opt) if idxMergeTask.cost() < t.cost() || planCounter.Empty() { t = idxMergeTask } @@ -922,10 +901,11 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter if allRangeIsPoint { var pointGetTask task if len(path.Ranges) == 1 { - pointGetTask = ds.convertToPointGet(prop, candidate) + pointGetTask = ds.convertToPointGet(prop, candidate, opt) } else { - pointGetTask = ds.convertToBatchPointGet(prop, candidate, hashPartColName) + pointGetTask = ds.convertToBatchPointGet(prop, candidate, hashPartColName, opt) } + appendCandidate(ds, pointGetTask, prop, opt) if !pointGetTask.invalid() { cntPlan += 1 planCounter.Dec(1) @@ -948,9 +928,9 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter } var tblTask task if ds.SampleInfo != nil { - tblTask, err = ds.convertToSampleTable(prop, candidate) + tblTask, err = ds.convertToSampleTable(prop, candidate, opt) } else { - tblTask, err = ds.convertToTableScan(prop, candidate) + tblTask, err = ds.convertToTableScan(prop, candidate, opt) } if err != nil { return nil, 0, err @@ -959,6 +939,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter cntPlan += 1 planCounter.Dec(1) } + appendCandidate(ds, tblTask, prop, opt) if tblTask.cost() < t.cost() || planCounter.Empty() { t = tblTask } @@ -971,7 +952,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter if ds.preferStoreType&preferTiFlash != 0 { continue } - idxTask, err := ds.convertToIndexScan(prop, candidate) + idxTask, err := ds.convertToIndexScan(prop, candidate, opt) if err != nil { return nil, 0, err } @@ -979,6 +960,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter cntPlan += 1 planCounter.Dec(1) } + appendCandidate(ds, idxTask, prop, opt) if idxTask.cost() < t.cost() || planCounter.Empty() { t = idxTask } @@ -1011,7 +993,7 @@ func (ds *DataSource) canConvertToPointGetForPlanCache(path *util.AccessPath) bo return false } -func (ds *DataSource) convertToIndexMergeScan(prop *property.PhysicalProperty, candidate *candidatePath) (task task, err error) { +func (ds *DataSource) convertToIndexMergeScan(prop *property.PhysicalProperty, candidate *candidatePath, opt *physicalOptimizeOp) (task task, err error) { if prop.TaskTp != property.RootTaskType || !prop.IsEmpty() { return invalidTask, nil } @@ -1299,7 +1281,8 @@ func (ds *DataSource) addSelection4PlanCache(task *rootTask, stats *property.Sta } // convertToIndexScan converts the DataSource to index scan with idx. -func (ds *DataSource) convertToIndexScan(prop *property.PhysicalProperty, candidate *candidatePath) (task task, err error) { +func (ds *DataSource) convertToIndexScan(prop *property.PhysicalProperty, + candidate *candidatePath, opt *physicalOptimizeOp) (task task, err error) { if !candidate.path.IsSingleScan { // If it's parent requires single read task, return max cost. if prop.TaskTp == property.CopSingleReadTaskType { @@ -1805,7 +1788,7 @@ func (s *LogicalIndexScan) GetPhysicalIndexScan(schema *expression.Schema, stats } // convertToTableScan converts the DataSource to table scan. -func (ds *DataSource) convertToTableScan(prop *property.PhysicalProperty, candidate *candidatePath) (task task, err error) { +func (ds *DataSource) convertToTableScan(prop *property.PhysicalProperty, candidate *candidatePath, opt *physicalOptimizeOp) (task task, err error) { // It will be handled in convertToIndexScan. if prop.TaskTp == property.CopDoubleReadTaskType { return invalidTask, nil @@ -1882,7 +1865,8 @@ func (ds *DataSource) convertToTableScan(prop *property.PhysicalProperty, candid return task, nil } -func (ds *DataSource) convertToSampleTable(prop *property.PhysicalProperty, candidate *candidatePath) (task task, err error) { +func (ds *DataSource) convertToSampleTable(prop *property.PhysicalProperty, + candidate *candidatePath, opt *physicalOptimizeOp) (task task, err error) { if prop.TaskTp == property.CopDoubleReadTaskType { return invalidTask, nil } @@ -1906,7 +1890,7 @@ func (ds *DataSource) convertToSampleTable(prop *property.PhysicalProperty, cand }, nil } -func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candidate *candidatePath) task { +func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candidate *candidatePath, opt *physicalOptimizeOp) (task task) { if !prop.IsEmpty() && !candidate.isMatchProp { return invalidTask } @@ -1990,7 +1974,8 @@ func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candida return rTsk } -func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, candidate *candidatePath, hashPartColName *ast.ColumnName) task { +func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, + candidate *candidatePath, hashPartColName *ast.ColumnName, opt *physicalOptimizeOp) (task task) { if !prop.IsEmpty() && !candidate.isMatchProp { return invalidTask } @@ -2266,3 +2251,10 @@ func (p *LogicalCTETable) findBestTask(prop *property.PhysicalProperty, planCoun t = &rootTask{p: pcteTable} return t, 1, nil } + +func appendCandidate(lp LogicalPlan, task task, prop *property.PhysicalProperty, opt *physicalOptimizeOp) { + if task == nil || task.invalid() { + return + } + opt.appendCandidate(lp, task.plan(), prop) +} diff --git a/planner/core/logical_plan_trace_test.go b/planner/core/logical_plan_trace_test.go index 4cca2869d0996..5beac1e77591d 100644 --- a/planner/core/logical_plan_trace_test.go +++ b/planner/core/logical_plan_trace_test.go @@ -414,7 +414,7 @@ func (s *testPlanSuite) TestSingleRuleTraceStep(c *C) { } _, err = logicalOptimize(ctx, flag, p.(LogicalPlan)) c.Assert(err, IsNil) - otrace := sctx.GetSessionVars().StmtCtx.LogicalOptimizeTrace + otrace := sctx.GetSessionVars().StmtCtx.OptimizeTracer.Logical c.Assert(otrace, NotNil) assert := false for _, step := range otrace.Steps { diff --git a/planner/core/optimizer.go b/planner/core/optimizer.go index 0b15dba11f08a..1131a3c7ba92d 100644 --- a/planner/core/optimizer.go +++ b/planner/core/optimizer.go @@ -290,7 +290,9 @@ func DoOptimize(ctx context.Context, sctx sessionctx.Context, flag uint64, logic if sctx.GetSessionVars().StmtCtx.EnableOptimizerCETrace { refineCETrace(sctx) } - + if sctx.GetSessionVars().StmtCtx.EnableOptimizeTrace { + sctx.GetSessionVars().StmtCtx.OptimizeTracer.RecordFinalPlan(finalPlan.buildPlanTrace()) + } return finalPlan, cost, nil } @@ -409,12 +411,13 @@ func logicalOptimize(ctx context.Context, flag uint64, logic LogicalPlan) (Logic opt := defaultLogicalOptimizeOption() vars := logic.SCtx().GetSessionVars() if vars.StmtCtx.EnableOptimizeTrace { + vars.StmtCtx.OptimizeTracer = &tracing.OptimizeTracer{} tracer := &tracing.LogicalOptimizeTracer{ Steps: make([]*tracing.LogicalRuleOptimizeTracer, 0), } opt = opt.withEnableOptimizeTracer(tracer) defer func() { - vars.StmtCtx.LogicalOptimizeTrace = tracer + vars.StmtCtx.OptimizeTracer.Logical = tracer }() } var err error @@ -455,12 +458,12 @@ func physicalOptimize(logic LogicalPlan, planCounter *PlanCounterTp) (plan Physi opt := defaultPhysicalOptimizeOption() stmtCtx := logic.SCtx().GetSessionVars().StmtCtx if stmtCtx.EnableOptimizeTrace { - tracer := &tracing.PhysicalOptimizeTracer{State: make(map[string]map[string]*tracing.PhysicalOptimizeTraceInfo)} + tracer := &tracing.PhysicalOptimizeTracer{State: make(map[string]map[string]*tracing.PlanTrace)} opt = opt.withEnableOptimizeTracer(tracer) defer func() { if err == nil { tracer.RecordFinalPlanTrace(plan.buildPlanTrace()) - stmtCtx.PhysicalOptimizeTrace = tracer + stmtCtx.OptimizeTracer.Physical = tracer } }() } diff --git a/planner/core/physical_plan_trace_test.go b/planner/core/physical_plan_trace_test.go index bcb3a97070da5..9988fb6cacfa9 100644 --- a/planner/core/physical_plan_trace_test.go +++ b/planner/core/physical_plan_trace_test.go @@ -38,28 +38,61 @@ func TestPhysicalOptimizeWithTraceEnabled(t *testing.T) { tk := testkit.NewTestKit(t, store) ctx := tk.Session().(sessionctx.Context) tk.MustExec("use test") - tk.MustExec("create table t(a int)") - - sql := "select * from t where a in (1,2)" + tk.MustExec("create table t(a int, primary key (a))") + testcases := []struct { + sql string + logicalList []string + physicalList []string + bests []string + }{ + { + sql: "select * from t", + logicalList: []string{ + "DataSource_1", "Projection_2", + }, + physicalList: []string{ + "Projection_3", "TableReader_5", + }, + bests: []string{ + "Projection_3", "TableReader_5", + }, + }, + { + sql: "select * from t where a = 1", + logicalList: []string{ + "DataSource_1", "Projection_3", + }, + physicalList: []string{ + "Point_Get_5", "Projection_4", + }, + bests: []string{ + "Point_Get_5", "Projection_4", + }, + }, + } - stmt, err := p.ParseOneStmt(sql, "", "") - require.NoError(t, err) - err = core.Preprocess(ctx, stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: dom.InfoSchema()})) - require.NoError(t, err) - sctx := core.MockContext() - sctx.GetSessionVars().StmtCtx.EnableOptimizeTrace = true - builder, _ := core.NewPlanBuilder().Init(sctx, dom.InfoSchema(), &hint.BlockHintProcessor{}) - domain.GetDomain(sctx).MockInfoCacheAndLoadInfoSchema(dom.InfoSchema()) - plan, err := builder.Build(context.TODO(), stmt) - require.NoError(t, err) - flag := uint64(0) - _, _, err = core.DoOptimize(context.TODO(), sctx, flag, plan.(core.LogicalPlan)) - require.NoError(t, err) - otrace := sctx.GetSessionVars().StmtCtx.PhysicalOptimizeTrace - require.NotNil(t, otrace) - logicalList, physicalList := getList(otrace) - require.True(t, checkList(logicalList, []string{"Projection_3", "Selection_2"})) - require.True(t, checkList(physicalList, []string{"Projection_4", "Selection_5"})) + for _, testcase := range testcases { + sql := testcase.sql + stmt, err := p.ParseOneStmt(sql, "", "") + require.NoError(t, err) + err = core.Preprocess(ctx, stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: dom.InfoSchema()})) + require.NoError(t, err) + sctx := core.MockContext() + sctx.GetSessionVars().StmtCtx.EnableOptimizeTrace = true + builder, _ := core.NewPlanBuilder().Init(sctx, dom.InfoSchema(), &hint.BlockHintProcessor{}) + domain.GetDomain(sctx).MockInfoCacheAndLoadInfoSchema(dom.InfoSchema()) + plan, err := builder.Build(context.TODO(), stmt) + require.NoError(t, err) + flag := uint64(0) + flag = flag | 1<<3 | 1<<8 + _, _, err = core.DoOptimize(context.TODO(), sctx, flag, plan.(core.LogicalPlan)) + require.NoError(t, err) + otrace := sctx.GetSessionVars().StmtCtx.OptimizeTracer.Physical + require.NotNil(t, otrace) + logicalList, physicalList := getList(otrace) + require.True(t, checkList(logicalList, testcase.logicalList)) + require.True(t, checkList(physicalList, testcase.physicalList)) + } } func checkList(d []string, s []string) bool { @@ -77,10 +110,8 @@ func checkList(d []string, s []string) bool { func getList(otrace *tracing.PhysicalOptimizeTracer) (ll []string, pl []string) { for logicalPlan, v := range otrace.State { ll = append(ll, logicalPlan) - for _, info := range v { - for _, task := range info.Candidates { - pl = append(pl, tracing.CodecPlanName(task.TP, task.ID)) - } + for physicalPlanKey := range v { + pl = append(pl, physicalPlanKey) } } sort.Strings(ll) diff --git a/sessionctx/stmtctx/stmtctx.go b/sessionctx/stmtctx/stmtctx.go index 0fc60e4c10d4c..dd63a94e8b7f6 100644 --- a/sessionctx/stmtctx/stmtctx.go +++ b/sessionctx/stmtctx/stmtctx.go @@ -204,10 +204,8 @@ type StatementContext struct { // EnableOptimizeTrace indicates whether enable optimizer trace by 'trace plan statement' EnableOptimizeTrace bool - // LogicalOptimizeTrace indicates the trace for optimize - LogicalOptimizeTrace *tracing.LogicalOptimizeTracer - // PhysicalOptimizeTrace indicates the trace for optimize - PhysicalOptimizeTrace *tracing.PhysicalOptimizeTracer + // OptimizeTracer indicates the tracer for optimize + OptimizeTracer *tracing.OptimizeTracer // EnableOptimizerCETrace indicate if cardinality estimation internal process needs to be traced. // CE Trace is currently a submodule of the optimizer trace and is controlled by a separated option. EnableOptimizerCETrace bool diff --git a/util/tracing/opt_trace.go b/util/tracing/opt_trace.go index 614f4bb5caa82..9977bddaa336c 100644 --- a/util/tracing/opt_trace.go +++ b/util/tracing/opt_trace.go @@ -20,20 +20,15 @@ import "fmt" type PlanTrace struct { ID int `json:"id"` TP string `json:"type"` - Children []*PlanTrace `json:"children"` - ChildrenID []int `json:"childrenID"` + Children []*PlanTrace `json:"-"` + ChildrenID []int `json:"children"` Cost float64 `json:"cost"` Selected bool `json:"selected"` ProperType string `json:"property"` - // ExplainInfo should be implemented by each implemented LogicalPlan + // ExplainInfo should be implemented by each implemented Plan ExplainInfo string `json:"info"` } -// SetCost sets cost for PhysicalPlanTrace -func (t *PlanTrace) SetCost(cost float64) { - t.Cost = cost -} - // LogicalOptimizeTracer indicates the trace for the whole logicalOptimize processing type LogicalOptimizeTracer struct { FinalLogicalPlan []*PlanTrace `json:"final"` @@ -161,26 +156,14 @@ func DedupCETrace(records []*CETraceRecord) []*CETraceRecord { return ret } -// PhysicalOptimizeTraceInfo indicates for the PhysicalOptimize trace information -// The essence of the physical optimization stage is a Dynamic Programming(DP). -// So, PhysicalOptimizeTraceInfo is to record the transfer and status information in the DP. -// Each (logicalPlan, property), the so-called state in DP, has its own PhysicalOptimizeTraceInfo. -// The Candidates are possible transfer paths. -// Because DP is performed on the plan tree, -// we need to record the state of each candidate's child node, namely Children. -type PhysicalOptimizeTraceInfo struct { - Property string `json:"property"` - Candidates []*PlanTrace `json:"candidates"` -} - // PhysicalOptimizeTracer indicates the trace for the whole physicalOptimize processing type PhysicalOptimizeTracer struct { // final indicates the final physical plan trace Final []*PlanTrace `json:"final"` SelectedCandidates []*CandidatePlanTrace `json:"selected_candidates"` DiscardedCandidates []*CandidatePlanTrace `json:"discarded_candidates"` - // (logical plan) -> property hashCode -> physical plan candidates - State map[string]map[string]*PhysicalOptimizeTraceInfo `json:"-"` + // (logical plan) -> physical plan codename -> physical plan + State map[string]map[string]*PlanTrace `json:"-"` } // RecordFinalPlanTrace records final physical plan trace @@ -225,15 +208,13 @@ func (tracer *PhysicalOptimizeTracer) buildCandidatesInfo() { for _, node := range tracer.Final { bestKeys[CodecPlanName(node.TP, node.ID)] = struct{}{} } - for logicalKey, tasksInfo := range tracer.State { - for _, taskInfo := range tasksInfo { - for _, candidate := range taskInfo.Candidates { - c := newCandidatePlanTrace(candidate, logicalKey, bestKeys) - if c.Selected { - sCandidates = append(sCandidates, c) - } else { - dCandidates = append(dCandidates, c) - } + for logicalKey, pps := range tracer.State { + for _, pp := range pps { + c := newCandidatePlanTrace(pp, logicalKey, bestKeys) + if c.Selected { + sCandidates = append(sCandidates, c) + } else { + dCandidates = append(dCandidates, c) } } } @@ -252,4 +233,11 @@ type OptimizeTracer struct { Logical *LogicalOptimizeTracer `json:"logical"` // Physical indicates physical plan Physical *PhysicalOptimizeTracer `json:"physical"` + // FinalPlan indicates the plan after post optimize + FinalPlan []*PlanTrace `json:"final"` +} + +// RecordFinalPlan records plan after post optimize +func (tracer *OptimizeTracer) RecordFinalPlan(final *PlanTrace) { + tracer.FinalPlan = toFlattenLogicalPlanTrace(final) }