diff --git a/executor/builder.go b/executor/builder.go index be208f244254f..def1fead41286 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -150,7 +150,7 @@ func (b *executorBuilder) build(p plannercore.Plan) Executor { return b.buildShowDDLJobQueries(v) case *plannercore.ShowSlow: return b.buildShowSlow(v) - case *plannercore.Show: + case *plannercore.PhysicalShow: return b.buildShow(v) case *plannercore.Simple: return b.buildSimple(v) @@ -604,7 +604,7 @@ func (b *executorBuilder) buildExecute(v *plannercore.Execute) Executor { return e } -func (b *executorBuilder) buildShow(v *plannercore.Show) Executor { +func (b *executorBuilder) buildShow(v *plannercore.PhysicalShow) Executor { e := &ShowExec{ baseExecutor: newBaseExecutor(b.ctx, v.Schema(), v.ExplainID()), Tp: v.Tp, diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 0f9991220dfb0..0d89157c5a5ff 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -22,7 +22,6 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/parser/ast" - "github.com/pingcap/parser/auth" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/expression" @@ -367,24 +366,6 @@ type Deallocate struct { Name string } -// Show represents a show plan. -type Show struct { - physicalSchemaProducer - - Tp ast.ShowStmtType // Databases/Tables/Columns/.... - DBName string - Table *ast.TableName // Used for showing columns. - Column *ast.ColumnName // Used for `desc table column`. - IndexName model.CIStr - Flag int // Some flag parsed from sql, such as FULL. - User *auth.UserIdentity // Used for show grants. - Roles []*auth.RoleIdentity // Used for show grants. - Full bool - IfNotExists bool // Used for `show create database if not exists` - - GlobalScope bool // Used by show variables -} - // Set represents a plan for set stmt. type Set struct { baseSchemaProducer diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index cc7952afd10db..390bbe22e2b91 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -68,13 +68,21 @@ func (p *LogicalTableDual) findBestTask(prop *property.PhysicalProperty) (task, return invalidTask, nil } dual := PhysicalTableDual{ - RowCount: p.RowCount, - placeHolder: p.placeHolder, + RowCount: p.RowCount, }.Init(p.ctx, p.stats) dual.SetSchema(p.schema) return &rootTask{p: dual}, nil } +func (p *LogicalShow) findBestTask(prop *property.PhysicalProperty) (task, error) { + if !prop.IsEmpty() { + return invalidTask, nil + } + pShow := PhysicalShow{baseShowContent: p.baseShowContent}.Init(p.ctx) + pShow.SetSchema(p.schema) + return &rootTask{p: pShow}, nil +} + // findBestTask implements LogicalPlan interface. func (p *baseLogicalPlan) findBestTask(prop *property.PhysicalProperty) (bestTask task, err error) { // If p is an inner plan in an IndexJoin, the IndexJoin will generate an inner plan by itself, diff --git a/planner/core/initialize.go b/planner/core/initialize.go index ff04a43630ddb..d0f9dddfeb958 100644 --- a/planner/core/initialize.go +++ b/planner/core/initialize.go @@ -267,8 +267,14 @@ func (p Insert) Init(ctx sessionctx.Context) *Insert { return &p } -// Init initializes Show. -func (p Show) Init(ctx sessionctx.Context) *Show { +// Init initializes LogicalShow. +func (p LogicalShow) Init(ctx sessionctx.Context) *LogicalShow { + p.baseLogicalPlan = newBaseLogicalPlan(ctx, TypeShow, &p) + return &p +} + +// Init initializes PhysicalShow. +func (p PhysicalShow) Init(ctx sessionctx.Context) *PhysicalShow { p.basePhysicalPlan = newBasePhysicalPlan(ctx, TypeShow, &p) // Just use pseudo stats to avoid panic. p.stats = &property.StatsInfo{RowCount: 1} diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index d59502c0efa95..4020c1d5facc6 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -938,7 +938,7 @@ func (s *testPlanSuite) TestPlanBuilder(c *C) { }, { sql: "show columns from t where `Key` = 'pri' like 't*'", - plan: "Show->Sel([eq(cast(key), 0)])", + plan: "Show->Sel([eq(cast(key), 0)])->Projection", }, { sql: "do sleep(5)", diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 5631d00dbea77..3f92f200a6956 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -17,6 +17,7 @@ import ( "math" "github.com/pingcap/parser/ast" + "github.com/pingcap/parser/auth" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/expression" @@ -316,10 +317,6 @@ type LogicalTableDual struct { logicalSchemaProducer RowCount int - // placeHolder indicates if this dual plan is a place holder in query optimization - // for data sources like `Show`, if true, the dual plan would be substituted by - // `Show` in the final plan. - placeHolder bool } // LogicalUnionScan is only used in non read-only txn. @@ -781,3 +778,24 @@ func extractCorColumnsBySchema(p LogicalPlan, schema *expression.Schema) []*expr } return resultCorCols[:length] } + +type baseShowContent struct { + Tp ast.ShowStmtType // Databases/Tables/Columns/.... + DBName string + Table *ast.TableName // Used for showing columns. + Column *ast.ColumnName // Used for `desc table column`. + IndexName model.CIStr + Flag int // Some flag parsed from sql, such as FULL. + User *auth.UserIdentity // Used for show grants. + Roles []*auth.RoleIdentity // Used for show grants. + Full bool + IfNotExists bool // Used for `show create database if not exists`. + + GlobalScope bool // Used by show variables. +} + +// LogicalShow represents a show plan. +type LogicalShow struct { + logicalSchemaProducer + baseShowContent +} diff --git a/planner/core/physical_plans.go b/planner/core/physical_plans.go index 644bd215c3e68..aeb16a5a14243 100644 --- a/planner/core/physical_plans.go +++ b/planner/core/physical_plans.go @@ -407,10 +407,6 @@ type PhysicalTableDual struct { physicalSchemaProducer RowCount int - // placeHolder indicates if this dual plan is a place holder in query optimization - // for data sources like `Show`, if true, the dual plan would be substituted by - // `Show` in the final plan. - placeHolder bool // names is used for OutputNames() method. Dual may be inited when building point get plan. // So it needs to hold names for itself. @@ -455,6 +451,13 @@ func CollectPlanStatsVersion(plan PhysicalPlan, statsInfos map[string]uint64) ma return statsInfos } +// PhysicalShow represents a show plan. +type PhysicalShow struct { + physicalSchemaProducer + + baseShowContent +} + // BuildMergeJoinPlan builds a PhysicalMergeJoin from the given fields. Currently, it is only used for test purpose. func BuildMergeJoinPlan(ctx sessionctx.Context, joinType JoinType, leftKeys, rightKeys []*expression.Column) *PhysicalMergeJoin { baseJoin := basePhysicalJoin{ diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index fb619967bc476..de77f426440e5 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -1361,18 +1361,20 @@ func splitWhere(where ast.ExprNode) []ast.ExprNode { } func (b *PlanBuilder) buildShow(ctx context.Context, show *ast.ShowStmt) (Plan, error) { - p := Show{ - Tp: show.Tp, - DBName: show.DBName, - Table: show.Table, - Column: show.Column, - IndexName: show.IndexName, - Flag: show.Flag, - Full: show.Full, - User: show.User, - Roles: show.Roles, - IfNotExists: show.IfNotExists, - GlobalScope: show.GlobalScope, + p := LogicalShow{ + baseShowContent: baseShowContent{ + Tp: show.Tp, + DBName: show.DBName, + Table: show.Table, + Column: show.Column, + IndexName: show.IndexName, + Flag: show.Flag, + Full: show.Full, + User: show.User, + Roles: show.Roles, + IfNotExists: show.IfNotExists, + GlobalScope: show.GlobalScope, + }, }.Init(b.ctx) isView := false switch show.Tp { @@ -1398,11 +1400,9 @@ func (b *PlanBuilder) buildShow(ctx context.Context, show *ast.ShowStmt) (Plan, for _, col := range p.schema.Columns { col.UniqueID = b.ctx.GetSessionVars().AllocPlanColumnID() } - mockTablePlan := LogicalTableDual{placeHolder: true}.Init(b.ctx) - mockTablePlan.SetSchema(p.schema) var err error var np LogicalPlan - np = mockTablePlan + np = p if show.Pattern != nil { show.Pattern.Expr = &ast.ColumnNameExpr{ Name: &ast.ColumnName{Name: p.Schema().Columns[0].ColName}, @@ -1418,11 +1418,12 @@ func (b *PlanBuilder) buildShow(ctx context.Context, show *ast.ShowStmt) (Plan, return nil, err } } - if np != mockTablePlan { - fieldsLen := len(mockTablePlan.schema.Columns) + if np != p { + b.optFlag |= flagEliminateProjection + fieldsLen := len(p.schema.Columns) proj := LogicalProjection{Exprs: make([]expression.Expression, 0, fieldsLen)}.Init(b.ctx) schema := expression.NewSchema(make([]*expression.Column, 0, fieldsLen)...) - for _, col := range mockTablePlan.schema.Columns { + for _, col := range p.schema.Columns { proj.Exprs = append(proj.Exprs, col) newCol := col.Clone().(*expression.Column) newCol.UniqueID = b.ctx.GetSessionVars().AllocPlanColumnID() @@ -1430,26 +1431,11 @@ func (b *PlanBuilder) buildShow(ctx context.Context, show *ast.ShowStmt) (Plan, } proj.SetSchema(schema) proj.SetChildren(np) - physical, err := DoOptimize(ctx, b.optFlag|flagEliminateProjection, proj) - if err != nil { - return nil, err - } - return substitutePlaceHolderDual(physical, p), nil + return proj, nil } return p, nil } -func substitutePlaceHolderDual(src PhysicalPlan, dst PhysicalPlan) PhysicalPlan { - if dual, ok := src.(*PhysicalTableDual); ok && dual.placeHolder { - return dst - } - for i, child := range src.Children() { - newChild := substitutePlaceHolderDual(child, dst) - src.SetChild(i, newChild) - } - return src -} - func (b *PlanBuilder) buildSimple(node ast.StmtNode) (Plan, error) { p := &Simple{Statement: node} diff --git a/planner/core/stats.go b/planner/core/stats.go index 3a93791c6d2e3..e4fa4fde052af 100644 --- a/planner/core/stats.go +++ b/planner/core/stats.go @@ -41,6 +41,20 @@ func (p *LogicalTableDual) DeriveStats(childStats []*property.StatsInfo) (*prope return p.stats, nil } +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalShow) DeriveStats(childStats []*property.StatsInfo) (*property.StatsInfo, error) { + // A fake count, just to avoid panic now. + profile := &property.StatsInfo{ + RowCount: 1, + Cardinality: make([]float64, p.Schema().Len()), + } + for i := range profile.Cardinality { + profile.Cardinality[i] = 1 + } + p.stats = profile + return p.stats, nil +} + func (p *baseLogicalPlan) recursiveDeriveStats() (*property.StatsInfo, error) { if p.stats != nil { return p.stats, nil diff --git a/planner/core/stringer.go b/planner/core/stringer.go index 02b336a70ec86..34aedf991bf5c 100644 --- a/planner/core/stringer.go +++ b/planner/core/stringer.go @@ -113,7 +113,7 @@ func toString(in Plan, strs []string, idxs []int) ([]string, []int) { str = "Lock" case *ShowDDL: str = "ShowDDL" - case *Show: + case *LogicalShow, *PhysicalShow: str = "Show" case *LogicalSort, *PhysicalSort: str = "Sort"