diff --git a/executor/executor_test.go b/executor/executor_test.go index 878cfd56c9857..c2f9a73a92740 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -3435,3 +3435,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 6854c1df3e8a2..97d3cb82cddbd 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -1303,3 +1303,34 @@ func (s *testPlanSuite) TestIndexLookupCartesianJoin(c *C) { lastWarn := warnings[len(warnings)-1] c.Assert(lastWarn.Err.Error(), Equals, "[planner:1815]Optimizer Hint /*+ TIDB_INLJ(t1, t2) */ is inapplicable without column equal ON condition") } + +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 := core.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 9d40429c81952..475f353c769c8 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -210,26 +210,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(), }) } - 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 d74132806d4e7..999862bd9510a 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 9a3658e579364..c10a0cfb00b96 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 }