Skip to content

Commit 123aba2

Browse files
AndrewDiXuHuaiyu
authored andcommitted
planner: support building data source from View (#8757)
1 parent 7ffdbca commit 123aba2

File tree

5 files changed

+139
-1
lines changed

5 files changed

+139
-1
lines changed

executor/executor_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -3427,6 +3427,41 @@ func (s *testSuite3) TestSelectHashPartitionTable(c *C) {
34273427
))
34283428
}
34293429

3430+
func (s *testSuite) TestSelectView(c *C) {
3431+
tk := testkit.NewTestKit(c, s.store)
3432+
tk.MustExec("use test")
3433+
tk.MustExec("create table view_t (a int,b int)")
3434+
tk.MustExec("insert into view_t values(1,2)")
3435+
tk.MustExec("create view view1 as select * from view_t")
3436+
tk.MustExec("create view view2(c,d) as select * from view_t")
3437+
tk.MustExec("create view view3(c,d) as select a,b from view_t")
3438+
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
3439+
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
3440+
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
3441+
tk.MustExec("drop table view_t;")
3442+
tk.MustExec("create table view_t(c int,d int)")
3443+
_, err := tk.Exec("select * from view1")
3444+
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view1").Error())
3445+
_, err = tk.Exec("select * from view2")
3446+
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view2").Error())
3447+
_, err = tk.Exec("select * from view3")
3448+
c.Assert(err.Error(), Equals, "[planner:1054]Unknown column 'a' in 'field list'")
3449+
tk.MustExec("drop table view_t;")
3450+
tk.MustExec("create table view_t(a int,b int,c int)")
3451+
tk.MustExec("insert into view_t values(1,2,3)")
3452+
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
3453+
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
3454+
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
3455+
tk.MustExec("alter table view_t drop column a")
3456+
tk.MustExec("alter table view_t add column a int after b")
3457+
tk.MustExec("update view_t set a=1;")
3458+
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
3459+
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
3460+
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
3461+
tk.MustExec("drop table view_t;")
3462+
tk.MustExec("drop view view1,view2,view3;")
3463+
}
3464+
34303465
type testSuite2 struct {
34313466
cluster *mocktikv.Cluster
34323467
mvccStore mocktikv.MVCCStore

planner/core/errors.go

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ var (
8787
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")
8888
ErrNonUniqTable = terror.ClassOptimizer.New(codeNonUniqTable, mysql.MySQLErrName[mysql.ErrNonuniqTable])
8989
ErrWrongValueCountOnRow = terror.ClassOptimizer.New(mysql.ErrWrongValueCountOnRow, mysql.MySQLErrName[mysql.ErrWrongValueCountOnRow])
90+
ErrViewInvalid = terror.ClassOptimizer.New(mysql.ErrViewInvalid, mysql.MySQLErrName[mysql.ErrViewInvalid])
9091
)
9192

9293
func init() {

planner/core/logical_plan_builder.go

+40
Original file line numberDiff line numberDiff line change
@@ -1902,6 +1902,10 @@ func (b *PlanBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error) {
19021902
tableInfo := tbl.Meta()
19031903
b.visitInfo = appendVisitInfo(b.visitInfo, mysql.SelectPriv, dbName.L, tableInfo.Name.L, "")
19041904

1905+
if tableInfo.IsView() {
1906+
return b.buildDataSourceFromView(dbName, tableInfo)
1907+
}
1908+
19051909
if tableInfo.GetPartitionInfo() != nil {
19061910
b.optFlag = b.optFlag | flagPartitionProcessor
19071911
}
@@ -1994,6 +1998,42 @@ func (b *PlanBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error) {
19941998
return result, nil
19951999
}
19962000

2001+
func (b *PlanBuilder) buildDataSourceFromView(dbName model.CIStr, tableInfo *model.TableInfo) (LogicalPlan, error) {
2002+
charset, collation := b.ctx.GetSessionVars().GetCharsetInfo()
2003+
selectNode, err := parser.New().ParseOneStmt(tableInfo.View.SelectStmt, charset, collation)
2004+
if err != nil {
2005+
return nil, err
2006+
}
2007+
selectLogicalPlan, err := b.Build(selectNode)
2008+
if err != nil {
2009+
return nil, err
2010+
}
2011+
2012+
projSchema := expression.NewSchema(make([]*expression.Column, 0, len(tableInfo.View.Cols))...)
2013+
projExprs := make([]expression.Expression, 0, len(tableInfo.View.Cols))
2014+
for i := range tableInfo.View.Cols {
2015+
col := selectLogicalPlan.Schema().FindColumnByName(tableInfo.View.Cols[i].L)
2016+
if col == nil {
2017+
return nil, ErrViewInvalid.GenWithStackByArgs(dbName.O, tableInfo.Name.O)
2018+
}
2019+
projSchema.Append(&expression.Column{
2020+
UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(),
2021+
TblName: col.TblName,
2022+
OrigTblName: col.OrigTblName,
2023+
ColName: tableInfo.Cols()[i].Name,
2024+
OrigColName: tableInfo.View.Cols[i],
2025+
DBName: col.DBName,
2026+
RetType: col.GetType(),
2027+
})
2028+
projExprs = append(projExprs, col)
2029+
}
2030+
2031+
projUponView := LogicalProjection{Exprs: projExprs}.Init(b.ctx)
2032+
projUponView.SetChildren(selectLogicalPlan.(LogicalPlan))
2033+
projUponView.SetSchema(projSchema)
2034+
return projUponView, nil
2035+
}
2036+
19972037
// projectVirtualColumns is only for DataSource. If some table has virtual generated columns,
19982038
// we add a projection on the original DataSource, and calculate those columns in the projection
19992039
// so that plans above it can reference generated columns by their name.

planner/core/logical_plan_test.go

+32-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type testPlanSuite struct {
4545
}
4646

4747
func (s *testPlanSuite) SetUpSuite(c *C) {
48-
s.is = infoschema.MockInfoSchema([]*model.TableInfo{MockTable()})
48+
s.is = infoschema.MockInfoSchema([]*model.TableInfo{MockTable(), MockView()})
4949
s.ctx = MockContext()
5050
s.Parser = parser.New()
5151
}
@@ -1873,3 +1873,34 @@ func (s *testPlanSuite) TestOuterJoinEliminator(c *C) {
18731873
c.Assert(ToString(p), Equals, tt.best, comment)
18741874
}
18751875
}
1876+
1877+
func (s *testPlanSuite) TestSelectView(c *C) {
1878+
defer func() {
1879+
testleak.AfterTest(c)()
1880+
}()
1881+
tests := []struct {
1882+
sql string
1883+
best string
1884+
}{
1885+
{
1886+
sql: "select * from v",
1887+
best: "DataScan(t)->Projection",
1888+
},
1889+
}
1890+
for i, tt := range tests {
1891+
comment := Commentf("case:%v sql:%s", i, tt.sql)
1892+
stmt, err := s.ParseOneStmt(tt.sql, "", "")
1893+
c.Assert(err, IsNil, comment)
1894+
Preprocess(s.ctx, stmt, s.is, false)
1895+
builder := &PlanBuilder{
1896+
ctx: MockContext(),
1897+
is: s.is,
1898+
colMapper: make(map[*ast.ColumnNameExpr]int),
1899+
}
1900+
p, err := builder.Build(stmt)
1901+
c.Assert(err, IsNil)
1902+
p, err = logicalOptimize(builder.optFlag, p.(LogicalPlan))
1903+
c.Assert(err, IsNil)
1904+
c.Assert(ToString(p), Equals, tt.best, comment)
1905+
}
1906+
}

planner/core/mock.go

+31
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package core
1515

1616
import (
17+
"github.com/pingcap/parser/auth"
1718
"github.com/pingcap/parser/model"
1819
"github.com/pingcap/parser/mysql"
1920
"github.com/pingcap/tidb/domain"
@@ -249,6 +250,36 @@ func MockTable() *model.TableInfo {
249250
return table
250251
}
251252

253+
// MockView is only used for plan related tests.
254+
func MockView() *model.TableInfo {
255+
selectStmt := "select b,c,d from t"
256+
col0 := &model.ColumnInfo{
257+
State: model.StatePublic,
258+
Offset: 0,
259+
Name: model.NewCIStr("b"),
260+
ID: 1,
261+
}
262+
col1 := &model.ColumnInfo{
263+
State: model.StatePublic,
264+
Offset: 1,
265+
Name: model.NewCIStr("c"),
266+
ID: 2,
267+
}
268+
col2 := &model.ColumnInfo{
269+
State: model.StatePublic,
270+
Offset: 2,
271+
Name: model.NewCIStr("d"),
272+
ID: 3,
273+
}
274+
view := &model.ViewInfo{SelectStmt: selectStmt, Security: model.SecurityDefiner, Definer: &auth.UserIdentity{Username: "root", Hostname: ""}, Cols: []model.CIStr{col0.Name, col1.Name, col2.Name}}
275+
table := &model.TableInfo{
276+
Name: model.NewCIStr("v"),
277+
Columns: []*model.ColumnInfo{col0, col1, col2},
278+
View: view,
279+
}
280+
return table
281+
}
282+
252283
// MockContext is only used for plan related tests.
253284
func MockContext() sessionctx.Context {
254285
ctx := mock.NewContext()

0 commit comments

Comments
 (0)