Skip to content

Commit b60541c

Browse files
committed
implement select from view
1 parent 20a468d commit b60541c

File tree

5 files changed

+141
-1
lines changed

5 files changed

+141
-1
lines changed

executor/executor_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -3428,3 +3428,38 @@ func (s *testSuite) TestSelectHashPartitionTable(c *C) {
34283428
" └─TableScan_13 10000.00 cop table:th, partition:, range:[-inf,+inf], keep order:false, stats:pseudo",
34293429
))
34303430
}
3431+
3432+
func (s *testSuite) TestSelectView(c *C) {
3433+
tk := testkit.NewTestKit(c, s.store)
3434+
tk.MustExec("use test")
3435+
tk.MustExec("create table view_t (a int,b int)")
3436+
tk.MustExec("insert into view_t values(1,2)")
3437+
tk.MustExec("create view view1 as select * from view_t")
3438+
tk.MustExec("create view view2(c,d) as select * from view_t")
3439+
tk.MustExec("create view view3(c,d) as select a,b from view_t")
3440+
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
3441+
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
3442+
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
3443+
tk.MustExec("drop table view_t;")
3444+
tk.MustExec("create table view_t(c int,d int)")
3445+
_, err := tk.Exec("select * from view1")
3446+
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view1").Error())
3447+
_, err = tk.Exec("select * from view2")
3448+
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view2").Error())
3449+
_, err = tk.Exec("select * from view3")
3450+
c.Assert(err.Error(), Equals, "[planner:1054]Unknown column 'a' in 'field list'")
3451+
tk.MustExec("drop table view_t;")
3452+
tk.MustExec("create table view_t(a int,b int,c int)")
3453+
tk.MustExec("insert into view_t values(1,2,3)")
3454+
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
3455+
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
3456+
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
3457+
tk.MustExec("alter table view_t drop column a")
3458+
tk.MustExec("alter table view_t add column a int after b")
3459+
tk.MustExec("update view_t set a=1;")
3460+
tk.MustQuery("select * from view1;").Check(testkit.Rows("1 2"))
3461+
tk.MustQuery("select * from view2;").Check(testkit.Rows("1 2"))
3462+
tk.MustQuery("select * from view3;").Check(testkit.Rows("1 2"))
3463+
tk.MustExec("drop table view_t;")
3464+
tk.MustExec("drop view view1,view2,view3;")
3465+
}

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

+42
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,44 @@ 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+
var (
2003+
selectNode ast.StmtNode
2004+
selectLogicalPlan Plan
2005+
err error
2006+
)
2007+
charset, collation := b.ctx.GetSessionVars().GetCharsetInfo()
2008+
selectNode, err = parser.New().ParseOneStmt(tableInfo.View.SelectStmt, charset, collation)
2009+
if err != nil {
2010+
return nil, err
2011+
}
2012+
selectLogicalPlan, err = b.Build(selectNode)
2013+
if err != nil {
2014+
return nil, err
2015+
}
2016+
projSchema := expression.NewSchema(make([]*expression.Column, 0, len(tableInfo.View.Cols))...)
2017+
for i := range tableInfo.View.Cols {
2018+
col := selectLogicalPlan.Schema().FindColumnByName(tableInfo.View.Cols[i].L)
2019+
if col == nil {
2020+
return nil, ErrViewInvalid.GenWithStackByArgs(dbName.O, tableInfo.Name.O)
2021+
}
2022+
projSchema.Append(&expression.Column{
2023+
UniqueID: col.UniqueID,
2024+
TblName: col.TblName,
2025+
OrigTblName: col.OrigTblName,
2026+
ColName: tableInfo.Cols()[i].Name,
2027+
OrigColName: tableInfo.View.Cols[i],
2028+
DBName: col.DBName,
2029+
RetType: col.GetType(),
2030+
})
2031+
}
2032+
2033+
projUponView := LogicalProjection{Exprs: expression.Column2Exprs(projSchema.Columns)}.Init(b.ctx)
2034+
projUponView.SetChildren(selectLogicalPlan.(LogicalPlan))
2035+
projUponView.SetSchema(projSchema)
2036+
return projUponView, nil
2037+
}
2038+
19972039
// projectVirtualColumns is only for DataSource. If some table has virtual generated columns,
19982040
// we add a projection on the original DataSource, and calculate those columns in the projection
19992041
// 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)