Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ddl: add more foreign key test case #40052

Merged
merged 25 commits into from
Dec 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d607801
fix alter add foreign key privilege check and txn too large check
crazycs520 Dec 19, 2022
57d8dad
fix bug and add test for temporary table and cache table
crazycs520 Dec 19, 2022
9d13a89
add test for fk with partition table
crazycs520 Dec 19, 2022
0cdafad
refine
crazycs520 Dec 20, 2022
07d1a0a
fix ci
crazycs520 Dec 20, 2022
e3cfe89
fix ci
crazycs520 Dec 20, 2022
2bc21b2
add foreign key and lock view test
crazycs520 Dec 20, 2022
a07f3b1
add foreign key and memory tracker test
crazycs520 Dec 20, 2022
5ffd336
add conccurent ddl test
crazycs520 Dec 20, 2022
c3047de
add more test case in TestForeignKeyAndConcurrentDDL
crazycs520 Dec 21, 2022
a5efc3d
add foreign key and mdl test
crazycs520 Dec 21, 2022
d1ec0c8
add foreign key and async commit test
crazycs520 Dec 21, 2022
1cf840a
add foreign key and transaction isolation test
crazycs520 Dec 21, 2022
4ad143b
add foreign key speed and index(lightning) test
crazycs520 Dec 22, 2022
3e0576c
Merge branch 'master' of https://github.com/pingcap/tidb into fk-test-8
crazycs520 Dec 23, 2022
b8632c6
refine
crazycs520 Dec 23, 2022
5717ec7
refine test
crazycs520 Dec 23, 2022
ebff4ad
Merge branch 'master' into fk-test-8
crazycs520 Dec 26, 2022
44f027b
update bazel
crazycs520 Dec 26, 2022
c2ead97
Merge branch 'master' into fk-test-8
crazycs520 Dec 26, 2022
c09bf55
Merge branch 'master' into fk-test-8
crazycs520 Dec 27, 2022
63d2ff5
refine code
crazycs520 Dec 27, 2022
e8c211a
Update tests/realtikvtest/testkit.go
crazycs520 Dec 27, 2022
e796250
Merge branch 'master' into fk-test-8
crazycs520 Dec 27, 2022
2e73029
Merge branch 'master' into fk-test-8
crazycs520 Dec 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions ddl/fktest/foreign_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"context"
"fmt"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -1700,3 +1701,112 @@ func TestForeignKeyWithCacheTable(t *testing.T) {
tk.MustExec("alter table t2 nocache;")
tk.MustExec("drop table t1,t2;")
}

func TestForeignKeyAndConcurrentDDL(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 (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 {
prepare []string
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",
},
{
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
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",
},
{
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));")
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())
}
}
}
41 changes: 41 additions & 0 deletions ddl/metadatalocktest/mdl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions executor/fktest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ go_test(
"//infoschema",
"//kv",
"//meta/autoid",
"//parser",
"//parser/ast",
"//parser/auth",
"//parser/format",
Expand Down
90 changes: 90 additions & 0 deletions executor/fktest/foreign_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"testing"
"time"

"github.com/pingcap/tidb/config"
"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"
Expand Down Expand Up @@ -2643,3 +2645,91 @@ func TestForeignKeyOnReplaceInto(t *testing.T) {
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")
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 <nil>", "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 <nil>"))
}

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()
}

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.
err := tk.ExecToErr("update t1 set id=id+100000 where id=1")
require.Error(t, err)
require.Contains(t, err.Error(), "Out Of Memory Quota!")
tk.MustQuery("select id,pid from t1 where id = 1").Check(testkit.Rows("1 <nil>"))
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 <nil>"))
}
26 changes: 26 additions & 0 deletions tests/realtikvtest/addindextest/add_index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,29 @@ 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 < 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")
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)")
}
1 change: 1 addition & 0 deletions tests/realtikvtest/pessimistictest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ go_test(
"//parser/model",
"//parser/mysql",
"//parser/terror",
"//planner/core",
"//session",
"//sessionctx/variable",
"//sessiontxn",
Expand Down
61 changes: 61 additions & 0 deletions tests/realtikvtest/pessimistictest/pessimistic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -2816,6 +2817,66 @@ 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 TestTransactionIsolationAndForeignKey(t *testing.T) {
if !*realtikvtest.WithRealTiKV {
t.Skip("The test only support test with tikv.")
}
Comment on lines +2843 to +2845
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this because all the tests in this package will be run with real TiKV.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the local environment, we can still run those tests with unistore. To avoid later being said that this test fails on unistore, I prefer keep this.

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)

Expand Down
Loading