diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index ff90a92b9b497..b85cae768c0d7 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -37,6 +37,7 @@ import ( "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/ranger" "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/util/tracing" "go.uber.org/zap" ) @@ -136,7 +137,7 @@ func GetPropByOrderByItemsContainScalarFunc(items []*util.ByItems) (*property.Ph return &property.PhysicalProperty{SortItems: propItems}, true, onlyColumn } -func (p *LogicalTableDual) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (task, int64, error) { +func (p *LogicalTableDual) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (task, int64, error) { // If the required property is not empty and the row count > 1, // we cannot ensure this required property. // But if the row count is 0 or 1, we don't need to care about the property. @@ -151,7 +152,7 @@ func (p *LogicalTableDual) findBestTask(prop *property.PhysicalProperty, planCou return &rootTask{p: dual, isEmpty: p.RowCount == 0}, 1, nil } -func (p *LogicalShow) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (task, int64, error) { +func (p *LogicalShow) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (task, int64, error) { if !prop.IsEmpty() || planCounter.Empty() { return invalidTask, 0, nil } @@ -161,7 +162,7 @@ func (p *LogicalShow) findBestTask(prop *property.PhysicalProperty, planCounter return &rootTask{p: pShow}, 1, nil } -func (p *LogicalShowDDLJobs) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (task, int64, error) { +func (p *LogicalShowDDLJobs) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (task, int64, error) { if !prop.IsEmpty() || planCounter.Empty() { return invalidTask, 0, nil } @@ -172,7 +173,7 @@ func (p *LogicalShowDDLJobs) findBestTask(prop *property.PhysicalProperty, planC } // rebuildChildTasks rebuilds the childTasks to make the clock_th combination. -func (p *baseLogicalPlan) rebuildChildTasks(childTasks *[]task, pp PhysicalPlan, childCnts []int64, planCounter int64, TS uint64) error { +func (p *baseLogicalPlan) rebuildChildTasks(childTasks *[]task, pp PhysicalPlan, childCnts []int64, planCounter int64, TS uint64, opt *physicalOptimizeOp) error { // The taskMap of children nodes should be rolled back first. for _, child := range p.children { child.rollBackTaskMap(TS) @@ -187,7 +188,7 @@ func (p *baseLogicalPlan) rebuildChildTasks(childTasks *[]task, pp PhysicalPlan, for j, child := range p.children { multAll /= childCnts[j] curClock = PlanCounterTp((planCounter-1)/multAll + 1) - childTask, _, err := child.findBestTask(pp.GetChildReqProps(j), &curClock) + childTask, _, err := child.findBestTask(pp.GetChildReqProps(j), &curClock, opt) planCounter = (planCounter-1)%multAll + 1 if err != nil { return err @@ -203,13 +204,14 @@ func (p *baseLogicalPlan) rebuildChildTasks(childTasks *[]task, pp PhysicalPlan, return nil } -func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPlan, prop *property.PhysicalProperty, addEnforcer bool, planCounter *PlanCounterTp) (task, int64, error) { +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 childTasks := make([]task, 0, len(p.children)) childCnts := make([]int64, len(p.children)) cntPlan = 0 for _, pp := range physicalPlans { + candidateInfo := opt.appendCandidate(p, pp, prop.String()) // Find best child tasks firstly. childTasks = childTasks[:0] // The curCntPlan records the number of possible plans for pp @@ -217,7 +219,8 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl TimeStampNow := p.GetLogicalTS4TaskMap() savedPlanID := p.ctx.GetSessionVars().PlanID for j, child := range p.children { - childTask, cnt, err := child.findBestTask(pp.GetChildReqProps(j), &PlanCounterDisabled) + childProp := pp.GetChildReqProps(j) + childTask, cnt, err := child.findBestTask(childProp, &PlanCounterDisabled, opt) childCnts[j] = cnt if err != nil { return nil, 0, err @@ -227,6 +230,9 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl break } childTasks = append(childTasks, childTask) + if opt != nil && childTask != nil { + opt.appendChildToCandidate(candidateInfo, childTask.plan()) + } } // This check makes sure that there is no invalid child task. @@ -238,7 +244,7 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl if planCounter.IsForce() && int64(*planCounter) <= curCntPlan { p.ctx.GetSessionVars().PlanID = savedPlanID curCntPlan = int64(*planCounter) - err := p.rebuildChildTasks(&childTasks, pp, childCnts, int64(*planCounter), TimeStampNow) + err := p.rebuildChildTasks(&childTasks, pp, childCnts, int64(*planCounter), TimeStampNow, opt) if err != nil { return nil, 0, err } @@ -274,6 +280,9 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl bestTask = curTask break } + if candidateInfo != nil { + candidateInfo.SetCost(curTask.cost()) + } // Get the most efficient one. if curTask.cost() < bestTask.cost() || (bestTask.invalid() && !curTask.invalid()) { bestTask = curTask @@ -282,8 +291,71 @@ func (p *baseLogicalPlan) enumeratePhysicalPlans4Task(physicalPlans []PhysicalPl return bestTask, cntPlan, nil } +type physicalOptimizeOp struct { + // tracer is goring to track optimize steps during physical optimizing + tracer *tracing.PhysicalOptimizeTracer +} + +func defaultPhysicalOptimizeOption() *physicalOptimizeOp { + return &physicalOptimizeOp{} +} + +func (op *physicalOptimizeOp) withEnableOptimizeTracer(tracer *tracing.PhysicalOptimizeTracer) *physicalOptimizeOp { + op.tracer = tracer + return op +} + +func (op *physicalOptimizeOp) buildPhysicalOptimizeTraceInfo(p LogicalPlan, prop string) *tracing.PhysicalOptimizeTraceInfo { + if op == nil || op.tracer == nil { + return nil + } + 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 + } + traceInfo := &tracing.PhysicalOptimizeTraceInfo{Property: prop} + op.tracer.State[name][prop] = traceInfo + return traceInfo +} + +func (op *physicalOptimizeOp) appendChildToCandidate(candidateInfo *tracing.PhysicalPlanTrace, plan PhysicalPlan) { + if op == nil || op.tracer == nil || candidateInfo == nil { + return + } + childPhysicalPlanTrace := &tracing.PhysicalPlanTrace{TP: plan.TP(), ID: plan.ID(), Info: plan.ExplainInfo(), Cost: plan.Cost()} + candidateInfo.Children = append(candidateInfo.Children, childPhysicalPlanTrace) +} + +func (op *physicalOptimizeOp) setBest(lp LogicalPlan, pp PhysicalPlan, prop string) { + if op == nil || op.tracer == nil { + return + } + traceInfo := op.tracer.State[fmt.Sprintf("%v_%v", lp.TP(), lp.ID())][prop] + if traceInfo == nil { + return + } + traceInfo.BestTask = &tracing.PhysicalPlanTrace{ID: pp.ID(), TP: pp.TP(), Info: pp.ExplainInfo(), Cost: pp.Cost()} +} + +func (op *physicalOptimizeOp) appendCandidate(logicalPlan *baseLogicalPlan, physicalPlan PhysicalPlan, prop string) *tracing.PhysicalPlanTrace { + if op == nil || op.tracer == nil { + return nil + } + PhysicalPlanTrace := &tracing.PhysicalPlanTrace{TP: physicalPlan.TP(), ID: physicalPlan.ID(), Info: physicalPlan.ExplainInfo()} + name := tracing.CodecPlanName(logicalPlan.TP(), logicalPlan.ID()) + traceInfo := op.tracer.State[name][prop] + if traceInfo == nil { + return nil + } + traceInfo.Candidates = append(traceInfo.Candidates, PhysicalPlanTrace) + return PhysicalPlanTrace +} + // findBestTask implements LogicalPlan interface. -func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (bestTask task, cntPlan int64, err error) { +func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (bestTask task, cntPlan int64, err error) { // If p is an inner plan in an IndexJoin, the IndexJoin will generate an inner plan by itself, // and set inner child prop nil, so here we do nothing. if prop == nil { @@ -353,7 +425,10 @@ func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCoun var cnt int64 var curTask task - if bestTask, cnt, err = p.enumeratePhysicalPlans4Task(plansFitsProp, newProp, false, planCounter); err != nil { + if opt != nil { + opt.buildPhysicalOptimizeTraceInfo(p, newProp.String()) + } + if bestTask, cnt, err = p.enumeratePhysicalPlans4Task(plansFitsProp, newProp, false, planCounter, opt); err != nil { return nil, 0, err } cntPlan += cnt @@ -361,7 +436,7 @@ func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCoun goto END } - curTask, cnt, err = p.enumeratePhysicalPlans4Task(plansNeedEnforce, newProp, true, planCounter) + curTask, cnt, err = p.enumeratePhysicalPlans4Task(plansNeedEnforce, newProp, true, planCounter, opt) if err != nil { return nil, 0, err } @@ -376,10 +451,13 @@ func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty, planCoun END: p.storeTask(prop, bestTask) + if opt != nil { + opt.setBest(p.self, bestTask.plan(), prop.String()) + } return bestTask, cntPlan, nil } -func (p *LogicalMemTable) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (t task, cntPlan int64, err error) { +func (p *LogicalMemTable) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (t task, cntPlan int64, err error) { if !prop.IsEmpty() || planCounter.Empty() { return invalidTask, 0, nil } @@ -689,7 +767,7 @@ func (ds *DataSource) isPointGetConvertableSchema() bool { // findBestTask implements the PhysicalPlan interface. // It will enumerate all the available indices and choose a plan with least cost. -func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (t task, cntPlan int64, err error) { +func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (t task, cntPlan int64, err error) { // If ds is an inner plan in an IndexJoin, the IndexJoin will generate an inner plan by itself, // and set inner child prop nil, so here we do nothing. if prop == nil { @@ -710,7 +788,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter if prop.CanAddEnforcer { // First, get the bestTask without enforced prop prop.CanAddEnforcer = false - t, cnt, err = ds.findBestTask(prop, planCounter) + t, cnt, err = ds.findBestTask(prop, planCounter, opt) if err != nil { return nil, 0, err } @@ -2169,7 +2247,7 @@ func (ds *DataSource) getOriginalPhysicalIndexScan(prop *property.PhysicalProper return is, cost, rowCount } -func (p *LogicalCTE) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (t task, cntPlan int64, err error) { +func (p *LogicalCTE) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (t task, cntPlan int64, err error) { if !prop.IsEmpty() && !prop.CanAddEnforcer { return invalidTask, 1, nil } @@ -2187,7 +2265,7 @@ func (p *LogicalCTE) findBestTask(prop *property.PhysicalProperty, planCounter * return t, 1, nil } -func (p *LogicalCTETable) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (t task, cntPlan int64, err error) { +func (p *LogicalCTETable) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (t task, cntPlan int64, err error) { if !prop.IsEmpty() { return nil, 1, nil } diff --git a/planner/core/find_best_task_test.go b/planner/core/find_best_task_test.go index 893f20bc93442..f2cf02abfa133 100644 --- a/planner/core/find_best_task_test.go +++ b/planner/core/find_best_task_test.go @@ -34,7 +34,7 @@ func (ds mockDataSource) Init(ctx sessionctx.Context) *mockDataSource { return &ds } -func (ds *mockDataSource) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (task, int64, error) { +func (ds *mockDataSource) findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, opt *physicalOptimizeOp) (task, int64, error) { // It can satisfy any of the property! // Just use a TableDual for convenience. p := PhysicalTableDual{}.Init(ds.ctx, &property.StatsInfo{RowCount: 1}, 0) @@ -144,7 +144,7 @@ func TestCostOverflow(t *testing.T) { mockPlan.SetChildren(mockDS) // An empty property is enough for this test. prop := property.NewPhysicalProperty(property.RootTaskType, nil, false, 0, false) - task, _, err := mockPlan.findBestTask(prop, &PlanCounterDisabled) + task, _, err := mockPlan.findBestTask(prop, &PlanCounterDisabled, defaultPhysicalOptimizeOption()) require.NoError(t, err) // The cost should be overflowed, but the task shouldn't be invalid. require.False(t, task.invalid()) @@ -171,7 +171,7 @@ func TestEnforcedProperty(t *testing.T) { CanAddEnforcer: false, } // should return invalid task because no physical plan can match this property. - task, _, err := mockPlan.findBestTask(prop0, &PlanCounterDisabled) + task, _, err := mockPlan.findBestTask(prop0, &PlanCounterDisabled, defaultPhysicalOptimizeOption()) require.NoError(t, err) require.True(t, task.invalid()) @@ -180,7 +180,7 @@ func TestEnforcedProperty(t *testing.T) { CanAddEnforcer: true, } // should return the valid task when the property is enforced. - task, _, err = mockPlan.findBestTask(prop1, &PlanCounterDisabled) + task, _, err = mockPlan.findBestTask(prop1, &PlanCounterDisabled, defaultPhysicalOptimizeOption()) require.NoError(t, err) require.False(t, task.invalid()) } @@ -203,7 +203,7 @@ func TestHintCannotFitProperty(t *testing.T) { SortItems: items, CanAddEnforcer: true, } - task, _, err := mockPlan0.findBestTask(prop0, &PlanCounterDisabled) + task, _, err := mockPlan0.findBestTask(prop0, &PlanCounterDisabled, defaultPhysicalOptimizeOption()) require.NoError(t, err) require.False(t, task.invalid()) _, enforcedSort := task.plan().(*PhysicalSort) @@ -219,7 +219,7 @@ func TestHintCannotFitProperty(t *testing.T) { SortItems: items, CanAddEnforcer: false, } - task, _, err = mockPlan0.findBestTask(prop1, &PlanCounterDisabled) + task, _, err = mockPlan0.findBestTask(prop1, &PlanCounterDisabled, defaultPhysicalOptimizeOption()) require.NoError(t, err) require.False(t, task.invalid()) _, enforcedSort = task.plan().(*PhysicalSort) @@ -240,7 +240,7 @@ func TestHintCannotFitProperty(t *testing.T) { canGeneratePlan2: false, }.Init(ctx) mockPlan1.SetChildren(mockDS) - task, _, err = mockPlan1.findBestTask(prop2, &PlanCounterDisabled) + task, _, err = mockPlan1.findBestTask(prop2, &PlanCounterDisabled, defaultPhysicalOptimizeOption()) require.NoError(t, err) require.False(t, task.invalid()) require.Equal(t, uint16(1), ctx.GetSessionVars().StmtCtx.WarningCount()) @@ -256,7 +256,7 @@ func TestHintCannotFitProperty(t *testing.T) { SortItems: items, CanAddEnforcer: true, } - task, _, err = mockPlan1.findBestTask(prop3, &PlanCounterDisabled) + task, _, err = mockPlan1.findBestTask(prop3, &PlanCounterDisabled, defaultPhysicalOptimizeOption()) require.NoError(t, err) require.False(t, task.invalid()) require.Equal(t, uint16(1), ctx.GetSessionVars().StmtCtx.WarningCount()) diff --git a/planner/core/optimizer.go b/planner/core/optimizer.go index fc731d1d735c9..9b21aa90b3b30 100644 --- a/planner/core/optimizer.go +++ b/planner/core/optimizer.go @@ -452,8 +452,18 @@ func physicalOptimize(logic LogicalPlan, planCounter *PlanCounterTp) (PhysicalPl ExpectedCnt: math.MaxFloat64, } + opt := defaultPhysicalOptimizeOption() + stmtCtx := logic.SCtx().GetSessionVars().StmtCtx + if stmtCtx.EnableOptimizeTrace { + tracer := &tracing.PhysicalOptimizeTracer{State: make(map[string]map[string]*tracing.PhysicalOptimizeTraceInfo)} + opt = opt.withEnableOptimizeTracer(tracer) + defer func() { + stmtCtx.PhysicalOptimizeTrace = tracer + }() + } + logic.SCtx().GetSessionVars().StmtCtx.TaskMapBakTS = 0 - t, _, err := logic.findBestTask(prop, planCounter) + t, _, err := logic.findBestTask(prop, planCounter, opt) if err != nil { return nil, 0, err } diff --git a/planner/core/physical_plan_trace_test.go b/planner/core/physical_plan_trace_test.go new file mode 100644 index 0000000000000..c47c51dbf07ae --- /dev/null +++ b/planner/core/physical_plan_trace_test.go @@ -0,0 +1,92 @@ +// Copyright 2021 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package core_test + +import ( + "context" + "sort" + "strings" + "testing" + + "github.com/pingcap/tidb/domain" + "github.com/pingcap/tidb/parser" + "github.com/pingcap/tidb/planner/core" + "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/util/tracing" + "github.com/stretchr/testify/require" +) + +func TestPhysicalOptimizeWithTraceEnabled(t *testing.T) { + p := parser.New() + store, dom, clean := testkit.CreateMockStoreAndDomain(t) + defer clean() + + 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)" + + 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, bests := getList(otrace) + require.True(t, checkList(logicalList, []string{"Projection_3", "Selection_2"})) + require.True(t, checkList(physicalList, []string{"Projection_4", "Selection_5"})) + require.True(t, checkList(bests, []string{"Projection_4", "Selection_5"})) +} + +func checkList(d []string, s []string) bool { + if len(d) != len(s) { + return false + } + for i := 0; i < len(d); i++ { + if strings.Compare(d[i], s[i]) != 0 { + return false + } + } + return true +} + +func getList(otrace *tracing.PhysicalOptimizeTracer) (ll []string, pl []string, bests []string) { + for logicalPlan, v := range otrace.State { + ll = append(ll, logicalPlan) + for _, info := range v { + bests = append(bests, tracing.CodecPlanName(info.BestTask.TP, info.BestTask.ID)) + for _, task := range info.Candidates { + pl = append(pl, tracing.CodecPlanName(task.TP, task.ID)) + } + } + } + sort.Strings(ll) + sort.Strings(pl) + sort.Strings(bests) + return ll, pl, bests +} diff --git a/planner/core/plan.go b/planner/core/plan.go index 46b20ac6ae43a..533f349dc7f1d 100644 --- a/planner/core/plan.go +++ b/planner/core/plan.go @@ -250,7 +250,7 @@ type LogicalPlan interface { // If planCounter > 0, the clock_th plan generated in this function will be returned. // If planCounter = 0, the plan generated in this function will not be considered. // If planCounter = -1, then we will not force plan. - findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp) (task, int64, error) + findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, op *physicalOptimizeOp) (task, int64, error) // BuildKeyInfo will collect the information of unique keys into schema. // Because this method is also used in cascades planner, we cannot use diff --git a/sessionctx/stmtctx/stmtctx.go b/sessionctx/stmtctx/stmtctx.go index 862a3c7dba6f8..65476c5f5b7d5 100644 --- a/sessionctx/stmtctx/stmtctx.go +++ b/sessionctx/stmtctx/stmtctx.go @@ -205,6 +205,8 @@ type StatementContext struct { EnableOptimizeTrace bool // LogicalOptimizeTrace indicates the trace for optimize LogicalOptimizeTrace *tracing.LogicalOptimizeTracer + // PhysicalOptimizeTrace indicates the trace for optimize + PhysicalOptimizeTrace *tracing.PhysicalOptimizeTracer // 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 506db98ee0d8d..52a7499036f26 100644 --- a/util/tracing/opt_trace.go +++ b/util/tracing/opt_trace.go @@ -14,6 +14,8 @@ package tracing +import "fmt" + // LogicalPlanTrace indicates for the LogicalPlan trace information type LogicalPlanTrace struct { ID int @@ -147,3 +149,41 @@ 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"` + BestTask *PhysicalPlanTrace `json:"best"` + Candidates []*PhysicalPlanTrace `json:"candidates"` +} + +// PhysicalPlanTrace records each generated physical plan during findBestTask +type PhysicalPlanTrace struct { + ID int `json:"id"` + TP string `json:"type"` + Cost float64 `json:"cost"` + Info string `json:"info"` + Children []*PhysicalPlanTrace `json:"children"` +} + +// SetCost sets cost for PhysicalPlanTrace +func (t *PhysicalPlanTrace) SetCost(cost float64) { + t.Cost = cost +} + +// PhysicalOptimizeTracer indicates the trace for the whole physicalOptimize processing +type PhysicalOptimizeTracer struct { + // (logical plan) -> property -> physical plan candidates + State map[string]map[string]*PhysicalOptimizeTraceInfo +} + +// CodecPlanName returns tp_id of plan. +func CodecPlanName(tp string, id int) string { + return fmt.Sprintf("%v_%v", tp, id) +}