github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/dbs/index_change_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  	"time"
    19  
    20  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    21  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    22  	. "github.com/whtcorpsinc/check"
    23  	"github.com/whtcorpsinc/errors"
    24  	"github.com/whtcorpsinc/milevadb/causet"
    25  	"github.com/whtcorpsinc/milevadb/ekv"
    26  	"github.com/whtcorpsinc/milevadb/spacetime"
    27  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    28  	"github.com/whtcorpsinc/milevadb/types"
    29  )
    30  
    31  var _ = Suite(&testIndexChangeSuite{})
    32  
    33  type testIndexChangeSuite struct {
    34  	causetstore ekv.CausetStorage
    35  	dbInfo      *perceptron.DBInfo
    36  }
    37  
    38  func (s *testIndexChangeSuite) SetUpSuite(c *C) {
    39  	s.causetstore = testCreateStore(c, "test_index_change")
    40  	s.dbInfo = &perceptron.DBInfo{
    41  		Name: perceptron.NewCIStr("test_index_change"),
    42  		ID:   1,
    43  	}
    44  	err := ekv.RunInNewTxn(s.causetstore, true, func(txn ekv.Transaction) error {
    45  		t := spacetime.NewMeta(txn)
    46  		return errors.Trace(t.CreateDatabase(s.dbInfo))
    47  	})
    48  	c.Check(err, IsNil, Commentf("err %v", errors.ErrorStack(err)))
    49  }
    50  
    51  func (s *testIndexChangeSuite) TearDownSuite(c *C) {
    52  	s.causetstore.Close()
    53  }
    54  
    55  func (s *testIndexChangeSuite) TestIndexChange(c *C) {
    56  	d := testNewDBSAndStart(
    57  		context.Background(),
    58  		c,
    59  		WithStore(s.causetstore),
    60  		WithLease(testLease),
    61  	)
    62  	defer d.Stop()
    63  	// create causet t (c1 int primary key, c2 int);
    64  	tblInfo := testBlockInfo(c, d, "t", 2)
    65  	tblInfo.DeferredCausets[0].Flag = allegrosql.PriKeyFlag | allegrosql.NotNullFlag
    66  	tblInfo.PKIsHandle = true
    67  	ctx := testNewContext(d)
    68  	err := ctx.NewTxn(context.Background())
    69  	c.Assert(err, IsNil)
    70  	testCreateBlock(c, ctx, d, s.dbInfo, tblInfo)
    71  	originBlock := testGetBlock(c, d, s.dbInfo.ID, tblInfo.ID)
    72  
    73  	// insert t values (1, 1), (2, 2), (3, 3)
    74  	_, err = originBlock.AddRecord(ctx, types.MakeCausets(1, 1))
    75  	c.Assert(err, IsNil)
    76  	_, err = originBlock.AddRecord(ctx, types.MakeCausets(2, 2))
    77  	c.Assert(err, IsNil)
    78  	_, err = originBlock.AddRecord(ctx, types.MakeCausets(3, 3))
    79  	c.Assert(err, IsNil)
    80  
    81  	txn, err := ctx.Txn(true)
    82  	c.Assert(err, IsNil)
    83  	err = txn.Commit(context.Background())
    84  	c.Assert(err, IsNil)
    85  
    86  	tc := &TestDBSCallback{}
    87  	// set up hook
    88  	prevState := perceptron.StateNone
    89  	addIndexDone := false
    90  	var (
    91  		deleteOnlyBlock causet.Block
    92  		writeOnlyBlock  causet.Block
    93  		publicBlock     causet.Block
    94  		checkErr        error
    95  	)
    96  	tc.onJobUFIDelated = func(job *perceptron.Job) {
    97  		if job.SchemaState == prevState {
    98  			return
    99  		}
   100  		ctx1 := testNewContext(d)
   101  		prevState = job.SchemaState
   102  		switch job.SchemaState {
   103  		case perceptron.StateDeleteOnly:
   104  			deleteOnlyBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID)
   105  			if err != nil {
   106  				checkErr = errors.Trace(err)
   107  			}
   108  		case perceptron.StateWriteOnly:
   109  			writeOnlyBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID)
   110  			if err != nil {
   111  				checkErr = errors.Trace(err)
   112  			}
   113  			err = s.checkAddWriteOnly(d, ctx1, deleteOnlyBlock, writeOnlyBlock)
   114  			if err != nil {
   115  				checkErr = errors.Trace(err)
   116  			}
   117  		case perceptron.StatePublic:
   118  			if job.GetRowCount() != 3 {
   119  				checkErr = errors.Errorf("job's event count %d != 3", job.GetRowCount())
   120  			}
   121  			publicBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID)
   122  			if err != nil {
   123  				checkErr = errors.Trace(err)
   124  			}
   125  			err = s.checkAddPublic(d, ctx1, writeOnlyBlock, publicBlock)
   126  			if err != nil {
   127  				checkErr = errors.Trace(err)
   128  			}
   129  			if job.State == perceptron.JobStateSynced {
   130  				addIndexDone = true
   131  			}
   132  		}
   133  	}
   134  	d.SetHook(tc)
   135  	testCreateIndex(c, ctx, d, s.dbInfo, originBlock.Meta(), false, "c2", "c2")
   136  	// We need to make sure onJobUFIDelated is called in the first hook.
   137  	// After testCreateIndex(), onJobUFIDelated() may not be called when job.state is Sync.
   138  	// If we skip this check, prevState may wrongly set to StatePublic.
   139  	for i := 0; i <= 10; i++ {
   140  		if addIndexDone {
   141  			break
   142  		}
   143  		time.Sleep(50 * time.Millisecond)
   144  	}
   145  	c.Check(errors.ErrorStack(checkErr), Equals, "")
   146  	txn, err = ctx.Txn(true)
   147  	c.Assert(err, IsNil)
   148  	c.Assert(txn.Commit(context.Background()), IsNil)
   149  	prevState = perceptron.StateNone
   150  	var noneBlock causet.Block
   151  	tc.onJobUFIDelated = func(job *perceptron.Job) {
   152  		if job.SchemaState == prevState {
   153  			return
   154  		}
   155  		prevState = job.SchemaState
   156  		var err error
   157  		ctx1 := testNewContext(d)
   158  		switch job.SchemaState {
   159  		case perceptron.StateWriteOnly:
   160  			writeOnlyBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID)
   161  			if err != nil {
   162  				checkErr = errors.Trace(err)
   163  			}
   164  			err = s.checkDropWriteOnly(d, ctx1, publicBlock, writeOnlyBlock)
   165  			if err != nil {
   166  				checkErr = errors.Trace(err)
   167  			}
   168  		case perceptron.StateDeleteOnly:
   169  			deleteOnlyBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID)
   170  			if err != nil {
   171  				checkErr = errors.Trace(err)
   172  			}
   173  			err = s.checkDroFIDeleleteOnly(d, ctx1, writeOnlyBlock, deleteOnlyBlock)
   174  			if err != nil {
   175  				checkErr = errors.Trace(err)
   176  			}
   177  		case perceptron.StateNone:
   178  			noneBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID)
   179  			if err != nil {
   180  				checkErr = errors.Trace(err)
   181  			}
   182  			if len(noneBlock.Indices()) != 0 {
   183  				checkErr = errors.New("index should have been dropped")
   184  			}
   185  		}
   186  	}
   187  	testDropIndex(c, ctx, d, s.dbInfo, publicBlock.Meta(), "c2")
   188  	c.Check(errors.ErrorStack(checkErr), Equals, "")
   189  }
   190  
   191  func checHoTTexExists(ctx stochastikctx.Context, tbl causet.Block, indexValue interface{}, handle int64, exists bool) error {
   192  	idx := tbl.Indices()[0]
   193  	txn, err := ctx.Txn(true)
   194  	if err != nil {
   195  		return errors.Trace(err)
   196  	}
   197  	doesExist, _, err := idx.Exist(ctx.GetStochastikVars().StmtCtx, txn.GetUnionStore(), types.MakeCausets(indexValue), ekv.IntHandle(handle))
   198  	if err != nil {
   199  		return errors.Trace(err)
   200  	}
   201  	if exists != doesExist {
   202  		if exists {
   203  			return errors.New("index should exists")
   204  		}
   205  		return errors.New("index should not exists")
   206  	}
   207  	return nil
   208  }
   209  
   210  func (s *testIndexChangeSuite) checkAddWriteOnly(d *dbs, ctx stochastikctx.Context, delOnlyTbl, writeOnlyTbl causet.Block) error {
   211  	// DeleteOnlyBlock: insert t values (4, 4);
   212  	err := ctx.NewTxn(context.Background())
   213  	if err != nil {
   214  		return errors.Trace(err)
   215  	}
   216  	_, err = delOnlyTbl.AddRecord(ctx, types.MakeCausets(4, 4))
   217  	if err != nil {
   218  		return errors.Trace(err)
   219  	}
   220  	err = checHoTTexExists(ctx, writeOnlyTbl, 4, 4, false)
   221  	if err != nil {
   222  		return errors.Trace(err)
   223  	}
   224  
   225  	// WriteOnlyBlock: insert t values (5, 5);
   226  	_, err = writeOnlyTbl.AddRecord(ctx, types.MakeCausets(5, 5))
   227  	if err != nil {
   228  		return errors.Trace(err)
   229  	}
   230  	err = checHoTTexExists(ctx, writeOnlyTbl, 5, 5, true)
   231  	if err != nil {
   232  		return errors.Trace(err)
   233  	}
   234  
   235  	// WriteOnlyBlock: uFIDelate t set c2 = 1 where c1 = 4 and c2 = 4
   236  	err = writeOnlyTbl.UFIDelateRecord(context.Background(), ctx, ekv.IntHandle(4), types.MakeCausets(4, 4), types.MakeCausets(4, 1), touchedSlice(writeOnlyTbl))
   237  	if err != nil {
   238  		return errors.Trace(err)
   239  	}
   240  	err = checHoTTexExists(ctx, writeOnlyTbl, 1, 4, true)
   241  	if err != nil {
   242  		return errors.Trace(err)
   243  	}
   244  
   245  	// DeleteOnlyBlock: uFIDelate t set c2 = 3 where c1 = 4 and c2 = 1
   246  	err = delOnlyTbl.UFIDelateRecord(context.Background(), ctx, ekv.IntHandle(4), types.MakeCausets(4, 1), types.MakeCausets(4, 3), touchedSlice(writeOnlyTbl))
   247  	if err != nil {
   248  		return errors.Trace(err)
   249  	}
   250  	// old value index not exists.
   251  	err = checHoTTexExists(ctx, writeOnlyTbl, 1, 4, false)
   252  	if err != nil {
   253  		return errors.Trace(err)
   254  	}
   255  	// new value index not exists.
   256  	err = checHoTTexExists(ctx, writeOnlyTbl, 3, 4, false)
   257  	if err != nil {
   258  		return errors.Trace(err)
   259  	}
   260  
   261  	// WriteOnlyBlock: delete t where c1 = 4 and c2 = 3
   262  	err = writeOnlyTbl.RemoveRecord(ctx, ekv.IntHandle(4), types.MakeCausets(4, 3))
   263  	if err != nil {
   264  		return errors.Trace(err)
   265  	}
   266  	err = checHoTTexExists(ctx, writeOnlyTbl, 3, 4, false)
   267  	if err != nil {
   268  		return errors.Trace(err)
   269  	}
   270  
   271  	// DeleteOnlyBlock: delete t where c1 = 5
   272  	err = delOnlyTbl.RemoveRecord(ctx, ekv.IntHandle(5), types.MakeCausets(5, 5))
   273  	if err != nil {
   274  		return errors.Trace(err)
   275  	}
   276  	err = checHoTTexExists(ctx, writeOnlyTbl, 5, 5, false)
   277  	if err != nil {
   278  		return errors.Trace(err)
   279  	}
   280  	return nil
   281  }
   282  
   283  func (s *testIndexChangeSuite) checkAddPublic(d *dbs, ctx stochastikctx.Context, writeTbl, publicTbl causet.Block) error {
   284  	// WriteOnlyBlock: insert t values (6, 6)
   285  	err := ctx.NewTxn(context.Background())
   286  	if err != nil {
   287  		return errors.Trace(err)
   288  	}
   289  	_, err = writeTbl.AddRecord(ctx, types.MakeCausets(6, 6))
   290  	if err != nil {
   291  		return errors.Trace(err)
   292  	}
   293  	err = checHoTTexExists(ctx, publicTbl, 6, 6, true)
   294  	if err != nil {
   295  		return errors.Trace(err)
   296  	}
   297  	// PublicBlock: insert t values (7, 7)
   298  	_, err = publicTbl.AddRecord(ctx, types.MakeCausets(7, 7))
   299  	if err != nil {
   300  		return errors.Trace(err)
   301  	}
   302  	err = checHoTTexExists(ctx, publicTbl, 7, 7, true)
   303  	if err != nil {
   304  		return errors.Trace(err)
   305  	}
   306  
   307  	// WriteOnlyBlock: uFIDelate t set c2 = 5 where c1 = 7 and c2 = 7
   308  	err = writeTbl.UFIDelateRecord(context.Background(), ctx, ekv.IntHandle(7), types.MakeCausets(7, 7), types.MakeCausets(7, 5), touchedSlice(writeTbl))
   309  	if err != nil {
   310  		return errors.Trace(err)
   311  	}
   312  	err = checHoTTexExists(ctx, publicTbl, 5, 7, true)
   313  	if err != nil {
   314  		return errors.Trace(err)
   315  	}
   316  	err = checHoTTexExists(ctx, publicTbl, 7, 7, false)
   317  	if err != nil {
   318  		return errors.Trace(err)
   319  	}
   320  	// WriteOnlyBlock: delete t where c1 = 6
   321  	err = writeTbl.RemoveRecord(ctx, ekv.IntHandle(6), types.MakeCausets(6, 6))
   322  	if err != nil {
   323  		return errors.Trace(err)
   324  	}
   325  	err = checHoTTexExists(ctx, publicTbl, 6, 6, false)
   326  	if err != nil {
   327  		return errors.Trace(err)
   328  	}
   329  
   330  	var rows [][]types.Causet
   331  	publicTbl.IterRecords(ctx, publicTbl.FirstKey(), publicTbl.DefCauss(),
   332  		func(_ ekv.Handle, data []types.Causet, defcaus []*causet.DeferredCauset) (bool, error) {
   333  			rows = append(rows, data)
   334  			return true, nil
   335  		})
   336  	if len(rows) == 0 {
   337  		return errors.New("causet is empty")
   338  	}
   339  	for _, event := range rows {
   340  		idxVal := event[1].GetInt64()
   341  		handle := event[0].GetInt64()
   342  		err = checHoTTexExists(ctx, publicTbl, idxVal, handle, true)
   343  		if err != nil {
   344  			return errors.Trace(err)
   345  		}
   346  	}
   347  	txn, err := ctx.Txn(true)
   348  	if err != nil {
   349  		return errors.Trace(err)
   350  	}
   351  	return txn.Commit(context.Background())
   352  }
   353  
   354  func (s *testIndexChangeSuite) checkDropWriteOnly(d *dbs, ctx stochastikctx.Context, publicTbl, writeTbl causet.Block) error {
   355  	// WriteOnlyBlock insert t values (8, 8)
   356  	err := ctx.NewTxn(context.Background())
   357  	if err != nil {
   358  		return errors.Trace(err)
   359  	}
   360  	_, err = writeTbl.AddRecord(ctx, types.MakeCausets(8, 8))
   361  	if err != nil {
   362  		return errors.Trace(err)
   363  	}
   364  
   365  	err = checHoTTexExists(ctx, publicTbl, 8, 8, true)
   366  	if err != nil {
   367  		return errors.Trace(err)
   368  	}
   369  
   370  	// WriteOnlyBlock uFIDelate t set c2 = 7 where c1 = 8 and c2 = 8
   371  	err = writeTbl.UFIDelateRecord(context.Background(), ctx, ekv.IntHandle(8), types.MakeCausets(8, 8), types.MakeCausets(8, 7), touchedSlice(writeTbl))
   372  	if err != nil {
   373  		return errors.Trace(err)
   374  	}
   375  
   376  	err = checHoTTexExists(ctx, publicTbl, 7, 8, true)
   377  	if err != nil {
   378  		return errors.Trace(err)
   379  	}
   380  
   381  	// WriteOnlyBlock delete t where c1 = 8
   382  	err = writeTbl.RemoveRecord(ctx, ekv.IntHandle(8), types.MakeCausets(8, 7))
   383  	if err != nil {
   384  		return errors.Trace(err)
   385  	}
   386  
   387  	err = checHoTTexExists(ctx, publicTbl, 7, 8, false)
   388  	if err != nil {
   389  		return errors.Trace(err)
   390  	}
   391  	txn, err := ctx.Txn(true)
   392  	if err != nil {
   393  		return errors.Trace(err)
   394  	}
   395  	return txn.Commit(context.Background())
   396  }
   397  
   398  func (s *testIndexChangeSuite) checkDroFIDeleleteOnly(d *dbs, ctx stochastikctx.Context, writeTbl, delTbl causet.Block) error {
   399  	// WriteOnlyBlock insert t values (9, 9)
   400  	err := ctx.NewTxn(context.Background())
   401  	if err != nil {
   402  		return errors.Trace(err)
   403  	}
   404  	_, err = writeTbl.AddRecord(ctx, types.MakeCausets(9, 9))
   405  	if err != nil {
   406  		return errors.Trace(err)
   407  	}
   408  
   409  	err = checHoTTexExists(ctx, writeTbl, 9, 9, true)
   410  	if err != nil {
   411  		return errors.Trace(err)
   412  	}
   413  
   414  	// DeleteOnlyBlock insert t values (10, 10)
   415  	_, err = delTbl.AddRecord(ctx, types.MakeCausets(10, 10))
   416  	if err != nil {
   417  		return errors.Trace(err)
   418  	}
   419  
   420  	err = checHoTTexExists(ctx, writeTbl, 10, 10, false)
   421  	if err != nil {
   422  		return errors.Trace(err)
   423  	}
   424  
   425  	// DeleteOnlyBlock uFIDelate t set c2 = 10 where c1 = 9
   426  	err = delTbl.UFIDelateRecord(context.Background(), ctx, ekv.IntHandle(9), types.MakeCausets(9, 9), types.MakeCausets(9, 10), touchedSlice(delTbl))
   427  	if err != nil {
   428  		return errors.Trace(err)
   429  	}
   430  
   431  	err = checHoTTexExists(ctx, writeTbl, 9, 9, false)
   432  	if err != nil {
   433  		return errors.Trace(err)
   434  	}
   435  
   436  	err = checHoTTexExists(ctx, writeTbl, 10, 9, false)
   437  	if err != nil {
   438  		return errors.Trace(err)
   439  	}
   440  	txn, err := ctx.Txn(true)
   441  	if err != nil {
   442  		return errors.Trace(err)
   443  	}
   444  	return txn.Commit(context.Background())
   445  }