Skip to content

Commit

Permalink
planner: support building data source from View (#8757)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewDi authored and XuHuaiyu committed Dec 26, 2018
1 parent 7ffdbca commit 123aba2
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 1 deletion.
35 changes: 35 additions & 0 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3427,6 +3427,41 @@ func (s *testSuite3) TestSelectHashPartitionTable(c *C) {
))
}

func (s *testSuite) TestSelectView(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec("create table view_t (a int,b int)")
tk.MustExec("insert into view_t values(1,2)")
tk.MustExec("create view view1 as select * from view_t")
tk.MustExec("create view view2(c,d) as select * from view_t")
tk.MustExec("create view view3(c,d) as select a,b from view_t")
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
tk.MustExec("drop table view_t;")
tk.MustExec("create table view_t(c int,d int)")
_, err := tk.Exec("select * from view1")
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view1").Error())
_, err = tk.Exec("select * from view2")
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view2").Error())
_, err = tk.Exec("select * from view3")
c.Assert(err.Error(), Equals, "[planner:1054]Unknown column 'a' in 'field list'")
tk.MustExec("drop table view_t;")
tk.MustExec("create table view_t(a int,b int,c int)")
tk.MustExec("insert into view_t values(1,2,3)")
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
tk.MustExec("alter table view_t drop column a")
tk.MustExec("alter table view_t add column a int after b")
tk.MustExec("update view_t set a=1;")
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
tk.MustExec("drop table view_t;")
tk.MustExec("drop view view1,view2,view3;")
}

type testSuite2 struct {
cluster *mocktikv.Cluster
mvccStore mocktikv.MVCCStore
Expand Down
1 change: 1 addition & 0 deletions planner/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ var (
ErrMixOfGroupFuncAndFields = terror.ClassOptimizer.New(codeMixOfGroupFuncAndFields, "In aggregated query without GROUP BY, expression #%d of SELECT list contains nonaggregated column '%s'; this is incompatible with sql_mode=only_full_group_by")
ErrNonUniqTable = terror.ClassOptimizer.New(codeNonUniqTable, mysql.MySQLErrName[mysql.ErrNonuniqTable])
ErrWrongValueCountOnRow = terror.ClassOptimizer.New(mysql.ErrWrongValueCountOnRow, mysql.MySQLErrName[mysql.ErrWrongValueCountOnRow])
ErrViewInvalid = terror.ClassOptimizer.New(mysql.ErrViewInvalid, mysql.MySQLErrName[mysql.ErrViewInvalid])
)

func init() {
Expand Down
40 changes: 40 additions & 0 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1902,6 +1902,10 @@ func (b *PlanBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error) {
tableInfo := tbl.Meta()
b.visitInfo = appendVisitInfo(b.visitInfo, mysql.SelectPriv, dbName.L, tableInfo.Name.L, "")

if tableInfo.IsView() {
return b.buildDataSourceFromView(dbName, tableInfo)
}

if tableInfo.GetPartitionInfo() != nil {
b.optFlag = b.optFlag | flagPartitionProcessor
}
Expand Down Expand Up @@ -1994,6 +1998,42 @@ func (b *PlanBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error) {
return result, nil
}

func (b *PlanBuilder) buildDataSourceFromView(dbName model.CIStr, tableInfo *model.TableInfo) (LogicalPlan, error) {
charset, collation := b.ctx.GetSessionVars().GetCharsetInfo()
selectNode, err := parser.New().ParseOneStmt(tableInfo.View.SelectStmt, charset, collation)
if err != nil {
return nil, err
}
selectLogicalPlan, err := b.Build(selectNode)
if err != nil {
return nil, err
}

projSchema := expression.NewSchema(make([]*expression.Column, 0, len(tableInfo.View.Cols))...)
projExprs := make([]expression.Expression, 0, len(tableInfo.View.Cols))
for i := range tableInfo.View.Cols {
col := selectLogicalPlan.Schema().FindColumnByName(tableInfo.View.Cols[i].L)
if col == nil {
return nil, ErrViewInvalid.GenWithStackByArgs(dbName.O, tableInfo.Name.O)
}
projSchema.Append(&expression.Column{
UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(),
TblName: col.TblName,
OrigTblName: col.OrigTblName,
ColName: tableInfo.Cols()[i].Name,
OrigColName: tableInfo.View.Cols[i],
DBName: col.DBName,
RetType: col.GetType(),
})
projExprs = append(projExprs, col)
}

projUponView := LogicalProjection{Exprs: projExprs}.Init(b.ctx)
projUponView.SetChildren(selectLogicalPlan.(LogicalPlan))
projUponView.SetSchema(projSchema)
return projUponView, nil
}

// projectVirtualColumns is only for DataSource. If some table has virtual generated columns,
// we add a projection on the original DataSource, and calculate those columns in the projection
// so that plans above it can reference generated columns by their name.
Expand Down
33 changes: 32 additions & 1 deletion planner/core/logical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type testPlanSuite struct {
}

func (s *testPlanSuite) SetUpSuite(c *C) {
s.is = infoschema.MockInfoSchema([]*model.TableInfo{MockTable()})
s.is = infoschema.MockInfoSchema([]*model.TableInfo{MockTable(), MockView()})
s.ctx = MockContext()
s.Parser = parser.New()
}
Expand Down Expand Up @@ -1873,3 +1873,34 @@ func (s *testPlanSuite) TestOuterJoinEliminator(c *C) {
c.Assert(ToString(p), Equals, tt.best, comment)
}
}

func (s *testPlanSuite) TestSelectView(c *C) {
defer func() {
testleak.AfterTest(c)()
}()
tests := []struct {
sql string
best string
}{
{
sql: "select * from v",
best: "DataScan(t)->Projection",
},
}
for i, tt := range tests {
comment := Commentf("case:%v sql:%s", i, tt.sql)
stmt, err := s.ParseOneStmt(tt.sql, "", "")
c.Assert(err, IsNil, comment)
Preprocess(s.ctx, stmt, s.is, false)
builder := &PlanBuilder{
ctx: MockContext(),
is: s.is,
colMapper: make(map[*ast.ColumnNameExpr]int),
}
p, err := builder.Build(stmt)
c.Assert(err, IsNil)
p, err = logicalOptimize(builder.optFlag, p.(LogicalPlan))
c.Assert(err, IsNil)
c.Assert(ToString(p), Equals, tt.best, comment)
}
}
31 changes: 31 additions & 0 deletions planner/core/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package core

import (
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/tidb/domain"
Expand Down Expand Up @@ -249,6 +250,36 @@ func MockTable() *model.TableInfo {
return table
}

// MockView is only used for plan related tests.
func MockView() *model.TableInfo {
selectStmt := "select b,c,d from t"
col0 := &model.ColumnInfo{
State: model.StatePublic,
Offset: 0,
Name: model.NewCIStr("b"),
ID: 1,
}
col1 := &model.ColumnInfo{
State: model.StatePublic,
Offset: 1,
Name: model.NewCIStr("c"),
ID: 2,
}
col2 := &model.ColumnInfo{
State: model.StatePublic,
Offset: 2,
Name: model.NewCIStr("d"),
ID: 3,
}
view := &model.ViewInfo{SelectStmt: selectStmt, Security: model.SecurityDefiner, Definer: &auth.UserIdentity{Username: "root", Hostname: ""}, Cols: []model.CIStr{col0.Name, col1.Name, col2.Name}}
table := &model.TableInfo{
Name: model.NewCIStr("v"),
Columns: []*model.ColumnInfo{col0, col1, col2},
View: view,
}
return table
}

// MockContext is only used for plan related tests.
func MockContext() sessionctx.Context {
ctx := mock.NewContext()
Expand Down

0 comments on commit 123aba2

Please sign in to comment.