From d6078016c9668acad39395bd2a90824a6d44fde3 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 19 Dec 2022 13:43:52 +0800 Subject: [PATCH 01/19] fix alter add foreign key privilege check and txn too large check Signed-off-by: crazycs520 --- ddl/fktest/foreign_key_test.go | 18 +++++++++++++++++ executor/fktest/foreign_key_test.go | 31 +++++++++++++++++++++++++++++ planner/core/planbuilder.go | 6 ++++++ 3 files changed, 55 insertions(+) diff --git a/ddl/fktest/foreign_key_test.go b/ddl/fktest/foreign_key_test.go index 911f86f49ec85..13701ad5b610d 100644 --- a/ddl/fktest/foreign_key_test.go +++ b/ddl/fktest/foreign_key_test.go @@ -319,6 +319,24 @@ func TestCreateTableWithForeignKeyPrivilegeCheck(t *testing.T) { tk2.MustExec("create table t4 (a int, foreign key fk(a) references t1(id), foreign key (a) references t3(id));") } +func TestAlterTableWithForeignKeyPrivilegeCheck(t *testing.T) { + store, _ := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create user 'u1'@'%' identified by '';") + tk.MustExec("grant create,alter on *.* to 'u1'@'%';") + tk.MustExec("create table t1 (id int key);") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890")) + tk2.MustExec("create table t2 (a int)") + err := tk2.ExecToErr("alter table t2 add foreign key (a) references t1 (id) on update cascade") + require.Error(t, err) + require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't1'", err.Error()) + tk.MustExec("grant references on test.t1 to 'u1'@'%';") + tk2.MustExec("alter table t2 add foreign key (a) references t1 (id) on update cascade") +} + func TestRenameTableWithForeignKeyMetaInfo(t *testing.T) { store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) diff --git a/executor/fktest/foreign_key_test.go b/executor/fktest/foreign_key_test.go index a162bd22b96aa..976c1d5c12c57 100644 --- a/executor/fktest/foreign_key_test.go +++ b/executor/fktest/foreign_key_test.go @@ -21,6 +21,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "testing" "time" @@ -2539,3 +2540,33 @@ func TestForeignKeyIssue39732(t *testing.T) { tk.MustExec("execute stmt1 using @a;") tk.MustQuery("select * from t1 order by id").Check(testkit.Rows()) } + +func TestForeignKeyLargeTxnErr(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int auto_increment key, pid int, name varchar(200), index(pid));") + tk.MustExec("insert into t1 (name) values ('abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890');") + for i := 0; i < 8; i++ { + tk.MustExec("insert into t1 (name) select name from t1;") + } + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("256")) + tk.MustExec("update t1 set pid=1 where id>1") + tk.MustExec("alter table t1 add foreign key (pid) references t1 (id) on update cascade") + originLimit := atomic.LoadUint64(&kv.TxnTotalSizeLimit) + defer func() { + atomic.StoreUint64(&kv.TxnTotalSizeLimit, originLimit) + }() + // Set the limitation to a small value, make it easier to reach the limitation. + atomic.StoreUint64(&kv.TxnTotalSizeLimit, 10240) + tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) + // foreign key cascade behaviour will cause ErrTxnTooLarge. + tk.MustGetDBError("update t1 set id=id+100000 where id=1", kv.ErrTxnTooLarge) + tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) + tk.MustGetDBError("update t1 set id=id+100000 where id=1", kv.ErrTxnTooLarge) + tk.MustQuery("select id,pid from t1 where id<3 order by id").Check(testkit.Rows("1 ", "2 1")) + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec("update t1 set id=id+100000 where id=1") + tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) +} diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 9201f953bdcdc..5fbc9607013a5 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -4511,6 +4511,12 @@ func (b *PlanBuilder) buildDDL(ctx context.Context, node ast.DDLNode) (Plan, err } b.visitInfo = appendVisitInfo(b.visitInfo, mysql.UpdatePriv, mysql.SystemDB, "stats_extended", "", authErr) + } else if spec.Tp == ast.AlterTableAddConstraint && spec.Constraint != nil && + spec.Constraint.Tp == ast.ConstraintForeignKey && spec.Constraint.Refer != nil { + authErr = ErrTableaccessDenied.GenWithStackByArgs("REFERENCES", b.ctx.GetSessionVars().User.AuthUsername, + b.ctx.GetSessionVars().User.AuthHostname, spec.Constraint.Refer.Table.Name.L) + b.visitInfo = appendVisitInfo(b.visitInfo, mysql.ReferencesPriv, spec.Constraint.Refer.Table.Schema.L, + spec.Constraint.Refer.Table.Name.L, "", authErr) } } case *ast.AlterSequenceStmt: From 57d8dad62fea5c02e8d9eeaec9e7af9c9bf58cf3 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 19 Dec 2022 15:37:02 +0800 Subject: [PATCH 02/19] fix bug and add test for temporary table and cache table Signed-off-by: crazycs520 --- ddl/fktest/foreign_key_test.go | 64 ++++++++++++++++++++++++++++++++++ planner/core/planbuilder.go | 3 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/ddl/fktest/foreign_key_test.go b/ddl/fktest/foreign_key_test.go index 13701ad5b610d..f8cd1fbe905b4 100644 --- a/ddl/fktest/foreign_key_test.go +++ b/ddl/fktest/foreign_key_test.go @@ -666,6 +666,17 @@ func TestCreateTableWithForeignKeyError(t *testing.T) { create: "create table t2 (id int key, constraint fk foreign key (id) references t1(name5678901234567890123456789012345678901234567890123456789012345));", err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", }, + // Test foreign key with temporary table + { + refer: "create temporary table t1 (id int key);", + create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id));", + err: "[schema:1824]Failed to open the referenced table 't1'", + }, + { + refer: "create table t1 (id int key);", + create: "create temporary table t2 (id int key, constraint fk foreign key (id) references t1(id));", + err: "[schema:1215]Cannot add foreign key constraint", + }, } for _, ca := range cases { tk.MustExec("drop table if exists t2") @@ -1414,6 +1425,23 @@ func TestAlterTableAddForeignKeyError(t *testing.T) { alter: "alter table t2 add constraint name5678901234567890123456789012345678901234567890123456789012345 foreign key (b) references t1(id)", err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", }, + // Test foreign key with temporary table. + { + prepares: []string{ + "create temporary table t1 (id int key);", + "create table t2 (a int, b int unique);", + }, + alter: "alter table t2 add constraint fk foreign key (b) references t1(id)", + err: "[schema:1824]Failed to open the referenced table 't1'", + }, + { + prepares: []string{ + "create table t1 (id int key);", + "create temporary table t2 (a int, b int unique);", + }, + alter: "alter table t2 add constraint fk foreign key (b) references t1(id)", + err: "[ddl:8200]TiDB doesn't support ALTER TABLE for local temporary table", + }, } for i, ca := range cases { tk.MustExec("drop table if exists t2") @@ -1562,3 +1590,39 @@ func getLatestSchemaDiff(t *testing.T, tk *testkit.TestKit) *model.SchemaDiff { require.NoError(t, err) return diff } + +func TestForeignKeyWithCacheTable(t *testing.T) { + store, _ := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + // Test foreign key refer cache table. + tk.MustExec("create table t1 (id int key);") + tk.MustExec("insert into t1 values (1),(2),(3),(4)") + tk.MustExec("alter table t1 cache;") + tk.MustExec("create table t2 (b int);") + tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") + tk.MustExec("insert into t2 values (1),(2),(3),(4)") + tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t1 set id = id+10 where id=1") + tk.MustExec("delete from t1 where id<10") + tk.MustQuery("select * from t1").Check(testkit.Rows("11")) + tk.MustQuery("select * from t2").Check(testkit.Rows("11")) + tk.MustExec("alter table t1 nocache;") + tk.MustExec("drop table t1,t2;") + + // Test add foreign key on cache table. + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (b int);") + tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") + tk.MustExec("alter table t2 cache;") + tk.MustExec("insert into t1 values (1),(2),(3),(4)") + tk.MustExec("insert into t2 values (1),(2),(3),(4)") + tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t1 set id = id+10 where id=1") + tk.MustExec("delete from t1 where id<10") + tk.MustQuery("select * from t1").Check(testkit.Rows("11")) + tk.MustQuery("select * from t2").Check(testkit.Rows("11")) + tk.MustExec("alter table t2 nocache;") + tk.MustExec("drop table t1,t2;") +} diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 5fbc9607013a5..4ffa25557b021 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -4512,7 +4512,8 @@ func (b *PlanBuilder) buildDDL(ctx context.Context, node ast.DDLNode) (Plan, err b.visitInfo = appendVisitInfo(b.visitInfo, mysql.UpdatePriv, mysql.SystemDB, "stats_extended", "", authErr) } else if spec.Tp == ast.AlterTableAddConstraint && spec.Constraint != nil && - spec.Constraint.Tp == ast.ConstraintForeignKey && spec.Constraint.Refer != nil { + spec.Constraint.Tp == ast.ConstraintForeignKey && spec.Constraint.Refer != nil && + b.ctx.GetSessionVars().User != nil { authErr = ErrTableaccessDenied.GenWithStackByArgs("REFERENCES", b.ctx.GetSessionVars().User.AuthUsername, b.ctx.GetSessionVars().User.AuthHostname, spec.Constraint.Refer.Table.Name.L) b.visitInfo = appendVisitInfo(b.visitInfo, mysql.ReferencesPriv, spec.Constraint.Refer.Table.Schema.L, From 9d13a89a31b357ed6a318c7083954345a32987e0 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 19 Dec 2022 16:04:50 +0800 Subject: [PATCH 03/19] add test for fk with partition table Signed-off-by: crazycs520 --- ddl/fktest/foreign_key_test.go | 28 ++++++++++++++++++++++++++++ ddl/foreign_key.go | 3 +++ infoschema/error.go | 2 ++ 3 files changed, 33 insertions(+) diff --git a/ddl/fktest/foreign_key_test.go b/ddl/fktest/foreign_key_test.go index f8cd1fbe905b4..0f27cde462d2c 100644 --- a/ddl/fktest/foreign_key_test.go +++ b/ddl/fktest/foreign_key_test.go @@ -677,6 +677,17 @@ func TestCreateTableWithForeignKeyError(t *testing.T) { create: "create temporary table t2 (id int key, constraint fk foreign key (id) references t1(id));", err: "[schema:1215]Cannot add foreign key constraint", }, + // Test foreign key with partition table + { + refer: "create table t1 (id int key) partition by hash(id) partitions 3;", + create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id));", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, + { + refer: "create table t1 (id int key);", + create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id)) partition by hash(id) partitions 3;", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, } for _, ca := range cases { tk.MustExec("drop table if exists t2") @@ -1442,6 +1453,23 @@ func TestAlterTableAddForeignKeyError(t *testing.T) { alter: "alter table t2 add constraint fk foreign key (b) references t1(id)", err: "[ddl:8200]TiDB doesn't support ALTER TABLE for local temporary table", }, + // Test foreign key with partition table + { + prepares: []string{ + "create table t1 (id int key) partition by hash(id) partitions 3;", + "create table t2 (id int key);", + }, + alter: "alter table t2 add constraint fk foreign key (id) references t1(id)", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, + { + prepares: []string{ + "create table t1 (id int key);", + "create table t2 (id int key) partition by hash(id) partitions 3;;", + }, + alter: "alter table t2 add constraint fk foreign key (id) references t1(id)", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, } for i, ca := range cases { tk.MustExec("drop table if exists t2") diff --git a/ddl/foreign_key.go b/ddl/foreign_key.go index af9570fbee837..4b6d7bc8b58e2 100644 --- a/ddl/foreign_key.go +++ b/ddl/foreign_key.go @@ -266,6 +266,9 @@ func checkTableForeignKey(referTblInfo, tblInfo *model.TableInfo, fkInfo *model. if referTblInfo.TTLInfo != nil { return dbterror.ErrUnsupportedTTLReferencedByFK } + if referTblInfo.GetPartitionInfo() != nil || tblInfo.GetPartitionInfo() != nil { + return infoschema.ErrForeignKeyOnPartitioned + } // check refer columns in parent table. for i := range fkInfo.RefCols { diff --git a/infoschema/error.go b/infoschema/error.go index a7e4929a35bcc..b1f2c8d11fbff 100644 --- a/infoschema/error.go +++ b/infoschema/error.go @@ -64,6 +64,8 @@ var ( ErrKeyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrKeyDoesNotExist) // ErrCannotAddForeign returns for foreign key exists. ErrCannotAddForeign = dbterror.ClassSchema.NewStd(mysql.ErrCannotAddForeign) + // ErrCannotAddForeign returns for foreign key on partition table. + ErrForeignKeyOnPartitioned = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyOnPartitioned) // ErrForeignKeyNotMatch returns for foreign key not match. ErrForeignKeyNotMatch = dbterror.ClassSchema.NewStd(mysql.ErrWrongFkDef) // ErrIndexExists returns for index already exists. From 0cdafadfe8e133a9209dabc29d6b5e5f9010cc9e Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 20 Dec 2022 11:12:51 +0800 Subject: [PATCH 04/19] refine Signed-off-by: crazycs520 --- ddl/fktest/foreign_key_test.go | 18 ------------------ planner/core/planbuilder.go | 7 ------- 2 files changed, 25 deletions(-) diff --git a/ddl/fktest/foreign_key_test.go b/ddl/fktest/foreign_key_test.go index 0f27cde462d2c..38b2753511880 100644 --- a/ddl/fktest/foreign_key_test.go +++ b/ddl/fktest/foreign_key_test.go @@ -319,24 +319,6 @@ func TestCreateTableWithForeignKeyPrivilegeCheck(t *testing.T) { tk2.MustExec("create table t4 (a int, foreign key fk(a) references t1(id), foreign key (a) references t3(id));") } -func TestAlterTableWithForeignKeyPrivilegeCheck(t *testing.T) { - store, _ := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create user 'u1'@'%' identified by '';") - tk.MustExec("grant create,alter on *.* to 'u1'@'%';") - tk.MustExec("create table t1 (id int key);") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890")) - tk2.MustExec("create table t2 (a int)") - err := tk2.ExecToErr("alter table t2 add foreign key (a) references t1 (id) on update cascade") - require.Error(t, err) - require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't1'", err.Error()) - tk.MustExec("grant references on test.t1 to 'u1'@'%';") - tk2.MustExec("alter table t2 add foreign key (a) references t1 (id) on update cascade") -} - func TestRenameTableWithForeignKeyMetaInfo(t *testing.T) { store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 4ffa25557b021..9201f953bdcdc 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -4511,13 +4511,6 @@ func (b *PlanBuilder) buildDDL(ctx context.Context, node ast.DDLNode) (Plan, err } b.visitInfo = appendVisitInfo(b.visitInfo, mysql.UpdatePriv, mysql.SystemDB, "stats_extended", "", authErr) - } else if spec.Tp == ast.AlterTableAddConstraint && spec.Constraint != nil && - spec.Constraint.Tp == ast.ConstraintForeignKey && spec.Constraint.Refer != nil && - b.ctx.GetSessionVars().User != nil { - authErr = ErrTableaccessDenied.GenWithStackByArgs("REFERENCES", b.ctx.GetSessionVars().User.AuthUsername, - b.ctx.GetSessionVars().User.AuthHostname, spec.Constraint.Refer.Table.Name.L) - b.visitInfo = appendVisitInfo(b.visitInfo, mysql.ReferencesPriv, spec.Constraint.Refer.Table.Schema.L, - spec.Constraint.Refer.Table.Name.L, "", authErr) } } case *ast.AlterSequenceStmt: From 07d1a0ac0f111dabdaa0d1e94e12b4a5a52c1d86 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 20 Dec 2022 11:31:13 +0800 Subject: [PATCH 05/19] fix ci Signed-off-by: crazycs520 --- infoschema/error.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infoschema/error.go b/infoschema/error.go index b1f2c8d11fbff..d89d3e3dc8201 100644 --- a/infoschema/error.go +++ b/infoschema/error.go @@ -64,7 +64,7 @@ var ( ErrKeyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrKeyDoesNotExist) // ErrCannotAddForeign returns for foreign key exists. ErrCannotAddForeign = dbterror.ClassSchema.NewStd(mysql.ErrCannotAddForeign) - // ErrCannotAddForeign returns for foreign key on partition table. + // ErrForeignKeyOnPartitioned returns for foreign key on partition table. ErrForeignKeyOnPartitioned = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyOnPartitioned) // ErrForeignKeyNotMatch returns for foreign key not match. ErrForeignKeyNotMatch = dbterror.ClassSchema.NewStd(mysql.ErrWrongFkDef) From e3cfe893037dd3fbdc414aefaa6be5494e1d71d6 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 20 Dec 2022 12:44:17 +0800 Subject: [PATCH 06/19] fix ci Signed-off-by: crazycs520 --- errors.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/errors.toml b/errors.toml index 32b8d40e902b2..86fe8931f9c7c 100644 --- a/errors.toml +++ b/errors.toml @@ -2406,6 +2406,11 @@ error = ''' Changing schema from '%-.192s' to '%-.192s' is not allowed. ''' +["schema:1506"] +error = ''' +Foreign key clause is not yet supported in conjunction with partitioning +''' + ["schema:1822"] error = ''' Failed to add the foreign key constraint. Missing index for constraint '%s' in the referenced table '%s' From 2bc21b2173185291967f782766f1088fdef772e2 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 20 Dec 2022 15:13:09 +0800 Subject: [PATCH 07/19] add foreign key and lock view test Signed-off-by: crazycs520 --- executor/fktest/foreign_key_test.go | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/executor/fktest/foreign_key_test.go b/executor/fktest/foreign_key_test.go index 976c1d5c12c57..ec2a4e41fcef0 100644 --- a/executor/fktest/foreign_key_test.go +++ b/executor/fktest/foreign_key_test.go @@ -29,6 +29,7 @@ import ( "github.com/pingcap/tidb/executor" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/parser" "github.com/pingcap/tidb/parser/ast" "github.com/pingcap/tidb/parser/auth" "github.com/pingcap/tidb/parser/format" @@ -2570,3 +2571,33 @@ func TestForeignKeyLargeTxnErr(t *testing.T) { tk.MustExec("update t1 set id=id+100000 where id=1") tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) } + +func TestForeignKeyAndLockView(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int key, foreign key (id) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE)") + tk.MustExec("insert into t1 values (1)") + tk.MustExec("insert into t2 values (1)") + tk.MustExec("begin pessimistic") + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec("update t2 set id=2") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@foreign_key_checks=1") + tk2.MustExec("use test") + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustExec("begin pessimistic") + tk2.MustExec("update t1 set id=2 where id=1") + tk2.MustExec("commit") + }() + time.Sleep(time.Millisecond * 200) + _, digest := parser.NormalizeDigest("update t1 set id=2 where id=1") + tk.MustQuery("select CURRENT_SQL_DIGEST from information_schema.tidb_trx where state='LockWaiting' and db='test'").Check(testkit.Rows(digest.String())) + tk.MustGetErrMsg("update t1 set id=2", "[executor:1213]Deadlock found when trying to get lock; try restarting transaction") + wg.Wait() +} From a07f3b1ba84589c5f523b8c0e72a1c6f655487b6 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 20 Dec 2022 16:00:01 +0800 Subject: [PATCH 08/19] add foreign key and memory tracker test Signed-off-by: crazycs520 --- executor/fktest/foreign_key_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/executor/fktest/foreign_key_test.go b/executor/fktest/foreign_key_test.go index ec2a4e41fcef0..e52cdccdf7f9f 100644 --- a/executor/fktest/foreign_key_test.go +++ b/executor/fktest/foreign_key_test.go @@ -2601,3 +2601,29 @@ func TestForeignKeyAndLockView(t *testing.T) { tk.MustGetErrMsg("update t1 set id=2", "[executor:1213]Deadlock found when trying to get lock; try restarting transaction") wg.Wait() } + +func TestForeignKeyAndMemoryTracker(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int auto_increment key, pid int, name varchar(200), index(pid));") + tk.MustExec("insert into t1 (name) values ('abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz');") + for i := 0; i < 8; i++ { + tk.MustExec("insert into t1 (name) select name from t1;") + } + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("256")) + tk.MustExec("update t1 set pid=1 where id>1") + tk.MustExec("alter table t1 add foreign key (pid) references t1 (id) on update cascade") + tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) + defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") + tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") + tk.MustExec("set @@tidb_mem_quota_query=40960;") + // foreign key cascade behaviour will exceed memory quota. + tk.MustGetErrMsg("update t1 set id=id+100000 where id=1", "Out Of Memory Quota![conn_id=1]") + tk.MustQuery("select id,pid from t1 where id = 1").Check(testkit.Rows("1 ")) + tk.MustExec("set @@foreign_key_checks=0") + // After disable foreign_key_checks, following DML will execute successful. + tk.MustExec("update t1 set id=id+100000 where id=1") + tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) +} From 5ffd336f91487ea6ee106539cba5e09df1e63d88 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 20 Dec 2022 17:25:47 +0800 Subject: [PATCH 09/19] add conccurent ddl test Signed-off-by: crazycs520 --- ddl/fktest/foreign_key_test.go | 92 ++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/ddl/fktest/foreign_key_test.go b/ddl/fktest/foreign_key_test.go index 38b2753511880..0173d417d9642 100644 --- a/ddl/fktest/foreign_key_test.go +++ b/ddl/fktest/foreign_key_test.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "fmt" + "sync" "testing" "github.com/pingcap/tidb/domain" @@ -1636,3 +1637,94 @@ func TestForeignKeyWithCacheTable(t *testing.T) { tk.MustExec("alter table t2 nocache;") tk.MustExec("drop table t1,t2;") } + +func TestForeignKeyAndConcurrentDDL(t *testing.T) { + store, _ := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + // Test foreign key refer cache table. + tk.MustExec("create table t1 (a int, b int, c int, index(a), index(b), index(c));") + tk.MustExec("create table t2 (a int, b int, c int, index(a), index(b), index(c));") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@foreign_key_checks=1;") + tk2.MustExec("use test") + passCases := []struct { + ddl1 string + ddl2 string + }{ + { + ddl1: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + ddl2: "alter table t2 add constraint fk_2 foreign key (b) references t1(b)", + }, + { + ddl1: "alter table t2 drop foreign key fk_1", + ddl2: "alter table t2 drop foreign key fk_2", + }, + } + for _, ca := range passCases { + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + tk.MustExec(ca.ddl1) + }() + go func() { + defer wg.Done() + tk2.MustExec(ca.ddl2) + }() + wg.Wait() + } + errorCases := []struct { + prepare []string + ddl1 string + err1 string + ddl2 string + err2 string + }{ + { + ddl1: "alter table t2 add constraint fk foreign key (a) references t1(a)", + err1: "[ddl:1826]Duplicate foreign key constraint name 'fk'", + ddl2: "alter table t2 add constraint fk foreign key (b) references t1(b)", + err2: "[ddl:1826]Duplicate foreign key constraint name 'fk'", + }, + { + prepare: []string{ + "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + }, + ddl1: "alter table t2 drop foreign key fk_1", + err1: "[schema:1091]Can't DROP 'fk_1'; check that column/key exists", + ddl2: "alter table t2 drop foreign key fk_1", + err2: "[schema:1091]Can't DROP 'fk_1'; check that column/key exists", + }, + } + tk.MustExec("drop table t1,t2") + tk.MustExec("create table t1 (a int, b int, c int, index(a), index(b), index(c));") + tk.MustExec("create table t2 (a int, b int, c int, index(a), index(b), index(c));") + for i, ca := range errorCases { + for _, sql := range ca.prepare { + tk.MustExec(sql) + } + var wg sync.WaitGroup + var err1, err2 error + wg.Add(2) + go func() { + defer wg.Done() + err1 = tk.ExecToErr(ca.ddl1) + }() + go func() { + defer wg.Done() + err2 = tk2.ExecToErr(ca.ddl2) + }() + wg.Wait() + if (err1 == nil && err2 == nil) || (err1 != nil && err2 != nil) { + require.Failf(t, "both ddl1 and ddl2 execute success, but expect 1 error", fmt.Sprintf("idx: %v, err1: %v, err2: %v", i, err1, err2)) + } + if err1 != nil { + require.Equal(t, ca.err1, err1.Error()) + } + if err2 != nil { + require.Equal(t, ca.err2, err2.Error()) + } + } +} From c3047de7e21673bea62b93dd30b11e90ffa10fd7 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Wed, 21 Dec 2022 14:49:26 +0800 Subject: [PATCH 10/19] add more test case in TestForeignKeyAndConcurrentDDL Signed-off-by: crazycs520 --- ddl/fktest/foreign_key_test.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/ddl/fktest/foreign_key_test.go b/ddl/fktest/foreign_key_test.go index 0173d417d9642..aeec82dcc7a11 100644 --- a/ddl/fktest/foreign_key_test.go +++ b/ddl/fktest/foreign_key_test.go @@ -1650,8 +1650,9 @@ func TestForeignKeyAndConcurrentDDL(t *testing.T) { tk2.MustExec("set @@foreign_key_checks=1;") tk2.MustExec("use test") passCases := []struct { - ddl1 string - ddl2 string + prepare []string + ddl1 string + ddl2 string }{ { ddl1: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", @@ -1661,6 +1662,17 @@ func TestForeignKeyAndConcurrentDDL(t *testing.T) { ddl1: "alter table t2 drop foreign key fk_1", ddl2: "alter table t2 drop foreign key fk_2", }, + { + prepare: []string{ + "alter table t2 drop index a", + }, + ddl1: "alter table t2 add index(a)", + ddl2: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + }, + { + ddl1: "alter table t2 drop index c", + ddl2: "alter table t2 add constraint fk_2 foreign key (b) references t1(b)", + }, } for _, ca := range passCases { var wg sync.WaitGroup @@ -1697,6 +1709,12 @@ func TestForeignKeyAndConcurrentDDL(t *testing.T) { ddl2: "alter table t2 drop foreign key fk_1", err2: "[schema:1091]Can't DROP 'fk_1'; check that column/key exists", }, + { + ddl1: "alter table t2 drop index a", + err1: "[ddl:1553]Cannot drop index 'a': needed in a foreign key constraint", + ddl2: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + err2: "[ddl:-1]Failed to add the foreign key constraint. Missing index for 'fk_1' foreign key columns in the table 't2'", + }, } tk.MustExec("drop table t1,t2") tk.MustExec("create table t1 (a int, b int, c int, index(a), index(b), index(c));") From a5efc3dab0d80b60fe1b5fbdce82fc53c88a6f8a Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Wed, 21 Dec 2022 15:08:51 +0800 Subject: [PATCH 11/19] add foreign key and mdl test Signed-off-by: crazycs520 --- ddl/metadatalocktest/mdl_test.go | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/ddl/metadatalocktest/mdl_test.go b/ddl/metadatalocktest/mdl_test.go index 64bdf77d55707..fd307968cad73 100644 --- a/ddl/metadatalocktest/mdl_test.go +++ b/ddl/metadatalocktest/mdl_test.go @@ -257,6 +257,47 @@ func TestMDLBasicBatchPointGet(t *testing.T) { require.Less(t, ts1, ts2) } +func TestMDLAddForeignKey(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + sv := server.CreateMockServer(t, store) + + sv.SetDomain(dom) + dom.InfoSyncer().SetSessionManager(sv) + defer sv.Close() + + conn1 := server.CreateMockConn(t, sv) + tk := testkit.NewTestKitWithSession(t, store, conn1.Context().Session) + conn2 := server.CreateMockConn(t, sv) + tkDDL := testkit.NewTestKitWithSession(t, store, conn2.Context().Session) + tk.MustExec("use test") + tk.MustExec("set global tidb_enable_metadata_lock=1") + tk.MustExec("create table t1(id int key);") + tk.MustExec("create table t2(id int key);") + + tk.MustExec("begin") + tk.MustExec("insert into t2 values(1);") + + var wg sync.WaitGroup + var ddlErr error + wg.Add(1) + var ts2 time.Time + go func() { + defer wg.Done() + ddlErr = tkDDL.ExecToErr("alter table test.t2 add foreign key (id) references t1(id)") + ts2 = time.Now() + }() + + time.Sleep(2 * time.Second) + + ts1 := time.Now() + tk.MustExec("commit") + + wg.Wait() + require.Error(t, ddlErr) + require.Equal(t, "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_1` FOREIGN KEY (`id`) REFERENCES `t1` (`id`))", ddlErr.Error()) + require.Less(t, ts1, ts2) +} + func TestMDLRRUpdateSchema(t *testing.T) { store, dom := testkit.CreateMockStoreAndDomain(t) sv := server.CreateMockServer(t, store) From d1ec0c8558720841f54cba2010c17df34420d252 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Wed, 21 Dec 2022 16:17:09 +0800 Subject: [PATCH 12/19] add foreign key and async commit test Signed-off-by: crazycs520 --- .../pessimistictest/pessimistic_test.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/realtikvtest/pessimistictest/pessimistic_test.go b/tests/realtikvtest/pessimistictest/pessimistic_test.go index ae7545e0e91f6..ed12510899cc7 100644 --- a/tests/realtikvtest/pessimistictest/pessimistic_test.go +++ b/tests/realtikvtest/pessimistictest/pessimistic_test.go @@ -2816,6 +2816,31 @@ func TestAsyncCommitCalTSFail(t *testing.T) { tk2.MustExec("commit") } +func TestAsyncCommitAndForeignKey(t *testing.T) { + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = time.Second + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + store := realtikvtest.CreateMockStoreAndSetup(t) + tk := createAsyncCommitTestKit(t, store) + + tk.MustExec("drop table if exists t_parent, t_child") + tk.MustExec("create table t_parent (id int primary key)") + tk.MustExec("create table t_child (id int primary key, pid int, foreign key (pid) references t_parent(id) on delete cascade on update cascade)") + tk.MustExec("insert into t_parent values (1),(2),(3),(4)") + tk.MustExec("insert into t_child values (1,1),(2,2),(3,3)") + + tk.MustExec("set tidb_enable_1pc = true") + tk.MustExec("begin pessimistic") + tk.MustExec("delete from t_parent where id in (1,4)") + tk.MustExec("update t_parent set id=22 where id=2") + tk.MustExec("commit") + tk.MustQuery("select * from t_parent order by id").Check(testkit.Rows("3", "22")) + tk.MustQuery("select * from t_child order by id").Check(testkit.Rows("2 22", "3 3")) +} + func TestChangeLockToPut(t *testing.T) { store := realtikvtest.CreateMockStoreAndSetup(t) From 1cf840a852656c821d619376736e4c8de77ed663 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Wed, 21 Dec 2022 17:40:58 +0800 Subject: [PATCH 13/19] add foreign key and transaction isolation test Signed-off-by: crazycs520 --- .../pessimistictest/pessimistic_test.go | 42 +++++++++++++++++-- tests/realtikvtest/testkit.go | 7 +++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/tests/realtikvtest/pessimistictest/pessimistic_test.go b/tests/realtikvtest/pessimistictest/pessimistic_test.go index ed12510899cc7..a70b31f0a87b8 100644 --- a/tests/realtikvtest/pessimistictest/pessimistic_test.go +++ b/tests/realtikvtest/pessimistictest/pessimistic_test.go @@ -35,6 +35,7 @@ import ( "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/parser/terror" + plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/sessiontxn" @@ -2822,16 +2823,13 @@ func TestAsyncCommitAndForeignKey(t *testing.T) { conf.TiKVClient.AsyncCommit.SafeWindow = time.Second conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 }) - store := realtikvtest.CreateMockStoreAndSetup(t) tk := createAsyncCommitTestKit(t, store) - tk.MustExec("drop table if exists t_parent, t_child") tk.MustExec("create table t_parent (id int primary key)") tk.MustExec("create table t_child (id int primary key, pid int, foreign key (pid) references t_parent(id) on delete cascade on update cascade)") tk.MustExec("insert into t_parent values (1),(2),(3),(4)") tk.MustExec("insert into t_child values (1,1),(2,2),(3,3)") - tk.MustExec("set tidb_enable_1pc = true") tk.MustExec("begin pessimistic") tk.MustExec("delete from t_parent where id in (1,4)") @@ -2841,6 +2839,44 @@ func TestAsyncCommitAndForeignKey(t *testing.T) { tk.MustQuery("select * from t_child order by id").Check(testkit.Rows("2 22", "3 3")) } +func TestTransactionIsolationAndForeignKey(t *testing.T) { + if !*realtikvtest.WithRealTiKV { + t.Skip("The test only support test with tikv.") + } + store := realtikvtest.CreateMockStoreAndSetup(t) + tk := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk2.MustExec("use test") + tk.MustExec("drop table if exists t1,t2") + tk.MustExec("create table t1 (id int primary key)") + tk.MustExec("create table t2 (id int primary key, pid int, foreign key (pid) references t1(id) on delete cascade on update cascade)") + tk.MustExec("insert into t1 values (1)") + tk.MustExec("set tx_isolation = 'READ-COMMITTED'") + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t2 values (1,1)") + tk.MustGetDBError("insert into t2 values (2,2)", plannercore.ErrNoReferencedRow2) + tk2.MustExec("insert into t1 values (2)") + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "2")) + tk.MustExec("insert into t2 values (2,2)") + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustExec("delete from t1 where id=2") + }() + time.Sleep(time.Millisecond * 10) + tk.MustExec("commit") + wg.Wait() + tk.MustQuery("select * from t1").Check(testkit.Rows("1")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1 1")) + tk2.MustExec("delete from t1 where id=1") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + tk.MustExec("admin check table t1") + tk.MustExec("admin check table t2") +} + func TestChangeLockToPut(t *testing.T) { store := realtikvtest.CreateMockStoreAndSetup(t) diff --git a/tests/realtikvtest/testkit.go b/tests/realtikvtest/testkit.go index b3ae5f3c6a2ac..0d63d23f07cd9 100644 --- a/tests/realtikvtest/testkit.go +++ b/tests/realtikvtest/testkit.go @@ -19,6 +19,7 @@ package realtikvtest import ( "flag" "fmt" + "strings" "sync/atomic" "testing" "time" @@ -110,8 +111,12 @@ func CreateMockStoreAndDomainAndSetup(t *testing.T, opts ...mockstore.MockTiKVSt tk.MustExec(fmt.Sprintf("set global innodb_lock_wait_timeout = %d", variable.DefInnodbLockWaitTimeout)) tk.MustExec("use test") rs := tk.MustQuery("show tables") + tables := []string{} for _, row := range rs.Rows() { - tk.MustExec(fmt.Sprintf("drop table %s", row[0])) + tables = append(tables, fmt.Sprintf("%v", row[0])) + } + if len(tables) > 0 { + tk.MustExec(fmt.Sprintf("drop table %s", strings.Join(tables, ","))) } } else { store, err = mockstore.NewMockStore(opts...) From 4ad143b4714c311f06219290116e576c634f0728 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Thu, 22 Dec 2022 16:54:12 +0800 Subject: [PATCH 14/19] add foreign key speed and index(lightning) test Signed-off-by: crazycs520 --- .../addindextest/add_index_test.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/realtikvtest/addindextest/add_index_test.go b/tests/realtikvtest/addindextest/add_index_test.go index 7dd4919570594..87e97595c793d 100644 --- a/tests/realtikvtest/addindextest/add_index_test.go +++ b/tests/realtikvtest/addindextest/add_index_test.go @@ -100,3 +100,28 @@ func TestCreateMultiColsIndex(t *testing.T) { ctx := initTest(t) testTwoColsFrame(ctx, coliIDs, coljIDs, addIndexMultiCols) } + +func TestAddForeignKeyWithAutoCreateIndex(t *testing.T) { + store := realtikvtest.CreateMockStoreAndSetup(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("drop database if exists fk_index;") + tk.MustExec("create database fk_index;") + tk.MustExec("use fk_index;") + tk.MustExec(`set global tidb_ddl_enable_fast_reorg=1;`) + tk.MustExec("create table employee (id bigint auto_increment key, pid bigint)") + tk.MustExec("insert into employee (id) values (1),(2),(3),(4),(5),(6),(7),(8)") + for i := 0; i < 10; i++ { + tk.MustExec("insert into employee (pid) select pid from employee") + } + tk.MustExec("update employee set pid=id-1 where id>1") + tk.MustExec("alter table employee add foreign key fk_1(pid) references employee(id)") + tk.MustExec("alter table employee drop foreign key fk_1") + tk.MustExec("alter table employee drop index fk_1") + tk.MustExec("update employee set pid=0 where id=1") + tk.MustGetErrMsg("alter table employee add foreign key fk_1(pid) references employee(id)", + "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`fk_index`.`employee`, CONSTRAINT `fk_1` FOREIGN KEY (`pid`) REFERENCES `employee` (`id`))") + tk.MustExec("update employee set pid=null where id=1") + tk.MustExec("insert into employee (pid) select pid from employee") + tk.MustExec("update employee set pid=id-1 where id>1 and pid is null") + tk.MustExec("alter table employee add foreign key fk_1(pid) references employee(id)") +} From b8632c6b20819f09db2eafb792435498c9e4adce Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Fri, 23 Dec 2022 14:44:54 +0800 Subject: [PATCH 15/19] refine Signed-off-by: crazycs520 --- ddl/fktest/foreign_key_test.go | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/ddl/fktest/foreign_key_test.go b/ddl/fktest/foreign_key_test.go index fbedfb17b44e3..df461fa048e5c 100644 --- a/ddl/fktest/foreign_key_test.go +++ b/ddl/fktest/foreign_key_test.go @@ -1666,8 +1666,44 @@ func TestAddForeignKeyInBigTable(t *testing.T) { require.Less(t, time.Since(start), time.Minute) } +func TestForeignKeyWithCacheTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + // Test foreign key refer cache table. + tk.MustExec("create table t1 (id int key);") + tk.MustExec("insert into t1 values (1),(2),(3),(4)") + tk.MustExec("alter table t1 cache;") + tk.MustExec("create table t2 (b int);") + tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") + tk.MustExec("insert into t2 values (1),(2),(3),(4)") + tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t1 set id = id+10 where id=1") + tk.MustExec("delete from t1 where id<10") + tk.MustQuery("select * from t1").Check(testkit.Rows("11")) + tk.MustQuery("select * from t2").Check(testkit.Rows("11")) + tk.MustExec("alter table t1 nocache;") + tk.MustExec("drop table t1,t2;") + + // Test add foreign key on cache table. + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (b int);") + tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") + tk.MustExec("alter table t2 cache;") + tk.MustExec("insert into t1 values (1),(2),(3),(4)") + tk.MustExec("insert into t2 values (1),(2),(3),(4)") + tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t1 set id = id+10 where id=1") + tk.MustExec("delete from t1 where id<10") + tk.MustQuery("select * from t1").Check(testkit.Rows("11")) + tk.MustQuery("select * from t2").Check(testkit.Rows("11")) + tk.MustExec("alter table t2 nocache;") + tk.MustExec("drop table t1,t2;") +} + func TestForeignKeyAndConcurrentDDL(t *testing.T) { - store, _ := testkit.CreateMockStoreAndDomain(t) + store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set @@foreign_key_checks=1;") tk.MustExec("use test") From 5717ec7de2cf33ba4bd41ea6ef36f833f3914cca Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Fri, 23 Dec 2022 14:49:35 +0800 Subject: [PATCH 16/19] refine test Signed-off-by: crazycs520 --- tests/realtikvtest/addindextest/add_index_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/realtikvtest/addindextest/add_index_test.go b/tests/realtikvtest/addindextest/add_index_test.go index 87e97595c793d..1c1403f66a922 100644 --- a/tests/realtikvtest/addindextest/add_index_test.go +++ b/tests/realtikvtest/addindextest/add_index_test.go @@ -110,10 +110,11 @@ func TestAddForeignKeyWithAutoCreateIndex(t *testing.T) { tk.MustExec(`set global tidb_ddl_enable_fast_reorg=1;`) tk.MustExec("create table employee (id bigint auto_increment key, pid bigint)") tk.MustExec("insert into employee (id) values (1),(2),(3),(4),(5),(6),(7),(8)") - for i := 0; i < 10; i++ { + for i := 0; i < 14; i++ { tk.MustExec("insert into employee (pid) select pid from employee") } tk.MustExec("update employee set pid=id-1 where id>1") + tk.MustQuery("select count(*) from employee").Check(testkit.Rows("131072")) tk.MustExec("alter table employee add foreign key fk_1(pid) references employee(id)") tk.MustExec("alter table employee drop foreign key fk_1") tk.MustExec("alter table employee drop index fk_1") From 44f027b65d80835e8acfcb45bc6c0801b1dea0a4 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 26 Dec 2022 11:29:20 +0800 Subject: [PATCH 17/19] update bazel Signed-off-by: crazycs520 --- executor/fktest/BUILD.bazel | 1 + tests/realtikvtest/pessimistictest/BUILD.bazel | 1 + 2 files changed, 2 insertions(+) diff --git a/executor/fktest/BUILD.bazel b/executor/fktest/BUILD.bazel index f245bba152c59..2c9f00dfa0624 100644 --- a/executor/fktest/BUILD.bazel +++ b/executor/fktest/BUILD.bazel @@ -15,6 +15,7 @@ go_test( "//infoschema", "//kv", "//meta/autoid", + "//parser", "//parser/ast", "//parser/auth", "//parser/format", diff --git a/tests/realtikvtest/pessimistictest/BUILD.bazel b/tests/realtikvtest/pessimistictest/BUILD.bazel index 97890c8b8b70b..67a01e83cf386 100644 --- a/tests/realtikvtest/pessimistictest/BUILD.bazel +++ b/tests/realtikvtest/pessimistictest/BUILD.bazel @@ -18,6 +18,7 @@ go_test( "//parser/model", "//parser/mysql", "//parser/terror", + "//planner/core", "//session", "//sessionctx/variable", "//sessiontxn", From 63d2ff5891bd3da50203617dc048a9649bb58fd6 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 27 Dec 2022 12:45:10 +0800 Subject: [PATCH 18/19] refine code Signed-off-by: crazycs520 --- executor/fktest/foreign_key_test.go | 84 ++++++++++++++--------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/executor/fktest/foreign_key_test.go b/executor/fktest/foreign_key_test.go index 4bebb6daf7b76..1dc92d6954a2e 100644 --- a/executor/fktest/foreign_key_test.go +++ b/executor/fktest/foreign_key_test.go @@ -2605,6 +2605,48 @@ func TestForeignKeyOnReplaceIntoChildTable(t *testing.T) { } func TestForeignKeyOnReplaceInto(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int, index (a));") + tk.MustExec("create table t2 (id int key, a int, index (a), constraint fk_1 foreign key (a) references t1(a));") + tk.MustExec("replace into t1 values (1, 1);") + tk.MustExec("replace into t2 values (1, 1);") + tk.MustExec("replace into t2 (id) values (2);") + tk.MustGetDBError("replace into t2 values (1, 2);", plannercore.ErrNoReferencedRow2) + // Test fk check on replace into parent table. + tk.MustGetDBError("replace into t1 values (1, 2);", plannercore.ErrRowIsReferenced2) + // Test fk cascade delete on replace into parent table. + tk.MustExec("alter table t2 drop foreign key fk_1") + tk.MustExec("alter table t2 add constraint fk_1 foreign key (a) references t1(a) on delete cascade") + tk.MustExec("replace into t1 values (1, 2);") + tk.MustQuery("select id, a from t1").Check(testkit.Rows("1 2")) + tk.MustQuery("select * from t2").Check(testkit.Rows("2 ")) + // Test fk cascade delete on replace into parent table. + tk.MustExec("alter table t2 drop foreign key fk_1") + tk.MustExec("alter table t2 add constraint fk_1 foreign key (a) references t1(a) on delete set null") + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("replace into t1 values (1, 1);") + tk.MustExec("replace into t2 values (1, 1);") + tk.MustExec("replace into t1 values (1, 2);") + tk.MustQuery("select id, a from t1").Check(testkit.Rows("1 2")) + tk.MustQuery("select id, a from t2").Check(testkit.Rows("1 ")) + + // Test cascade delete in self table by replace into statement. + tk.MustExec("drop table t1,t2") + tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") + tk.MustExec("replace into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("replace into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") + tk.MustExec("replace into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") + tk.MustExec("replace into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") + tk.MustExec("replace into t1 values (1000,'l3_a1', 100)") + tk.MustExec("replace into t1 values (1, 'new-boss', null)") + tk.MustQuery("select id from t1 order by id").Check(testkit.Rows("1")) +} + +func TestForeignKeyLargeTxnErr(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set @@foreign_key_checks=1") @@ -2691,45 +2733,3 @@ func TestForeignKeyAndMemoryTracker(t *testing.T) { tk.MustExec("update t1 set id=id+100000 where id=1") tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) } - -func TestForeignKeyLargeTxnErr(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int, index (a));") - tk.MustExec("create table t2 (id int key, a int, index (a), constraint fk_1 foreign key (a) references t1(a));") - tk.MustExec("replace into t1 values (1, 1);") - tk.MustExec("replace into t2 values (1, 1);") - tk.MustExec("replace into t2 (id) values (2);") - tk.MustGetDBError("replace into t2 values (1, 2);", plannercore.ErrNoReferencedRow2) - // Test fk check on replace into parent table. - tk.MustGetDBError("replace into t1 values (1, 2);", plannercore.ErrRowIsReferenced2) - // Test fk cascade delete on replace into parent table. - tk.MustExec("alter table t2 drop foreign key fk_1") - tk.MustExec("alter table t2 add constraint fk_1 foreign key (a) references t1(a) on delete cascade") - tk.MustExec("replace into t1 values (1, 2);") - tk.MustQuery("select id, a from t1").Check(testkit.Rows("1 2")) - tk.MustQuery("select * from t2").Check(testkit.Rows("2 ")) - // Test fk cascade delete on replace into parent table. - tk.MustExec("alter table t2 drop foreign key fk_1") - tk.MustExec("alter table t2 add constraint fk_1 foreign key (a) references t1(a) on delete set null") - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("replace into t1 values (1, 1);") - tk.MustExec("replace into t2 values (1, 1);") - tk.MustExec("replace into t1 values (1, 2);") - tk.MustQuery("select id, a from t1").Check(testkit.Rows("1 2")) - tk.MustQuery("select id, a from t2").Check(testkit.Rows("1 ")) - - // Test cascade delete in self table by replace into statement. - tk.MustExec("drop table t1,t2") - tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") - tk.MustExec("replace into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("replace into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") - tk.MustExec("replace into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") - tk.MustExec("replace into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") - tk.MustExec("replace into t1 values (1000,'l3_a1', 100)") - tk.MustExec("replace into t1 values (1, 'new-boss', null)") - tk.MustQuery("select id from t1 order by id").Check(testkit.Rows("1")) -} From e8c211a97375b93385f735b80de8b80499b50832 Mon Sep 17 00:00:00 2001 From: crazycs Date: Tue, 27 Dec 2022 17:13:37 +0800 Subject: [PATCH 19/19] Update tests/realtikvtest/testkit.go Co-authored-by: tangenta --- tests/realtikvtest/testkit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/realtikvtest/testkit.go b/tests/realtikvtest/testkit.go index 0d63d23f07cd9..4b8a749e65c9d 100644 --- a/tests/realtikvtest/testkit.go +++ b/tests/realtikvtest/testkit.go @@ -113,7 +113,7 @@ func CreateMockStoreAndDomainAndSetup(t *testing.T, opts ...mockstore.MockTiKVSt rs := tk.MustQuery("show tables") tables := []string{} for _, row := range rs.Rows() { - tables = append(tables, fmt.Sprintf("%v", row[0])) + tables = append(tables, fmt.Sprintf("`%v`", row[0])) } if len(tables) > 0 { tk.MustExec(fmt.Sprintf("drop table %s", strings.Join(tables, ",")))