diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index 39fee9b9d7432..e4cfe86f4d89c 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -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" @@ -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" @@ -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() +} diff --git a/errors.toml b/errors.toml index db65bcbabce44..9bd98a0a76001 100644 --- a/errors.toml +++ b/errors.toml @@ -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 diff --git a/planner/core/errors.go b/planner/core/errors.go index e501af7676c5f..de8ff8cb09fcd 100644 --- a/planner/core/errors.go +++ b/planner/core/errors.go @@ -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) ) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 09703b286de85..09b34216c8cbc 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -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) diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index b1de3857541a0..2dd9f0df6e785 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -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. @@ -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 {