From 3e0e7d5a69c089e100501c4235b01f517b269b34 Mon Sep 17 00:00:00 2001 From: Kenan Yao Date: Tue, 20 Nov 2018 14:10:24 +0800 Subject: [PATCH] plan: support subquery in `Do` statement (#8343) --- executor/executor_test.go | 13 ++++++++++ planner/core/physical_plan_test.go | 31 +++++++++++++++++++++++ planner/core/planbuilder.go | 24 +++++++++--------- planner/core/rule_column_pruning.go | 5 ++-- planner/core/rule_eliminate_projection.go | 4 +++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index eec74f41ac546..d289cad4993e9 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -3329,3 +3329,16 @@ func (s *testSuite) TestRowID(c *C) { tk.MustExec(`insert into t values('a')`) tk.MustQuery("select *, _tidb_rowid from t use index(`primary`) where _tidb_rowid=1").Check(testkit.Rows("a 1")) } + +func (s *testSuite) TestDoSubquery(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a int)`) + _, err := tk.Exec(`do 1 in (select * from t)`) + c.Assert(err, IsNil, Commentf("err %v", err)) + tk.MustExec(`insert into t values(1)`) + r, err := tk.Exec(`do 1 in (select * from t)`) + c.Assert(err, IsNil, Commentf("err %v", err)) + c.Assert(r, IsNil, Commentf("result of Do not empty")) +} diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index dd1799228d6f1..12bf1c62e5b25 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -1327,3 +1327,34 @@ func (s *testPlanSuite) TestIndexJoinUnionScan(c *C) { c.Assert(core.ToString(p), Equals, tt.best, comment) } } + +func (s *testPlanSuite) TestDoSubquery(c *C) { + defer testleak.AfterTest(c)() + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + defer func() { + dom.Close() + store.Close() + }() + se, err := session.CreateSession4Test(store) + c.Assert(err, IsNil) + _, err = se.Execute(context.Background(), "use test") + c.Assert(err, IsNil) + tests := []struct { + sql string + best string + }{ + { + sql: "do 1 in (select a from t)", + best: "LeftHashJoin{Dual->TableReader(Table(t))}->Projection", + }, + } + for _, tt := range tests { + comment := Commentf("for %s", tt.sql) + stmt, err := s.ParseOneStmt(tt.sql, "", "") + c.Assert(err, IsNil, comment) + p, err := planner.Optimize(se, stmt, s.is) + c.Assert(err, IsNil) + c.Assert(core.ToString(p), Equals, tt.best, comment) + } +} diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index d1934c86b5d40..56ebb71f47816 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -214,29 +214,29 @@ func (b *PlanBuilder) buildExecute(v *ast.ExecuteStmt) (Plan, error) { } func (b *PlanBuilder) buildDo(v *ast.DoStmt) (Plan, error) { + var p LogicalPlan dual := LogicalTableDual{RowCount: 1}.Init(b.ctx) - - p := LogicalProjection{Exprs: make([]expression.Expression, 0, len(v.Exprs))}.Init(b.ctx) + dual.SetSchema(expression.NewSchema()) + p = dual + proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(v.Exprs))}.Init(b.ctx) schema := expression.NewSchema(make([]*expression.Column, 0, len(v.Exprs))...) for _, astExpr := range v.Exprs { - expr, _, err := b.rewrite(astExpr, dual, nil, true) + expr, np, err := b.rewrite(astExpr, p, nil, true) if err != nil { return nil, errors.Trace(err) } - p.Exprs = append(p.Exprs, expr) + p = np + proj.Exprs = append(proj.Exprs, expr) schema.Append(&expression.Column{ UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), RetType: expr.GetType(), }) } - if dual.schema == nil { - dual.schema = expression.NewSchema() - } - p.SetChildren(dual) - p.self = p - p.SetSchema(schema) - p.calculateNoDelay = true - return p, nil + proj.SetChildren(p) + proj.self = proj + proj.SetSchema(schema) + proj.calculateNoDelay = true + return proj, nil } func (b *PlanBuilder) buildSet(v *ast.SetStmt) (Plan, error) { diff --git a/planner/core/rule_column_pruning.go b/planner/core/rule_column_pruning.go index b96ed9e76a314..05b239ee3baf7 100644 --- a/planner/core/rule_column_pruning.go +++ b/planner/core/rule_column_pruning.go @@ -14,11 +14,12 @@ package core import ( + "fmt" + "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/infoschema" - log "github.com/sirupsen/logrus" ) type columnPruner struct { @@ -34,7 +35,7 @@ func getUsedList(usedCols []*expression.Column, schema *expression.Schema) []boo for _, col := range usedCols { idx := schema.ColumnIndex(col) if idx == -1 { - log.Errorf("Can't find column %s from schema %s.", col, schema) + panic(fmt.Sprintf("Can't find column %s from schema %s.", col, schema)) } used[idx] = true } diff --git a/planner/core/rule_eliminate_projection.go b/planner/core/rule_eliminate_projection.go index 2060e7dd64ff0..3e70bdb998327 100644 --- a/planner/core/rule_eliminate_projection.go +++ b/planner/core/rule_eliminate_projection.go @@ -32,6 +32,10 @@ func canProjectionBeEliminatedLoose(p *LogicalProjection) bool { // canProjectionBeEliminatedStrict checks whether a projection can be // eliminated, returns true if the projection just copy its child's output. func canProjectionBeEliminatedStrict(p *PhysicalProjection) bool { + // If this projection is specially added for `DO`, we keep it. + if p.CalculateNoDelay == true { + return false + } if p.Schema().Len() == 0 { return true }