From 682d8bedaa35eddca4ef4ae4c8ca4d4788c89277 Mon Sep 17 00:00:00 2001 From: tison Date: Sat, 19 Mar 2022 23:02:31 +0800 Subject: [PATCH] ddl: migrate test-infra to testify for part of ddl.testSerialDBSuite (#33258) ref pingcap/tidb#33004 --- ddl/column_modify_test.go | 243 ++++++++++++++++++++++++++++ ddl/db_legacy_test.go | 324 -------------------------------------- ddl/db_test.go | 86 ++++++++++ 3 files changed, 329 insertions(+), 324 deletions(-) diff --git a/ddl/column_modify_test.go b/ddl/column_modify_test.go index 3a86e6b905..f91316d977 100644 --- a/ddl/column_modify_test.go +++ b/ddl/column_modify_test.go @@ -30,6 +30,7 @@ import ( "github.com/pingcap/tidb/errno" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/parser/ast" "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/sessionctx" @@ -1059,3 +1060,245 @@ func TestAddMultiColumnsIndex(t *testing.T) { tk.MustExec("alter table tidb.test add index idx1 (a, b);") tk.MustExec("admin check table test") } + +// For issue #31735. +func TestAddGeneratedColumnAndInsert(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomainWithSchemaLease(t, columnModifyLease) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a int, unique kye(a))") + tk.MustExec("insert into t1 value (1), (10)") + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + + d := dom.DDL() + hook := &ddl.TestDDLCallback{Do: dom} + ctx := mock.NewContext() + ctx.Store = store + times := 0 + var checkErr error + hook.OnJobUpdatedExported = func(job *model.Job) { + if checkErr != nil { + return + } + switch job.SchemaState { + case model.StateDeleteOnly: + _, checkErr = tk1.Exec("insert into t1 values (1) on duplicate key update a=a+1") + if checkErr == nil { + _, checkErr = tk1.Exec("replace into t1 values (2)") + } + case model.StateWriteOnly: + _, checkErr = tk1.Exec("insert into t1 values (2) on duplicate key update a=a+1") + if checkErr == nil { + _, checkErr = tk1.Exec("replace into t1 values (3)") + } + case model.StateWriteReorganization: + if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 { + _, checkErr = tk1.Exec("insert into t1 values (3) on duplicate key update a=a+1") + if checkErr == nil { + _, checkErr = tk1.Exec("replace into t1 values (4)") + } + times++ + } + } + } + d.SetHook(hook) + + tk.MustExec("alter table t1 add column gc int as ((a+1))") + tk.MustQuery("select * from t1 order by a").Check(testkit.Rows("4 5", "10 11")) + require.NoError(t, checkErr) +} + +func TestColumnTypeChangeGenUniqueChangingName(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomainWithSchemaLease(t, columnModifyLease) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + hook := &ddl.TestDDLCallback{} + var checkErr error + assertChangingColName := "_col$_c2_0" + assertChangingIdxName := "_idx$_idx_0" + hook.OnJobUpdatedExported = func(job *model.Job) { + if job.SchemaState == model.StateDeleteOnly && job.Type == model.ActionModifyColumn { + var ( + newCol *model.ColumnInfo + oldColName *model.CIStr + modifyColumnTp byte + updatedAutoRandomBits uint64 + changingCol *model.ColumnInfo + changingIdxs []*model.IndexInfo + ) + pos := &ast.ColumnPosition{} + err := job.DecodeArgs(&newCol, &oldColName, pos, &modifyColumnTp, &updatedAutoRandomBits, &changingCol, &changingIdxs) + if err != nil { + checkErr = err + return + } + if changingCol.Name.L != assertChangingColName { + checkErr = errors.New("changing column name is incorrect") + } else if changingIdxs[0].Name.L != assertChangingIdxName { + checkErr = errors.New("changing index name is incorrect") + } + } + } + d := dom.DDL() + d.SetHook(hook) + + tk.MustExec("create table if not exists t(c1 varchar(256), c2 bigint, `_col$_c2` varchar(10), unique _idx$_idx(c1), unique idx(c2));") + tk.MustExec("alter table test.t change column c2 cC2 tinyint after `_col$_c2`") + require.NoError(t, checkErr) + + tbl := external.GetTableByName(t, tk, "test", "t") + require.Len(t, tbl.Meta().Columns, 3) + require.Equal(t, "c1", tbl.Meta().Columns[0].Name.O) + require.Equal(t, 0, tbl.Meta().Columns[0].Offset) + require.Equal(t, "_col$_c2", tbl.Meta().Columns[1].Name.O) + require.Equal(t, 1, tbl.Meta().Columns[1].Offset) + require.Equal(t, "cC2", tbl.Meta().Columns[2].Name.O) + require.Equal(t, 2, tbl.Meta().Columns[2].Offset) + + require.Len(t, tbl.Meta().Indices, 2) + require.Equal(t, "_idx$_idx", tbl.Meta().Indices[0].Name.O) + require.Equal(t, "idx", tbl.Meta().Indices[1].Name.O) + + require.Len(t, tbl.Meta().Indices[0].Columns, 1) + require.Equal(t, "c1", tbl.Meta().Indices[0].Columns[0].Name.O) + require.Equal(t, 0, tbl.Meta().Indices[0].Columns[0].Offset) + + require.Len(t, tbl.Meta().Indices[1].Columns, 1) + require.Equal(t, "cC2", tbl.Meta().Indices[1].Columns[0].Name.O) + require.Equal(t, 2, tbl.Meta().Indices[1].Columns[0].Offset) + + assertChangingColName1 := "_col$__col$_c1_1" + assertChangingColName2 := "_col$__col$__col$_c1_0_1" + query1 := "alter table t modify column _col$_c1 tinyint" + query2 := "alter table t modify column _col$__col$_c1_0 tinyint" + hook.OnJobUpdatedExported = func(job *model.Job) { + if (job.Query == query1 || job.Query == query2) && job.SchemaState == model.StateDeleteOnly && job.Type == model.ActionModifyColumn { + var ( + newCol *model.ColumnInfo + oldColName *model.CIStr + modifyColumnTp byte + updatedAutoRandomBits uint64 + changingCol *model.ColumnInfo + changingIdxs []*model.IndexInfo + ) + pos := &ast.ColumnPosition{} + err := job.DecodeArgs(&newCol, &oldColName, pos, &modifyColumnTp, &updatedAutoRandomBits, &changingCol, &changingIdxs) + if err != nil { + checkErr = err + return + } + if job.Query == query1 && changingCol.Name.L != assertChangingColName1 { + checkErr = errors.New("changing column name is incorrect") + } + if job.Query == query2 && changingCol.Name.L != assertChangingColName2 { + checkErr = errors.New("changing column name is incorrect") + } + } + } + d.SetHook(hook) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table if not exists t(c1 bigint, _col$_c1 bigint, _col$__col$_c1_0 bigint, _col$__col$__col$_c1_0_0 bigint)") + tk.MustExec("alter table t modify column c1 tinyint") + tk.MustExec("alter table t modify column _col$_c1 tinyint") + require.NoError(t, checkErr) + tk.MustExec("alter table t modify column _col$__col$_c1_0 tinyint") + require.NoError(t, checkErr) + tk.MustExec("alter table t change column _col$__col$__col$_c1_0_0 _col$__col$__col$_c1_0_0 tinyint") + + tbl = external.GetTableByName(t, tk, "test", "t") + require.Len(t, tbl.Meta().Columns, 4) + require.Equal(t, "c1", tbl.Meta().Columns[0].Name.O) + require.Equal(t, mysql.TypeTiny, tbl.Meta().Columns[0].Tp) + require.Equal(t, 0, tbl.Meta().Columns[0].Offset) + require.Equal(t, "_col$_c1", tbl.Meta().Columns[1].Name.O) + require.Equal(t, mysql.TypeTiny, tbl.Meta().Columns[1].Tp) + require.Equal(t, 1, tbl.Meta().Columns[1].Offset) + require.Equal(t, "_col$__col$_c1_0", tbl.Meta().Columns[2].Name.O) + require.Equal(t, mysql.TypeTiny, tbl.Meta().Columns[2].Tp) + require.Equal(t, 2, tbl.Meta().Columns[2].Offset) + require.Equal(t, "_col$__col$__col$_c1_0_0", tbl.Meta().Columns[3].Name.O) + require.Equal(t, mysql.TypeTiny, tbl.Meta().Columns[3].Tp) + require.Equal(t, 3, tbl.Meta().Columns[3].Offset) + + tk.MustExec("drop table if exists t") +} + +func TestWriteReorgForColumnTypeChangeOnAmendTxn(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomainWithSchemaLease(t, columnModifyLease) + defer clean() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set global tidb_enable_amend_pessimistic_txn = ON") + defer tk.MustExec("set global tidb_enable_amend_pessimistic_txn = OFF") + + d := dom.DDL() + testInsertOnModifyColumn := func(sql string, startColState, commitColState model.SchemaState, retStrs []string, retErr error) { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int, unique key(c1))") + tk.MustExec("insert into t1 values (20, 20, 20);") + + var checkErr error + tk1 := testkit.NewTestKit(t, store) + defer func() { + if tk1.Session() != nil { + tk1.Session().Close() + } + }() + hook := &ddl.TestDDLCallback{Do: dom} + times := 0 + hook.OnJobUpdatedExported = func(job *model.Job) { + if job.Type != model.ActionModifyColumn || checkErr != nil || + (job.SchemaState != startColState && job.SchemaState != commitColState) { + return + } + + if job.SchemaState == startColState { + tk1.MustExec("use test") + tk1.MustExec("begin pessimistic;") + tk1.MustExec("insert into t1 values(101, 102, 103)") + return + } + if times == 0 { + _, checkErr = tk1.Exec("commit;") + } + times++ + } + d.SetHook(hook) + + tk.MustExec(sql) + if retErr == nil { + require.NoError(t, checkErr) + } else { + require.Error(t, checkErr) + require.Contains(t, checkErr.Error(), retErr.Error()) + } + tk.MustQuery("select * from t1").Check(testkit.Rows(retStrs...)) + tk.MustExec("admin check table t1") + } + + // Testing it needs reorg data. + ddlStatement := "alter table t1 change column c2 cc smallint;" + testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StateWriteReorganization, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) + testInsertOnModifyColumn(ddlStatement, model.StateDeleteOnly, model.StateWriteReorganization, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) + testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StateWriteReorganization, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) + testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StatePublic, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) + testInsertOnModifyColumn(ddlStatement, model.StateDeleteOnly, model.StatePublic, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) + testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StatePublic, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) + + // Testing it needs not reorg data. This case only have two states: none, public. + ddlStatement = "alter table t1 change column c2 cc bigint;" + testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StateWriteReorganization, []string{"20 20 20"}, nil) + testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StateWriteReorganization, []string{"20 20 20"}, nil) + testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StatePublic, []string{"20 20 20", "101 102 103"}, nil) + testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StatePublic, []string{"20 20 20"}, nil) +} diff --git a/ddl/db_legacy_test.go b/ddl/db_legacy_test.go index 49459d5a85..dd01ef5b27 100644 --- a/ddl/db_legacy_test.go +++ b/ddl/db_legacy_test.go @@ -31,9 +31,7 @@ import ( "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/errno" "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" "github.com/pingcap/tidb/parser/auth" "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/mysql" @@ -47,7 +45,6 @@ import ( "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/tablecodec" "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/mock" "github.com/pingcap/tidb/util/sqlexec" "github.com/pingcap/tidb/util/testkit" "github.com/pingcap/tidb/util/testleak" @@ -167,159 +164,6 @@ func backgroundExecT(s kv.Storage, sql string, done chan error) { done <- errors.Trace(err) } -func (s *testSerialDBSuite) TestWriteReorgForColumnTypeChangeOnAmendTxn(c *C) { - tk2 := testkit.NewTestKit(c, s.store) - tk2.MustExec("use test_db") - tk2.MustExec("set global tidb_enable_amend_pessimistic_txn = ON;") - defer func() { - tk2.MustExec("set global tidb_enable_amend_pessimistic_txn = OFF;") - }() - - d := s.dom.DDL() - originalHook := d.GetHook() - defer d.SetHook(originalHook) - testInsertOnModifyColumn := func(sql string, startColState, commitColState model.SchemaState, retStrs []string, retErr error) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test_db") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int, unique key(c1))") - tk.MustExec("insert into t1 values (20, 20, 20);") - - var checkErr error - tk1 := testkit.NewTestKit(c, s.store) - defer func() { - if tk1.Se != nil { - tk1.Se.Close() - } - }() - hook := &ddl.TestDDLCallback{Do: s.dom} - times := 0 - hook.OnJobUpdatedExported = func(job *model.Job) { - if job.Type != model.ActionModifyColumn || checkErr != nil || - (job.SchemaState != startColState && job.SchemaState != commitColState) { - return - } - - if job.SchemaState == startColState { - tk1.MustExec("use test_db") - tk1.MustExec("begin pessimistic;") - tk1.MustExec("insert into t1 values(101, 102, 103)") - return - } - if times == 0 { - _, checkErr = tk1.Exec("commit;") - } - times++ - } - d.SetHook(hook) - - tk.MustExec(sql) - if retErr == nil { - c.Assert(checkErr, IsNil) - } else { - c.Assert(strings.Contains(checkErr.Error(), retErr.Error()), IsTrue) - } - tk.MustQuery("select * from t1;").Check(testkit.Rows(retStrs...)) - - tk.MustExec("admin check table t1") - } - - // Testing it needs reorg data. - ddlStatement := "alter table t1 change column c2 cc smallint;" - testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StateWriteReorganization, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) - testInsertOnModifyColumn(ddlStatement, model.StateDeleteOnly, model.StateWriteReorganization, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) - testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StateWriteReorganization, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) - testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StatePublic, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) - testInsertOnModifyColumn(ddlStatement, model.StateDeleteOnly, model.StatePublic, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) - testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StatePublic, []string{"20 20 20"}, domain.ErrInfoSchemaChanged) - - // Testing it needs not reorg data. This case only have two state: none, public. - ddlStatement = "alter table t1 change column c2 cc bigint;" - testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StateWriteReorganization, []string{"20 20 20"}, nil) - testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StateWriteReorganization, []string{"20 20 20"}, nil) - testInsertOnModifyColumn(ddlStatement, model.StateNone, model.StatePublic, []string{"20 20 20", "101 102 103"}, nil) - testInsertOnModifyColumn(ddlStatement, model.StateWriteOnly, model.StatePublic, []string{"20 20 20"}, nil) -} - -func (s *testSerialDBSuite) TestAddExpressionIndexRollback(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test_db") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int, unique key(c1))") - tk.MustExec("insert into t1 values (20, 20, 20), (40, 40, 40), (80, 80, 80), (160, 160, 160);") - - var checkErr error - tk1 := testkit.NewTestKit(c, s.store) - _, checkErr = tk1.Exec("use test_db") - - d := s.dom.DDL() - hook := &ddl.TestDDLCallback{Do: s.dom} - var currJob *model.Job - ctx := mock.NewContext() - ctx.Store = s.store - times := 0 - hook.OnJobUpdatedExported = func(job *model.Job) { - if checkErr != nil { - return - } - switch job.SchemaState { - case model.StateDeleteOnly: - _, checkErr = tk1.Exec("insert into t1 values (6, 3, 3) on duplicate key update c1 = 10") - if checkErr == nil { - _, checkErr = tk1.Exec("update t1 set c1 = 7 where c2=6;") - } - if checkErr == nil { - _, checkErr = tk1.Exec("delete from t1 where c1 = 40;") - } - case model.StateWriteOnly: - _, checkErr = tk1.Exec("insert into t1 values (2, 2, 2)") - if checkErr == nil { - _, checkErr = tk1.Exec("update t1 set c1 = 3 where c2 = 80") - } - case model.StateWriteReorganization: - if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 { - _, checkErr = tk1.Exec("insert into t1 values (4, 4, 4)") - if checkErr != nil { - return - } - _, checkErr = tk1.Exec("update t1 set c1 = 5 where c2 = 80") - if checkErr != nil { - return - } - currJob = job - times++ - } - } - } - d.SetHook(hook) - - tk.MustGetErrMsg("alter table t1 add index expr_idx ((pow(c1, c2)));", "[ddl:8202]Cannot decode index value, because [types:1690]DOUBLE value is out of range in 'pow(160, 160)'") - c.Assert(checkErr, IsNil) - tk.MustQuery("select * from t1 order by c1;").Check(testkit.Rows("2 2 2", "4 4 4", "5 80 80", "10 3 3", "20 20 20", "160 160 160")) - - // Check whether the reorg information is cleaned up. - err := ctx.NewTxn(context.Background()) - c.Assert(err, IsNil) - txn, err := ctx.Txn(true) - c.Assert(err, IsNil) - m := meta.NewMeta(txn) - element, start, end, physicalID, err := m.GetDDLReorgHandle(currJob) - c.Assert(meta.ErrDDLReorgElementNotExist.Equal(err), IsTrue) - c.Assert(element, IsNil) - c.Assert(start, IsNil) - c.Assert(end, IsNil) - c.Assert(physicalID, Equals, int64(0)) -} - -func (s *testSerialDBSuite) TestDropTableOnTiKVDiskFull(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test_db") - tk.MustExec("create table test_disk_full_drop_table(a int);") - c.Assert(failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull", `return(true)`), IsNil) - defer failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull") - tk.MustExec("drop table test_disk_full_drop_table;") -} - func testGetIndexID(t *testing.T, ctx sessionctx.Context, dbName, tblName, idxName string) int64 { is := domain.GetDomain(ctx).InfoSchema() tt, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) @@ -1209,125 +1053,6 @@ func (s *testSerialDBSuite) TestCreateTableWithIntegerLengthWaring(c *C) { tk.MustExec("drop table if exists t") } -func (s *testSerialDBSuite) TestColumnTypeChangeGenUniqueChangingName(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - - hook := &ddl.TestDDLCallback{} - var checkErr error - assertChangingColName := "_col$_c2_0" - assertChangingIdxName := "_idx$_idx_0" - hook.OnJobUpdatedExported = func(job *model.Job) { - if job.SchemaState == model.StateDeleteOnly && job.Type == model.ActionModifyColumn { - var ( - newCol *model.ColumnInfo - oldColName *model.CIStr - modifyColumnTp byte - updatedAutoRandomBits uint64 - changingCol *model.ColumnInfo - changingIdxs []*model.IndexInfo - ) - pos := &ast.ColumnPosition{} - err := job.DecodeArgs(&newCol, &oldColName, pos, &modifyColumnTp, &updatedAutoRandomBits, &changingCol, &changingIdxs) - if err != nil { - checkErr = err - return - } - if changingCol.Name.L != assertChangingColName { - checkErr = errors.New("changing column name is incorrect") - } else if changingIdxs[0].Name.L != assertChangingIdxName { - checkErr = errors.New("changing index name is incorrect") - } - } - } - d := s.dom.DDL() - originHook := d.GetHook() - d.SetHook(hook) - defer d.SetHook(originHook) - - tk.MustExec("create table if not exists t(c1 varchar(256), c2 bigint, `_col$_c2` varchar(10), unique _idx$_idx(c1), unique idx(c2));") - tk.MustExec("alter table test.t change column c2 cC2 tinyint after `_col$_c2`") - c.Assert(checkErr, IsNil) - - t := testGetTableByName(c, tk.Se, "test", "t") - c.Assert(len(t.Meta().Columns), Equals, 3) - c.Assert(t.Meta().Columns[0].Name.O, Equals, "c1") - c.Assert(t.Meta().Columns[0].Offset, Equals, 0) - c.Assert(t.Meta().Columns[1].Name.O, Equals, "_col$_c2") - c.Assert(t.Meta().Columns[1].Offset, Equals, 1) - c.Assert(t.Meta().Columns[2].Name.O, Equals, "cC2") - c.Assert(t.Meta().Columns[2].Offset, Equals, 2) - - c.Assert(len(t.Meta().Indices), Equals, 2) - c.Assert(t.Meta().Indices[0].Name.O, Equals, "_idx$_idx") - c.Assert(t.Meta().Indices[1].Name.O, Equals, "idx") - - c.Assert(len(t.Meta().Indices[0].Columns), Equals, 1) - c.Assert(t.Meta().Indices[0].Columns[0].Name.O, Equals, "c1") - c.Assert(t.Meta().Indices[0].Columns[0].Offset, Equals, 0) - - c.Assert(len(t.Meta().Indices[1].Columns), Equals, 1) - c.Assert(t.Meta().Indices[1].Columns[0].Name.O, Equals, "cC2") - c.Assert(t.Meta().Indices[1].Columns[0].Offset, Equals, 2) - - assertChangingColName1 := "_col$__col$_c1_1" - assertChangingColName2 := "_col$__col$__col$_c1_0_1" - query1 := "alter table t modify column _col$_c1 tinyint" - query2 := "alter table t modify column _col$__col$_c1_0 tinyint" - hook.OnJobUpdatedExported = func(job *model.Job) { - if (job.Query == query1 || job.Query == query2) && job.SchemaState == model.StateDeleteOnly && job.Type == model.ActionModifyColumn { - var ( - newCol *model.ColumnInfo - oldColName *model.CIStr - modifyColumnTp byte - updatedAutoRandomBits uint64 - changingCol *model.ColumnInfo - changingIdxs []*model.IndexInfo - ) - pos := &ast.ColumnPosition{} - err := job.DecodeArgs(&newCol, &oldColName, pos, &modifyColumnTp, &updatedAutoRandomBits, &changingCol, &changingIdxs) - if err != nil { - checkErr = err - return - } - if job.Query == query1 && changingCol.Name.L != assertChangingColName1 { - checkErr = errors.New("changing column name is incorrect") - } - if job.Query == query2 && changingCol.Name.L != assertChangingColName2 { - checkErr = errors.New("changing column name is incorrect") - } - } - } - d.SetHook(hook) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table if not exists t(c1 bigint, _col$_c1 bigint, _col$__col$_c1_0 bigint, _col$__col$__col$_c1_0_0 bigint)") - tk.MustExec("alter table t modify column c1 tinyint") - tk.MustExec("alter table t modify column _col$_c1 tinyint") - c.Assert(checkErr, IsNil) - tk.MustExec("alter table t modify column _col$__col$_c1_0 tinyint") - c.Assert(checkErr, IsNil) - tk.MustExec("alter table t change column _col$__col$__col$_c1_0_0 _col$__col$__col$_c1_0_0 tinyint") - - t = testGetTableByName(c, tk.Se, "test", "t") - c.Assert(len(t.Meta().Columns), Equals, 4) - c.Assert(t.Meta().Columns[0].Name.O, Equals, "c1") - c.Assert(t.Meta().Columns[0].Tp, Equals, mysql.TypeTiny) - c.Assert(t.Meta().Columns[0].Offset, Equals, 0) - c.Assert(t.Meta().Columns[1].Name.O, Equals, "_col$_c1") - c.Assert(t.Meta().Columns[1].Tp, Equals, mysql.TypeTiny) - c.Assert(t.Meta().Columns[1].Offset, Equals, 1) - c.Assert(t.Meta().Columns[2].Name.O, Equals, "_col$__col$_c1_0") - c.Assert(t.Meta().Columns[2].Tp, Equals, mysql.TypeTiny) - c.Assert(t.Meta().Columns[2].Offset, Equals, 2) - c.Assert(t.Meta().Columns[3].Name.O, Equals, "_col$__col$__col$_c1_0_0") - c.Assert(t.Meta().Columns[3].Tp, Equals, mysql.TypeTiny) - c.Assert(t.Meta().Columns[3].Offset, Equals, 3) - - tk.MustExec("drop table if exists t") -} - // Close issue #24172. // See https://github.com/pingcap/tidb/issues/24172 func (s *testSerialDBSuite) TestCancelJobWriteConflict(c *C) { @@ -1372,52 +1097,3 @@ func (s *testSerialDBSuite) TestCancelJobWriteConflict(c *C) { result := tk1.ResultSetToResultWithCtx(context.Background(), rs[0], Commentf("cancel ddl job fails")) result.Check(testkit.Rows(fmt.Sprintf("%d successful", jobID))) } - -func (s *testSerialDBSuite) TestAddGeneratedColumnAndInsert(c *C) { - // For issue #31735. - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test_db") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int, unique kye(a))") - tk.MustExec("insert into t1 value (1), (10)") - - var checkErr error - tk1 := testkit.NewTestKit(c, s.store) - _, checkErr = tk1.Exec("use test_db") - - d := s.dom.DDL() - hook := &ddl.TestDDLCallback{Do: s.dom} - ctx := mock.NewContext() - ctx.Store = s.store - times := 0 - hook.OnJobUpdatedExported = func(job *model.Job) { - if checkErr != nil { - return - } - switch job.SchemaState { - case model.StateDeleteOnly: - _, checkErr = tk1.Exec("insert into t1 values (1) on duplicate key update a=a+1") - if checkErr == nil { - _, checkErr = tk1.Exec("replace into t1 values (2)") - } - case model.StateWriteOnly: - _, checkErr = tk1.Exec("insert into t1 values (2) on duplicate key update a=a+1") - if checkErr == nil { - _, checkErr = tk1.Exec("replace into t1 values (3)") - } - case model.StateWriteReorganization: - if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 { - _, checkErr = tk1.Exec("insert into t1 values (3) on duplicate key update a=a+1") - if checkErr == nil { - _, checkErr = tk1.Exec("replace into t1 values (4)") - } - times++ - } - } - } - d.SetHook(hook) - - tk.MustExec("alter table t1 add column gc int as ((a+1))") - tk.MustQuery("select * from t1 order by a").Check(testkit.Rows("4 5", "10 11")) - c.Assert(checkErr, IsNil) -} diff --git a/ddl/db_test.go b/ddl/db_test.go index 945af05537..2e4a2bf34e 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -23,12 +23,14 @@ import ( "time" "github.com/pingcap/errors" + "github.com/pingcap/failpoint" "github.com/pingcap/tidb/ddl" ddlutil "github.com/pingcap/tidb/ddl/util" "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/errno" "github.com/pingcap/tidb/executor" "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/meta" "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/sessionctx" @@ -1164,3 +1166,87 @@ func TestCancelTruncateTable(t *testing.T) { require.NoError(t, checkErr) require.EqualError(t, err, "[ddl:8214]Cancelled DDL job") } + +func TestAddExpressionIndexRollback(t *testing.T) { + store, dom, clean := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + defer clean() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int, unique key(c1))") + tk.MustExec("insert into t1 values (20, 20, 20), (40, 40, 40), (80, 80, 80), (160, 160, 160);") + + var checkErr error + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + + d := dom.DDL() + hook := &ddl.TestDDLCallback{Do: dom} + var currJob *model.Job + ctx := mock.NewContext() + ctx.Store = store + times := 0 + hook.OnJobUpdatedExported = func(job *model.Job) { + if checkErr != nil { + return + } + switch job.SchemaState { + case model.StateDeleteOnly: + _, checkErr = tk1.Exec("insert into t1 values (6, 3, 3) on duplicate key update c1 = 10") + if checkErr == nil { + _, checkErr = tk1.Exec("update t1 set c1 = 7 where c2=6;") + } + if checkErr == nil { + _, checkErr = tk1.Exec("delete from t1 where c1 = 40;") + } + case model.StateWriteOnly: + _, checkErr = tk1.Exec("insert into t1 values (2, 2, 2)") + if checkErr == nil { + _, checkErr = tk1.Exec("update t1 set c1 = 3 where c2 = 80") + } + case model.StateWriteReorganization: + if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 { + _, checkErr = tk1.Exec("insert into t1 values (4, 4, 4)") + if checkErr != nil { + return + } + _, checkErr = tk1.Exec("update t1 set c1 = 5 where c2 = 80") + if checkErr != nil { + return + } + currJob = job + times++ + } + } + } + d.SetHook(hook) + + tk.MustGetErrMsg("alter table t1 add index expr_idx ((pow(c1, c2)));", "[ddl:8202]Cannot decode index value, because [types:1690]DOUBLE value is out of range in 'pow(160, 160)'") + require.NoError(t, checkErr) + tk.MustQuery("select * from t1 order by c1;").Check(testkit.Rows("2 2 2", "4 4 4", "5 80 80", "10 3 3", "20 20 20", "160 160 160")) + + // Check whether the reorg information is cleaned up. + err := ctx.NewTxn(context.Background()) + require.NoError(t, err) + txn, err := ctx.Txn(true) + require.NoError(t, err) + m := meta.NewMeta(txn) + element, start, end, physicalID, err := m.GetDDLReorgHandle(currJob) + require.True(t, meta.ErrDDLReorgElementNotExist.Equal(err)) + require.Nil(t, element) + require.Nil(t, start) + require.Nil(t, end) + require.Equal(t, int64(0), physicalID) +} + +func TestDropTableOnTiKVDiskFull(t *testing.T) { + store, clean := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + defer clean() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table test_disk_full_drop_table(a int);") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull")) + }() + tk.MustExec("drop table test_disk_full_drop_table;") +}