diff --git a/ddl/serial_test.go b/ddl/serial_test.go index 994f52c443a7e..b06139c8dd312 100644 --- a/ddl/serial_test.go +++ b/ddl/serial_test.go @@ -525,6 +525,10 @@ func (s *testSerialSuite) TestCreateTableWithLike(c *C) { tk.MustExec("drop database ctwl_db") tk.MustExec("drop database ctwl_db1") +} + +func (s *testSerialSuite) TestCreateTableWithLikeAtTemporaryMode(c *C) { + tk := testkit.NewTestKit(c, s.store) // Test create table like at temporary mode. tk.MustExec("set tidb_enable_global_temporary_table=true") @@ -532,9 +536,83 @@ func (s *testSerialSuite) TestCreateTableWithLike(c *C) { tk.MustExec("drop table if exists temporary_table;") tk.MustExec("create global temporary table temporary_table (a int, b int,index(a)) on commit delete rows") tk.MustExec("drop table if exists temporary_table_t1;") - _, err = tk.Exec("create table temporary_table_t1 like temporary_table") + _, err := tk.Exec("create table temporary_table_t1 like temporary_table") c.Assert(err.Error(), Equals, core.ErrOptOnTemporaryTable.GenWithStackByArgs("create table like").Error()) tk.MustExec("drop table if exists temporary_table;") + + // Test create temporary table like. + // Test auto_random. + tk.MustExec("drop table if exists auto_random_table") + _, err = tk.Exec("create table auto_random_table (a bigint primary key auto_random(3), b varchar(255));") + defer tk.MustExec("drop table if exists auto_random_table") + tk.MustExec("drop table if exists auto_random_temporary_global") + _, err = tk.Exec("create global temporary table auto_random_temporary_global like auto_random_table on commit delete rows;") + c.Assert(err.Error(), Equals, core.ErrOptOnTemporaryTable.GenWithStackByArgs("auto_random").Error()) + + // Test pre split regions. + tk.MustExec("drop table if exists table_pre_split") + _, err = tk.Exec("create table table_pre_split(id int) shard_row_id_bits = 2 pre_split_regions=2;") + defer tk.MustExec("drop table if exists table_pre_split") + tk.MustExec("drop table if exists temporary_table_pre_split") + _, err = tk.Exec("create global temporary table temporary_table_pre_split like table_pre_split ON COMMIT DELETE ROWS;") + c.Assert(err.Error(), Equals, core.ErrOptOnTemporaryTable.GenWithStackByArgs("pre split regions").Error()) + + // Test shard_row_id_bits. + tk.MustExec("drop table if exists shard_row_id_table, shard_row_id_temporary_table, shard_row_id_table_plus, shard_row_id_temporary_table_plus") + _, err = tk.Exec("create table shard_row_id_table (a int) shard_row_id_bits = 5;") + _, err = tk.Exec("create global temporary table shard_row_id_temporary_table like shard_row_id_table on commit delete rows;") + c.Assert(err.Error(), Equals, core.ErrOptOnTemporaryTable.GenWithStackByArgs("shard_row_id_bits").Error()) + tk.MustExec("create table shard_row_id_table_plus (a int);") + tk.MustExec("create global temporary table shard_row_id_temporary_table_plus (a int) on commit delete rows;") + defer tk.MustExec("drop table if exists shard_row_id_table, shard_row_id_temporary_table, shard_row_id_table_plus, shard_row_id_temporary_table_plus") + _, err = tk.Exec("alter table shard_row_id_temporary_table_plus shard_row_id_bits = 4;") + c.Assert(err.Error(), Equals, ddl.ErrOptOnTemporaryTable.GenWithStackByArgs("shard_row_id_bits").Error()) + + // Test partition. + tk.MustExec("drop table if exists global_partition_table;") + tk.MustExec("create table global_partition_table (a int, b int) partition by hash(a) partitions 3;") + defer tk.MustExec("drop table if exists global_partition_table;") + tk.MustGetErrCode("create global temporary table global_partition_temp_table like global_partition_table ON COMMIT DELETE ROWS;", + errno.ErrPartitionNoTemporary) + // Test virtual columns. + tk.MustExec("drop table if exists test_gv_ddl, test_gv_ddl_temp") + tk.MustExec(`create table test_gv_ddl(a int, b int as (a+8) virtual, c int as (b + 2) stored)`) + tk.MustExec(`create global temporary table test_gv_ddl_temp like test_gv_ddl on commit delete rows;`) + defer tk.MustExec("drop table if exists test_gv_ddl_temp, test_gv_ddl") + is := tk.Se.(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("test_gv_ddl")) + c.Assert(err, IsNil) + testCases := []struct { + generatedExprString string + generatedStored bool + }{ + {"", false}, + {"`a` + 8", false}, + {"`b` + 2", true}, + } + for i, column := range table.Meta().Columns { + c.Assert(column.GeneratedExprString, Equals, testCases[i].generatedExprString) + c.Assert(column.GeneratedStored, Equals, testCases[i].generatedStored) + } + result := tk.MustQuery(`DESC test_gv_ddl_temp`) + result.Check(testkit.Rows(`a int(11) YES `, `b int(11) YES VIRTUAL GENERATED`, `c int(11) YES STORED GENERATED`)) + tk.MustExec("begin;") + tk.MustExec("insert into test_gv_ddl_temp values (1, default, default)") + tk.MustQuery("select * from test_gv_ddl_temp").Check(testkit.Rows("1 9 11")) + _, err = tk.Exec("commit") + c.Assert(err, IsNil) + + // Test foreign key. + tk.MustExec("drop table if exists test_foreign_key, t1") + tk.MustExec("create table t1 (a int, b int);") + tk.MustExec("create table test_foreign_key (c int,d int,foreign key (d) references t1 (b));") + defer tk.MustExec("drop table if exists test_foreign_key, t1;") + tk.MustExec("create global temporary table test_foreign_key_temp like test_foreign_key on commit delete rows;") + is = tk.Se.(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + table, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("test_foreign_key_temp")) + c.Assert(err, IsNil) + tableInfo := table.Meta() + c.Assert(len(tableInfo.ForeignKeys), Equals, 0) } // TestCancelAddIndex1 tests canceling ddl job when the add index worker is not started. diff --git a/errors.toml b/errors.toml index 1c6903d1905e5..30bf5e5b62477 100644 --- a/errors.toml +++ b/errors.toml @@ -981,6 +981,11 @@ error = ''' `%-.192s`.`%-.192s` contains view recursion ''' +["planner:1562"] +error = ''' +Cannot create temporary table with partitions +''' + ["planner:1706"] error = ''' Primary key/partition key update is not allowed since the table is updated both as '%-.192s' and '%-.192s'. diff --git a/planner/core/errors.go b/planner/core/errors.go index dd457e0ca4efe..9b7aa27858607 100644 --- a/planner/core/errors.go +++ b/planner/core/errors.go @@ -100,4 +100,6 @@ var ( ErrDifferentAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknown) ErrAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknown) ErrOptOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnTemporaryTable) + // ErrPartitionNoTemporary returns when partition at temporary mode + ErrPartitionNoTemporary = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionNoTemporary) ) diff --git a/planner/core/preprocess.go b/planner/core/preprocess.go index 70e1391d3361c..8366545b1fd45 100644 --- a/planner/core/preprocess.go +++ b/planner/core/preprocess.go @@ -639,10 +639,19 @@ func (p *preprocessor) checkCreateTableGrammar(stmt *ast.CreateTableStmt) { p.err = err return } - if tableInfo.Meta().TempTableType != model.TempTableNone { + tableMetaInfo := tableInfo.Meta() + if tableMetaInfo.TempTableType != model.TempTableNone { p.err = ErrOptOnTemporaryTable.GenWithStackByArgs("create table like") return } + if stmt.TemporaryKeyword != ast.TemporaryNone { + err := checkReferInfoForTemporaryTable(tableMetaInfo) + if err != nil { + p.err = err + return + } + + } } if stmt.TemporaryKeyword != ast.TemporaryNone { for _, opt := range stmt.Options { @@ -1023,6 +1032,23 @@ func checkTableEngine(engineName string) error { return nil } +func checkReferInfoForTemporaryTable(tableMetaInfo *model.TableInfo) error { + if tableMetaInfo.AutoRandomBits != 0 { + return ErrOptOnTemporaryTable.GenWithStackByArgs("auto_random") + } + if tableMetaInfo.PreSplitRegions != 0 { + return ErrOptOnTemporaryTable.GenWithStackByArgs("pre split regions") + } + if tableMetaInfo.Partition != nil { + return ErrPartitionNoTemporary + } + if tableMetaInfo.ShardRowIDBits != 0 { + return ErrOptOnTemporaryTable.GenWithStackByArgs("shard_row_id_bits") + } + + return nil +} + // checkColumn checks if the column definition is valid. // See https://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html func checkColumn(colDef *ast.ColumnDef) error {