github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/foreign_key_test.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package dbs 15 16 import ( 17 "context" 18 "strings" 19 "sync" 20 21 "github.com/whtcorpsinc/BerolinaSQL/ast" 22 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 23 . "github.com/whtcorpsinc/check" 24 "github.com/whtcorpsinc/errors" 25 "github.com/whtcorpsinc/milevadb/causet" 26 "github.com/whtcorpsinc/milevadb/ekv" 27 "github.com/whtcorpsinc/milevadb/stochastikctx" 28 ) 29 30 var _ = Suite(&testForeignKeySuite{}) 31 32 type testForeignKeySuite struct { 33 causetstore ekv.CausetStorage 34 dbInfo *perceptron.DBInfo 35 d *dbs 36 ctx stochastikctx.Context 37 } 38 39 func (s *testForeignKeySuite) SetUpSuite(c *C) { 40 s.causetstore = testCreateStore(c, "test_foreign") 41 } 42 43 func (s *testForeignKeySuite) TearDownSuite(c *C) { 44 err := s.causetstore.Close() 45 c.Assert(err, IsNil) 46 } 47 48 func (s *testForeignKeySuite) testCreateForeignKey(c *C, tblInfo *perceptron.BlockInfo, fkName string, keys []string, refBlock string, refKeys []string, onDelete ast.ReferOptionType, onUFIDelate ast.ReferOptionType) *perceptron.Job { 49 FKName := perceptron.NewCIStr(fkName) 50 Keys := make([]perceptron.CIStr, len(keys)) 51 for i, key := range keys { 52 Keys[i] = perceptron.NewCIStr(key) 53 } 54 55 RefBlock := perceptron.NewCIStr(refBlock) 56 RefKeys := make([]perceptron.CIStr, len(refKeys)) 57 for i, key := range refKeys { 58 RefKeys[i] = perceptron.NewCIStr(key) 59 } 60 61 fkInfo := &perceptron.FKInfo{ 62 Name: FKName, 63 RefBlock: RefBlock, 64 RefDefCauss: RefKeys, 65 DefCauss: Keys, 66 OnDelete: int(onDelete), 67 OnUFIDelate: int(onUFIDelate), 68 State: perceptron.StateNone, 69 } 70 71 job := &perceptron.Job{ 72 SchemaID: s.dbInfo.ID, 73 BlockID: tblInfo.ID, 74 Type: perceptron.CausetActionAddForeignKey, 75 BinlogInfo: &perceptron.HistoryInfo{}, 76 Args: []interface{}{fkInfo}, 77 } 78 err := s.ctx.NewTxn(context.Background()) 79 c.Assert(err, IsNil) 80 err = s.d.doDBSJob(s.ctx, job) 81 c.Assert(err, IsNil) 82 return job 83 } 84 85 func testDropForeignKey(c *C, ctx stochastikctx.Context, d *dbs, dbInfo *perceptron.DBInfo, tblInfo *perceptron.BlockInfo, foreignKeyName string) *perceptron.Job { 86 job := &perceptron.Job{ 87 SchemaID: dbInfo.ID, 88 BlockID: tblInfo.ID, 89 Type: perceptron.CausetActionDropForeignKey, 90 BinlogInfo: &perceptron.HistoryInfo{}, 91 Args: []interface{}{perceptron.NewCIStr(foreignKeyName)}, 92 } 93 err := d.doDBSJob(ctx, job) 94 c.Assert(err, IsNil) 95 v := getSchemaVer(c, ctx) 96 checkHistoryJobArgs(c, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) 97 return job 98 } 99 100 func getForeignKey(t causet.Block, name string) *perceptron.FKInfo { 101 for _, fk := range t.Meta().ForeignKeys { 102 // only public foreign key can be read. 103 if fk.State != perceptron.StatePublic { 104 continue 105 } 106 if fk.Name.L == strings.ToLower(name) { 107 return fk 108 } 109 } 110 return nil 111 } 112 113 func (s *testForeignKeySuite) TestForeignKey(c *C) { 114 d := testNewDBSAndStart( 115 context.Background(), 116 c, 117 WithStore(s.causetstore), 118 WithLease(testLease), 119 ) 120 defer d.Stop() 121 s.d = d 122 s.dbInfo = testSchemaInfo(c, d, "test_foreign") 123 ctx := testNewContext(d) 124 s.ctx = ctx 125 testCreateSchema(c, ctx, d, s.dbInfo) 126 tblInfo := testBlockInfo(c, d, "t", 3) 127 128 err := ctx.NewTxn(context.Background()) 129 c.Assert(err, IsNil) 130 131 testCreateBlock(c, ctx, d, s.dbInfo, tblInfo) 132 133 txn, err := ctx.Txn(true) 134 c.Assert(err, IsNil) 135 err = txn.Commit(context.Background()) 136 c.Assert(err, IsNil) 137 138 // fix data race 139 var mu sync.Mutex 140 checkOK := false 141 var hookErr error 142 tc := &TestDBSCallback{} 143 tc.onJobUFIDelated = func(job *perceptron.Job) { 144 if job.State != perceptron.JobStateDone { 145 return 146 } 147 mu.Lock() 148 defer mu.Unlock() 149 var t causet.Block 150 t, err = testGetBlockWithError(d, s.dbInfo.ID, tblInfo.ID) 151 if err != nil { 152 hookErr = errors.Trace(err) 153 return 154 } 155 fk := getForeignKey(t, "c1_fk") 156 if fk == nil { 157 hookErr = errors.New("foreign key not exists") 158 return 159 } 160 checkOK = true 161 } 162 originalHook := d.GetHook() 163 defer d.SetHook(originalHook) 164 d.SetHook(tc) 165 166 job := s.testCreateForeignKey(c, tblInfo, "c1_fk", []string{"c1"}, "t2", []string{"c1"}, ast.ReferOptionCascade, ast.ReferOptionSetNull) 167 testCheckJobDone(c, d, job, true) 168 txn, err = ctx.Txn(true) 169 c.Assert(err, IsNil) 170 err = txn.Commit(context.Background()) 171 c.Assert(err, IsNil) 172 mu.Lock() 173 hErr := hookErr 174 ok := checkOK 175 mu.Unlock() 176 c.Assert(hErr, IsNil) 177 c.Assert(ok, IsTrue) 178 v := getSchemaVer(c, ctx) 179 checkHistoryJobArgs(c, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) 180 181 mu.Lock() 182 checkOK = false 183 mu.Unlock() 184 // fix data race pr/#9491 185 tc2 := &TestDBSCallback{} 186 tc2.onJobUFIDelated = func(job *perceptron.Job) { 187 if job.State != perceptron.JobStateDone { 188 return 189 } 190 mu.Lock() 191 defer mu.Unlock() 192 var t causet.Block 193 t, err = testGetBlockWithError(d, s.dbInfo.ID, tblInfo.ID) 194 if err != nil { 195 hookErr = errors.Trace(err) 196 return 197 } 198 fk := getForeignKey(t, "c1_fk") 199 if fk != nil { 200 hookErr = errors.New("foreign key has not been dropped") 201 return 202 } 203 checkOK = true 204 } 205 d.SetHook(tc2) 206 207 job = testDropForeignKey(c, ctx, d, s.dbInfo, tblInfo, "c1_fk") 208 testCheckJobDone(c, d, job, false) 209 mu.Lock() 210 hErr = hookErr 211 ok = checkOK 212 mu.Unlock() 213 c.Assert(hErr, IsNil) 214 c.Assert(ok, IsTrue) 215 216 err = ctx.NewTxn(context.Background()) 217 c.Assert(err, IsNil) 218 219 job = testDropBlock(c, ctx, d, s.dbInfo, tblInfo) 220 testCheckJobDone(c, d, job, false) 221 222 txn, err = ctx.Txn(true) 223 c.Assert(err, IsNil) 224 err = txn.Commit(context.Background()) 225 c.Assert(err, IsNil) 226 }