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  }