Skip to content

Commit

Permalink
planner: Forbide create view on tmp table (pingcap#26091)
Browse files Browse the repository at this point in the history
  • Loading branch information
lcwangchao authored Jul 13, 2021
1 parent e04181b commit 88cbdec
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 1 deletion.
78 changes: 78 additions & 0 deletions ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
. "github.com/pingcap/check"
"github.com/pingcap/errors"
"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/charset"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
Expand All @@ -35,6 +36,7 @@ import (
"github.com/pingcap/tidb/infoschema"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/meta"
"github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/stmtctx"
Expand Down Expand Up @@ -2849,3 +2851,79 @@ func (s *testIntegrationSuite3) TestCreateTemporaryTable(c *C) {
tk.MustExec("set @@tidb_snapshot = '2016-01-01 15:04:05.999999'")
tk.MustExec("select * from overlap")
}

func (s *testIntegrationSuite3) TestAvoidCreateViewOnLocalTemporaryTable(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")

tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"))
tk.MustExec("drop table if exists tt0")
tk.MustExec("drop table if exists tt1")
tk.MustExec("drop table if exists tt2")

tk.MustExec("set @@tidb_enable_noop_functions=1")
tk.MustExec("create table tt0 (a int, b int)")
tk.MustExec("create view v0 as select * from tt0")
tk.MustExec("create temporary table tt1 (a int, b int)")
tk.MustExec("create temporary table tt2 (c int, d int)")

checkCreateView := func() {
_, err := tk.Exec("create view v1 as select * from tt1")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v1")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v1' doesn't exist")

_, err = tk.Exec("create view v1 as select * from (select * from tt1) as tt")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v1")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v1' doesn't exist")

_, err = tk.Exec("create view v2 as select * from tt0 union select * from tt1")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v2")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v2' doesn't exist")

_, err = tk.Exec("create view v3 as select * from tt0, tt1 where tt0.a = tt1.a")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v3")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v3' doesn't exist")

_, err = tk.Exec("create view v4 as select a, (select count(1) from tt1 where tt1.a = tt0.a) as tt1a from tt0")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v4")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v4' doesn't exist")

_, err = tk.Exec("create view v5 as select a, (select count(1) from tt1 where tt1.a = 1) as tt1a from tt0")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v5")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v5' doesn't exist")

_, err = tk.Exec("create view v6 as select * from tt0 where tt0.a=(select max(tt1.b) from tt1)")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v6")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v6' doesn't exist")

_, err = tk.Exec("create view v7 as select * from tt0 where tt0.b=(select max(tt1.b) from tt1 where tt0.a=tt1.a)")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
_, err = tk.Exec("select * from v7")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[schema:1146]Table 'test.v7' doesn't exist")

_, err = tk.Exec("create or replace view v0 as select * from tt1")
c.Assert(core.ErrViewSelectTemporaryTable.Equal(err), IsTrue)
}

checkCreateView()
tk.MustExec("create temporary table tt0 (a int, b int)")
tk.MustExec("create table tt1 (a int, b int)")
tk.MustExec("create table tt2 (c int, d int)")
tk.MustExec("create view vv as select * from v0")
checkCreateView()
}
5 changes: 5 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,11 @@ error = '''
EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
'''

["planner:1352"]
error = '''
View's SELECT refers to a temporary table '%-.192s'
'''

["planner:1356"]
error = '''
View '%-.192s.%-.192s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
Expand Down
3 changes: 2 additions & 1 deletion planner/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,6 @@ var (
ErrOptOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnTemporaryTable)
ErrDropTableOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrDropTableOnTemporaryTable)
// ErrPartitionNoTemporary returns when partition at temporary mode
ErrPartitionNoTemporary = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionNoTemporary)
ErrPartitionNoTemporary = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionNoTemporary)
ErrViewSelectTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrViewSelectTmptable)
)
4 changes: 4 additions & 0 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3819,6 +3819,10 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as
}

tableInfo := tbl.Meta()
if b.isCreateView && tableInfo.TempTableType == model.TempTableLocal {
return nil, ErrViewSelectTemporaryTable.GenWithStackByArgs(tn.Name)
}

var authErr error
if sessionVars.User != nil {
authErr = ErrTableaccessDenied.FastGenByArgs("SELECT", sessionVars.User.AuthUsername, sessionVars.User.AuthHostname, tableInfo.Name.L)
Expand Down
4 changes: 4 additions & 0 deletions planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ type PlanBuilder struct {
buildingViewStack set.StringSet
// renamingViewName is the name of the view which is being renamed.
renamingViewName string
// isCreateView indicates whether the query is create view.
isCreateView bool

// evalDefaultExpr needs this information to find the corresponding column.
// It stores the OutputNames before buildProjection.
Expand Down Expand Up @@ -3608,11 +3610,13 @@ func (b *PlanBuilder) buildDDL(ctx context.Context, node ast.DDLNode) (Plan, err
v.ReferTable.Name.L, "", authErr)
}
case *ast.CreateViewStmt:
b.isCreateView = true
b.capFlag |= canExpandAST | renameView
b.renamingViewName = v.ViewName.Schema.L + "." + v.ViewName.Name.L
defer func() {
b.capFlag &= ^canExpandAST
b.capFlag &= ^renameView
b.isCreateView = false
}()
plan, err := b.Build(ctx, v.Select)
if err != nil {
Expand Down

0 comments on commit 88cbdec

Please sign in to comment.