Skip to content

Commit

Permalink
planner: unfold the wildcard when creating view (#11818) (#12936)
Browse files Browse the repository at this point in the history
  • Loading branch information
XuHuaiyu authored and ngaut committed Oct 28, 2019
1 parent c4befe6 commit d5e52b4
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 49 deletions.
52 changes: 23 additions & 29 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1487,11 +1487,22 @@ func (d *ddl) CreateView(ctx sessionctx.Context, s *ast.CreateViewStmt) (err err
if err = checkTooLongTable(ident.Name); err != nil {
return err
}
viewInfo, cols := buildViewInfoWithTableColumns(ctx, s)
viewInfo, err := buildViewInfo(ctx, s)
if err != nil {
return err
}

colObjects := make([]interface{}, 0, len(viewInfo.Cols))
for _, col := range viewInfo.Cols {
colObjects = append(colObjects, col)
cols := make([]*table.Column, len(s.Cols))
colObjects := make([]interface{}, 0, len(s.Cols))

for i, v := range s.Cols {
cols[i] = table.ToColumn(&model.ColumnInfo{
Name: v,
ID: int64(i),
Offset: i,
State: model.StatePublic,
})
colObjects = append(colObjects, v)
}

if err = checkTooLongColumn(colObjects); err != nil {
Expand Down Expand Up @@ -1529,33 +1540,16 @@ func (d *ddl) CreateView(ctx sessionctx.Context, s *ast.CreateViewStmt) (err err
return d.callHookOnChanged(err)
}

func buildViewInfoWithTableColumns(ctx sessionctx.Context, s *ast.CreateViewStmt) (*model.ViewInfo, []*table.Column) {
viewInfo := &model.ViewInfo{Definer: s.Definer, Algorithm: s.Algorithm,
Security: s.Security, SelectStmt: s.Select.Text(), CheckOption: s.CheckOption, Cols: s.SchemaCols}
var tableColumns = make([]*table.Column, len(s.SchemaCols))
if s.Cols == nil {
for i, v := range s.SchemaCols {
tableColumns[i] = table.ToColumn(&model.ColumnInfo{
Name: v,
ID: int64(i),
Offset: i,
State: model.StatePublic,
Version: model.CurrLatestColumnInfoVersion,
})
}
} else {
for i, v := range s.Cols {
tableColumns[i] = table.ToColumn(&model.ColumnInfo{
Name: v,
ID: int64(i),
Offset: i,
State: model.StatePublic,
Version: model.CurrLatestColumnInfoVersion,
})
}
func buildViewInfo(ctx sessionctx.Context, s *ast.CreateViewStmt) (*model.ViewInfo, error) {
// Always Use `format.RestoreNameBackQuotes` to restore `SELECT` statement despite the `ANSI_QUOTES` SQL Mode is enabled or not.
restoreFlag := format.RestoreStringSingleQuotes | format.RestoreKeyWordUppercase | format.RestoreNameBackQuotes
var sb strings.Builder
if err := s.Select.Restore(format.NewRestoreCtx(restoreFlag, &sb)); err != nil {
return nil, err
}

return viewInfo, tableColumns
return &model.ViewInfo{Definer: s.Definer, Algorithm: s.Algorithm,
Security: s.Security, SelectStmt: sb.String(), CheckOption: s.CheckOption, Cols: nil}, nil
}

func checkPartitionByHash(ctx sessionctx.Context, pi *model.PartitionInfo, s *ast.CreateTableStmt, cols []*table.Column, tbInfo *model.TableInfo) error {
Expand Down
4 changes: 2 additions & 2 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3604,9 +3604,9 @@ func (s *testSuite) TestSelectView(c *C) {
tk.MustExec("drop table view_t;")
tk.MustExec("create table view_t(c int,d int)")
err := tk.ExecToErr("select * from view1")
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view1").Error())
c.Assert(err.Error(), Equals, "[planner:1054]Unknown column 'test.view_t.a' in 'field list'")
err = tk.ExecToErr("select * from view2")
c.Assert(err.Error(), Equals, plannercore.ErrViewInvalid.GenWithStackByArgs("test", "view2").Error())
c.Assert(err.Error(), Equals, "[planner:1054]Unknown column 'test.view_t.a' in 'field list'")
err = tk.ExecToErr("select * from view3")
c.Assert(err.Error(), Equals, "[planner:1054]Unknown column 'a' in 'field list'")
tk.MustExec("drop table view_t;")
Expand Down
16 changes: 13 additions & 3 deletions executor/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,16 +446,26 @@ func (s *testSuite2) TestShowCreateTable(c *C) {
tk.MustExec("create table t1(a int,b int)")
tk.MustExec("drop view if exists v1")
tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select * from t1")
tk.MustQuery("show create table v1").Check(testutil.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS select * from t1 "))
tk.MustQuery("show create view v1").Check(testutil.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS select * from t1 "))
tk.MustQuery("show create table v1").Check(testutil.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS SELECT `test`.`t1`.`a`,`test`.`t1`.`b` FROM `test`.`t1` "))
tk.MustQuery("show create view v1").Check(testutil.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS SELECT `test`.`t1`.`a`,`test`.`t1`.`b` FROM `test`.`t1` "))
tk.MustExec("drop view v1")
tk.MustExec("drop table t1")

tk.MustExec("drop view if exists v")
tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v as select JSON_MERGE('{}', '{}') as col;")
tk.MustQuery("show create view v").Check(testutil.RowsWithSep("|", "v|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v` (`col`) AS select JSON_MERGE('{}', '{}') as col; "))
tk.MustQuery("show create view v").Check(testutil.RowsWithSep("|", "v|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v` (`col`) AS SELECT JSON_MERGE('{}', '{}') AS `col` "))
tk.MustExec("drop view if exists v")

tk.MustExec("drop table if exists t1")
tk.MustExec("create table t1(a int,b int)")
tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select avg(a),t1.* from t1 group by a")
tk.MustQuery("show create view v1").Check(testutil.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`avg(a)`, `a`, `b`) AS SELECT AVG(`a`),`test`.`t1`.`a`,`test`.`t1`.`b` FROM `test`.`t1` GROUP BY `a` "))
tk.MustExec("drop view v1")
tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select a+b, t1.* , a as c from t1")
tk.MustQuery("show create view v1").Check(testutil.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a+b`, `a`, `b`, `c`) AS SELECT `a`+`b`,`test`.`t1`.`a`,`test`.`t1`.`b`,`a` AS `c` FROM `test`.`t1` "))
tk.MustExec("drop table t1")
tk.MustExec("drop view v1")

// For issue #9211
tk.MustExec("create table t(c int, b int as (c + 1))ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustQuery("show create table `t`").Check(testutil.RowsWithSep("|",
Expand Down
44 changes: 35 additions & 9 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2076,6 +2076,9 @@ func (b *PlanBuilder) buildSelect(ctx context.Context, sel *ast.SelectStmt) (p L
if err != nil {
return nil, err
}
if b.capFlag&canExpandAST != 0 {
originalFields = sel.Fields.Fields
}

if sel.GroupBy != nil {
p, gbyCols, err = b.resolveGbyExprs(ctx, p, sel.GroupBy, sel.Fields.Fields)
Expand Down Expand Up @@ -2429,25 +2432,48 @@ func (b *PlanBuilder) BuildDataSourceFromView(ctx context.Context, dbName model.
b.visitInfo = appendVisitInfo(b.visitInfo, mysql.ShowViewPriv, dbName.L, tableInfo.Name.L, "", ErrViewNoExplain)
}

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)
if len(tableInfo.Columns) != selectLogicalPlan.Schema().Len() {
return nil, ErrViewInvalid.GenWithStackByArgs(dbName.O, tableInfo.Name.O)
}

return b.buildProjUponView(ctx, dbName, tableInfo, selectLogicalPlan)
}

func (b *PlanBuilder) buildProjUponView(ctx context.Context, dbName model.CIStr, tableInfo *model.TableInfo, selectLogicalPlan Plan) (LogicalPlan, error) {
columnInfo := tableInfo.Cols()
cols := selectLogicalPlan.Schema().Columns
// In the old version of VIEW implementation, tableInfo.View.Cols is used to
// store the origin columns' names of the underlying SelectStmt used when
// creating the view.
if tableInfo.View.Cols != nil {
cols = cols[:0]
for _, info := range columnInfo {
col := selectLogicalPlan.Schema().FindColumnByName(info.Name.L)
if col == nil {
return nil, ErrViewInvalid.GenWithStackByArgs(dbName.O, tableInfo.Name.O)
}
cols = append(cols, col)
}
}

projSchema := expression.NewSchema(make([]*expression.Column, 0, len(tableInfo.Columns))...)
projExprs := make([]expression.Expression, 0, len(tableInfo.Columns))
for i, col := range cols {
origColName := col.ColName
if tableInfo.View.Cols != nil {
origColName = tableInfo.View.Cols[i]
}
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],
ColName: columnInfo[i].Name,
OrigColName: origColName,
DBName: col.DBName,
RetType: col.GetType(),
})
projExprs = append(projExprs, col)
}

projUponView := LogicalProjection{Exprs: projExprs}.Init(b.ctx)
projUponView.SetChildren(selectLogicalPlan.(LogicalPlan))
projUponView.SetSchema(projSchema)
Expand Down
30 changes: 24 additions & 6 deletions planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ var clauseMsg = map[clauseCode]string{
globalOrderByClause: "global ORDER clause",
}

type capFlagType = uint64

const (
_ capFlagType = iota
// canExpandAST indicates whether the origin AST can be expanded during plan
// building. ONLY used for `CreateViewStmt` now.
canExpandAST
)

// PlanBuilder builds Plan from an ast.Node.
// It just builds the ast node straightforwardly.
type PlanBuilder struct {
Expand All @@ -186,7 +195,10 @@ type PlanBuilder struct {
// visitInfo is used for privilege check.
visitInfo []visitInfo
tableHintInfo []tableHintInfo
optFlag uint64
// optFlag indicates the flags of the optimizer rules.
optFlag uint64
// capFlag indicates the capability flags.
capFlag capFlagType

curClause clauseCode

Expand Down Expand Up @@ -2080,17 +2092,23 @@ func (b *PlanBuilder) buildDDL(ctx context.Context, node ast.DDLNode) (Plan, err
v.ReferTable.Name.L, "", authErr)
}
case *ast.CreateViewStmt:
b.capFlag |= canExpandAST
defer func() {
b.capFlag &= ^canExpandAST
}()
plan, err := b.Build(ctx, v.Select)
if err != nil {
return nil, err
}
schema := plan.Schema()
if v.Cols != nil && len(v.Cols) != schema.Len() {
return nil, ddl.ErrViewWrongList
if v.Cols == nil {
v.Cols = make([]model.CIStr, len(schema.Columns))
for i, col := range schema.Columns {
v.Cols[i] = col.ColName
}
}
v.SchemaCols = make([]model.CIStr, schema.Len())
for i, col := range schema.Columns {
v.SchemaCols[i] = col.ColName
if len(v.Cols) != schema.Len() {
return nil, ddl.ErrViewWrongList
}
if _, ok := plan.(LogicalPlan); ok {
if b.ctx.GetSessionVars().User != nil {
Expand Down

0 comments on commit d5e52b4

Please sign in to comment.