github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/stochastik/schema_amender_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 stochastik
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"sort"
    20  	"strconv"
    21  
    22  	"github.com/whtcorpsinc/BerolinaSQL"
    23  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    24  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    25  	. "github.com/whtcorpsinc/check"
    26  	"github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb"
    27  	"github.com/whtcorpsinc/milevadb/blockcodec"
    28  	"github.com/whtcorpsinc/milevadb/causet"
    29  	"github.com/whtcorpsinc/milevadb/causet/embedded"
    30  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb"
    31  	"github.com/whtcorpsinc/milevadb/ekv"
    32  	"github.com/whtcorpsinc/milevadb/soliton/rowcodec"
    33  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    34  	"github.com/whtcorpsinc/milevadb/types"
    35  )
    36  
    37  var _ = SerialSuites(&testSchemaAmenderSuite{})
    38  
    39  type testSchemaAmenderSuite struct {
    40  }
    41  
    42  func (s *testSchemaAmenderSuite) SetUpSuite(c *C) {
    43  }
    44  
    45  func (s *testSchemaAmenderSuite) TearDownSuite(c *C) {
    46  }
    47  
    48  func initTblDefCausIdxID(spacetimeInfo *perceptron.TableInfo) {
    49  	for i, col := range spacetimeInfo.DeferredCausets {
    50  		col.ID = int64(i + 1)
    51  	}
    52  	for i, idx := range spacetimeInfo.Indices {
    53  		idx.ID = int64(i + 1)
    54  		// TODO unique index is not supported now.
    55  		idx.Unique = false
    56  	}
    57  	spacetimeInfo.ID = 1
    58  	spacetimeInfo.State = perceptron.StatePublic
    59  }
    60  
    61  func mutationsEqual(res *einsteindb.CommitterMutations, expected *einsteindb.CommitterMutations, c *C) {
    62  	c.Assert(len(res.GetKeys()), Equals, len(expected.GetKeys()))
    63  	for i := 0; i < len(res.GetKeys()); i++ {
    64  		foundIdx := -1
    65  		for j := 0; j < len(expected.GetKeys()); j++ {
    66  			if bytes.Equal(res.GetKeys()[i], expected.GetKeys()[j]) {
    67  				foundIdx = j
    68  				break
    69  			}
    70  		}
    71  		c.Assert(foundIdx, GreaterEqual, 0)
    72  		c.Assert(res.GetOps()[i], Equals, expected.GetOps()[foundIdx])
    73  		c.Assert(res.GetPessimisticFlags()[i], Equals, expected.GetPessimisticFlags()[foundIdx])
    74  		c.Assert(res.GetKeys()[i], BytesEquals, expected.GetKeys()[foundIdx])
    75  		c.Assert(res.GetValues()[i], BytesEquals, expected.GetValues()[foundIdx])
    76  	}
    77  }
    78  
    79  type data struct {
    80  	ops      []ekvrpcpb.Op
    81  	keys     [][]byte
    82  	values   [][]byte
    83  	rowValue [][]types.Causet
    84  }
    85  
    86  func prepareTestData(se *stochastik, mutations *einsteindb.CommitterMutations, oldTblInfo causet.Block, newTblInfo causet.Block,
    87  	expecetedAmendOps []amendOp, c *C) (*data, *data, einsteindb.CommitterMutations) {
    88  	var err error
    89  	// Generated test data.
    90  	colIds := make([]int64, len(oldTblInfo.Meta().DeferredCausets))
    91  	basicRowValue := make([]types.Causet, len(oldTblInfo.Meta().DeferredCausets))
    92  	for i, col := range oldTblInfo.Meta().DeferredCausets {
    93  		colIds[i] = oldTblInfo.Meta().DeferredCausets[col.Offset].ID
    94  		if col.FieldType.Tp == allegrosql.TypeLong {
    95  			basicRowValue[i] = types.NewIntCauset(int64(col.Offset))
    96  		} else {
    97  			basicRowValue[i] = types.NewStringCauset(strconv.Itoa(col.Offset))
    98  		}
    99  	}
   100  	KeyOps := []ekvrpcpb.Op{ekvrpcpb.Op_Put, ekvrpcpb.Op_Del, ekvrpcpb.Op_Lock, ekvrpcpb.Op_Insert, ekvrpcpb.Op_Put,
   101  		ekvrpcpb.Op_Del, ekvrpcpb.Op_Insert, ekvrpcpb.Op_Lock}
   102  	oldRowValues := make([][]types.Causet, len(KeyOps))
   103  	newRowValues := make([][]types.Causet, len(KeyOps))
   104  	rd := rowcodec.CausetEncoder{Enable: true}
   105  	newData := &data{}
   106  	oldData := &data{}
   107  	expecteMutations := einsteindb.NewCommiterMutations(8)
   108  
   109  	// Generate old data.
   110  	for i := 0; i < len(KeyOps); i++ {
   111  		keyOp := KeyOps[i]
   112  		thisRowValue := make([]types.Causet, len(basicRowValue))
   113  		copy(thisRowValue, basicRowValue)
   114  		thisRowValue[0] = types.NewIntCauset(int64(i + 1))
   115  		thisRowValue[4] = types.NewIntCauset(int64(i + 1 + 4))
   116  
   117  		// Save old data.
   118  		rowKey := blockcodec.EncodeRowKeyWithHandle(oldTblInfo.Meta().ID, ekv.IntHandle(i+1))
   119  		var rowValue []byte
   120  		rowValue, err = rd.Encode(se.stochastikVars.StmtCtx, colIds, thisRowValue, nil)
   121  		c.Assert(err, IsNil)
   122  		if keyOp == ekvrpcpb.Op_Del || keyOp == ekvrpcpb.Op_Put {
   123  			// Skip the last Op_put, it has no old event value.
   124  			if i == 4 {
   125  				continue
   126  			}
   127  			oldData.keys = append(oldData.keys, rowKey)
   128  			oldData.values = append(oldData.values, rowValue)
   129  			oldData.ops = append(oldData.ops, keyOp)
   130  			oldData.rowValue = append(oldData.rowValue, thisRowValue)
   131  			if keyOp == ekvrpcpb.Op_Del {
   132  				mutations.Push(keyOp, rowKey, nil, true)
   133  			}
   134  		}
   135  		oldRowValues[i] = thisRowValue
   136  	}
   137  
   138  	// Generate new data.
   139  	for i := 0; i < len(KeyOps); i++ {
   140  		keyOp := KeyOps[i]
   141  		thisRowValue := make([]types.Causet, len(basicRowValue))
   142  		copy(thisRowValue, basicRowValue)
   143  		thisRowValue[0] = types.NewIntCauset(int64(i + 1))
   144  		// New column e value should be different from old event values.
   145  		thisRowValue[4] = types.NewIntCauset(int64(i+1+4) * 20)
   146  
   147  		var rowValue []byte
   148  		// Save new data.
   149  		rowKey := blockcodec.EncodeRowKeyWithHandle(oldTblInfo.Meta().ID, ekv.IntHandle(i+1))
   150  		if keyOp == ekvrpcpb.Op_Insert {
   151  			rowValue, err = blockcodec.EncodeOldRow(se.stochastikVars.StmtCtx, thisRowValue, colIds, nil, nil)
   152  		} else {
   153  			rowValue, err = rd.Encode(se.stochastikVars.StmtCtx, colIds, thisRowValue, nil)
   154  		}
   155  		if keyOp == ekvrpcpb.Op_Put || keyOp == ekvrpcpb.Op_Insert {
   156  			newData.keys = append(newData.keys, rowKey)
   157  			newData.values = append(newData.values, rowValue)
   158  			newData.ops = append(newData.ops, keyOp)
   159  			newData.rowValue = append(newData.rowValue, thisRowValue)
   160  			mutations.Push(keyOp, rowKey, rowValue, true)
   161  		} else if keyOp == ekvrpcpb.Op_Lock {
   162  			mutations.Push(keyOp, rowKey, []byte{}, true)
   163  		}
   164  		newRowValues[i] = thisRowValue
   165  	}
   166  
   167  	// Prepare expected results.
   168  	for _, op := range expecetedAmendOps {
   169  		var oldOp *amendOperationDeleteOldIndex
   170  		var newOp *amendOperationAddNewIndex
   171  		var info *amendOperationAddIndexInfo
   172  		var ok bool
   173  		oldOp, ok = op.(*amendOperationDeleteOldIndex)
   174  		if ok {
   175  			info = oldOp.info
   176  		} else {
   177  			newOp = op.(*amendOperationAddNewIndex)
   178  			info = newOp.info
   179  		}
   180  		var idxVal []byte
   181  		genIndexKV := func(inputRow []types.Causet) ([]byte, []byte) {
   182  			indexCausets := make([]types.Causet, len(info.relatedOldIdxDefCauss))
   183  			for colIdx, col := range info.relatedOldIdxDefCauss {
   184  				indexCausets[colIdx] = inputRow[col.Offset]
   185  			}
   186  			ekvHandle := ekv.IntHandle(inputRow[0].GetInt64())
   187  			idxKey, _, err := blockcodec.GenIndexKey(se.stochastikVars.StmtCtx, newTblInfo.Meta(),
   188  				info.indexInfoAtCommit.Meta(), newTblInfo.Meta().ID, indexCausets, ekvHandle, nil)
   189  			c.Assert(err, IsNil)
   190  			idxVal, err = blockcodec.GenIndexValue(se.stochastikVars.StmtCtx, newTblInfo.Meta(), info.indexInfoAtCommit.Meta(),
   191  				false, info.indexInfoAtCommit.Meta().Unique, false, indexCausets, ekvHandle)
   192  			c.Assert(err, IsNil)
   193  			return idxKey, idxVal
   194  		}
   195  		_, ok = op.(*amendOperationDeleteOldIndex)
   196  		if ok {
   197  			c.Assert(addIndexNeedRemoveOp(info.AmendOpType), IsTrue)
   198  			for i := range oldData.keys {
   199  				if addIndexNeedRemoveOp(info.AmendOpType) && isDeleteOp(oldData.ops[i]) {
   200  					thisRowValue := oldData.rowValue[i]
   201  					idxKey, _ := genIndexKV(thisRowValue)
   202  					expecteMutations.Push(ekvrpcpb.Op_Del, idxKey, []byte{}, false)
   203  				}
   204  			}
   205  		}
   206  		_, ok = op.(*amendOperationAddNewIndex)
   207  		if ok {
   208  			c.Assert(addIndexNeedAddOp(info.AmendOpType), IsTrue)
   209  			for i := range newData.keys {
   210  				if addIndexNeedAddOp(info.AmendOpType) && isInsertOp(newData.ops[i]) {
   211  					thisRowValue := newData.rowValue[i]
   212  					idxKey, idxVal := genIndexKV(thisRowValue)
   213  					c.Assert(err, IsNil)
   214  					mutOp := ekvrpcpb.Op_Put
   215  					if info.indexInfoAtCommit.Meta().Unique {
   216  						mutOp = ekvrpcpb.Op_Insert
   217  					}
   218  					expecteMutations.Push(mutOp, idxKey, idxVal, false)
   219  				}
   220  			}
   221  		}
   222  	}
   223  	return newData, oldData, expecteMutations
   224  }
   225  
   226  func (s *testSchemaAmenderSuite) TestAmendDefCauslectAndGenMutations(c *C) {
   227  	ctx := context.Background()
   228  	causetstore := newStore(c, "test_schema_amender")
   229  	defer causetstore.Close()
   230  	se := &stochastik{
   231  		causetstore:    causetstore,
   232  		BerolinaSQL:    BerolinaSQL.New(),
   233  		stochastikVars: variable.NewStochastikVars(),
   234  	}
   235  	startStates := []perceptron.SchemaState{perceptron.StateNone, perceptron.StateDeleteOnly}
   236  	for _, startState := range startStates {
   237  		endStatMap := ConstOpAddIndex[startState]
   238  		var endStates []perceptron.SchemaState
   239  		for st := range endStatMap {
   240  			endStates = append(endStates, st)
   241  		}
   242  		sort.Slice(endStates, func(i, j int) bool { return endStates[i] < endStates[j] })
   243  		for _, endState := range endStates {
   244  			// column: a, b, c, d, e, c_str, d_str, e_str, f, g.
   245  			// PK: a.
   246  			// indices: c_d_e, e, f, g, f_g, c_d_e_str, c_d_e_str_prefix.
   247  			oldTblMeta := embedded.MockSignedTable()
   248  			initTblDefCausIdxID(oldTblMeta)
   249  			// Indices[0] does not exist at the start.
   250  			oldTblMeta.Indices = oldTblMeta.Indices[1:]
   251  			oldTbInfo, err := causet.TableFromMeta(nil, oldTblMeta)
   252  			c.Assert(err, IsNil)
   253  			oldTblMeta.Indices[0].State = startState
   254  			oldTblMeta.Indices[2].State = endState
   255  
   256  			newTblMeta := embedded.MockSignedTable()
   257  			initTblDefCausIdxID(newTblMeta)
   258  			// colh is newly added.
   259  			colh := &perceptron.DeferredCausetInfo{
   260  				State:     perceptron.StatePublic,
   261  				Offset:    12,
   262  				Name:      perceptron.NewCIStr("b"),
   263  				FieldType: *(types.NewFieldType(allegrosql.TypeLong)),
   264  				ID:        13,
   265  			}
   266  			newTblMeta.DeferredCausets = append(newTblMeta.DeferredCausets, colh)
   267  			// The last index "c_d_e_str_prefix is dropped.
   268  			newTblMeta.Indices = newTblMeta.Indices[:len(newTblMeta.Indices)-1]
   269  			newTblMeta.Indices[0].Unique = false
   270  			newTblInfo, err := causet.TableFromMeta(nil, newTblMeta)
   271  			c.Assert(err, IsNil)
   272  			newTblMeta.Indices[0].State = endState
   273  			// Indices[1] is newly created.
   274  			newTblMeta.Indices[1].State = endState
   275  			// Indices[3] is dropped
   276  			newTblMeta.Indices[3].State = startState
   277  
   278  			// Only the add index amend operations is collected in the results.
   279  			collector := newAmendDefCauslector()
   280  			tblID := int64(1)
   281  			err = collector.collectTblAmendOps(se, tblID, oldTbInfo, newTblInfo, 1<<perceptron.CausetActionAddIndex)
   282  			c.Assert(err, IsNil)
   283  			c.Assert(len(collector.tblAmendOpMap[tblID]), GreaterEqual, 2)
   284  			var expectedAmendOps []amendOp
   285  
   286  			// For index 0.
   287  			addIndexOpInfo := &amendOperationAddIndexInfo{
   288  				AmendOpType:           ConstOpAddIndex[perceptron.StateNone][endState],
   289  				tblInfoAtStart:        oldTbInfo,
   290  				tblInfoAtCommit:       newTblInfo,
   291  				indexInfoAtStart:      nil,
   292  				indexInfoAtCommit:     newTblInfo.Indices()[0],
   293  				relatedOldIdxDefCauss: []*causet.DeferredCauset{oldTbInfo.DefCauss()[2], oldTbInfo.DefCauss()[3], oldTbInfo.DefCauss()[4]},
   294  			}
   295  			if addIndexNeedRemoveOp(addIndexOpInfo.AmendOpType) {
   296  				expectedAmendOps = append(expectedAmendOps, &amendOperationDeleteOldIndex{
   297  					info: addIndexOpInfo,
   298  				})
   299  			}
   300  			if addIndexNeedAddOp(addIndexOpInfo.AmendOpType) {
   301  				expectedAmendOps = append(expectedAmendOps, &amendOperationAddNewIndex{
   302  					info: addIndexOpInfo,
   303  				})
   304  			}
   305  
   306  			// For index 1.
   307  			addIndexOpInfo1 := &amendOperationAddIndexInfo{
   308  				AmendOpType:           ConstOpAddIndex[startState][endState],
   309  				tblInfoAtStart:        oldTbInfo,
   310  				tblInfoAtCommit:       newTblInfo,
   311  				indexInfoAtStart:      oldTbInfo.Indices()[0],
   312  				indexInfoAtCommit:     newTblInfo.Indices()[1],
   313  				relatedOldIdxDefCauss: []*causet.DeferredCauset{oldTbInfo.DefCauss()[4]},
   314  			}
   315  			if addIndexNeedRemoveOp(addIndexOpInfo1.AmendOpType) {
   316  				expectedAmendOps = append(expectedAmendOps, &amendOperationDeleteOldIndex{
   317  					info: addIndexOpInfo1,
   318  				})
   319  			}
   320  			if addIndexNeedAddOp(addIndexOpInfo1.AmendOpType) {
   321  				expectedAmendOps = append(expectedAmendOps, &amendOperationAddNewIndex{
   322  					info: addIndexOpInfo1,
   323  				})
   324  			}
   325  			// Check collect results.
   326  			for i, amendOp := range collector.tblAmendOpMap[tblID] {
   327  				oldOp, ok := amendOp.(*amendOperationDeleteOldIndex)
   328  				var info *amendOperationAddIndexInfo
   329  				var expectedInfo *amendOperationAddIndexInfo
   330  				if ok {
   331  					info = oldOp.info
   332  					expectedOp, ok := expectedAmendOps[i].(*amendOperationDeleteOldIndex)
   333  					c.Assert(ok, IsTrue)
   334  					expectedInfo = expectedOp.info
   335  				} else {
   336  					newOp, ok := amendOp.(*amendOperationAddNewIndex)
   337  					c.Assert(ok, IsTrue)
   338  					info = newOp.info
   339  					expectedOp, ok := expectedAmendOps[i].(*amendOperationAddNewIndex)
   340  					c.Assert(ok, IsTrue)
   341  					expectedInfo = expectedOp.info
   342  				}
   343  				c.Assert(info.AmendOpType, Equals, expectedInfo.AmendOpType)
   344  				c.Assert(info.tblInfoAtStart, Equals, expectedInfo.tblInfoAtStart)
   345  				c.Assert(info.tblInfoAtCommit, Equals, expectedInfo.tblInfoAtCommit)
   346  				c.Assert(info.indexInfoAtStart, Equals, expectedInfo.indexInfoAtStart)
   347  				c.Assert(info.indexInfoAtCommit, Equals, expectedInfo.indexInfoAtCommit)
   348  				for j, col := range expectedInfo.relatedOldIdxDefCauss {
   349  					c.Assert(col, Equals, expectedInfo.relatedOldIdxDefCauss[j])
   350  				}
   351  			}
   352  			// Generated test data.
   353  			mutations := einsteindb.NewCommiterMutations(8)
   354  			newData, oldData, expectedMutations := prepareTestData(se, &mutations, oldTbInfo, newTblInfo, expectedAmendOps, c)
   355  			// Prepare old data in causet.
   356  			txnPrepare, err := se.causetstore.Begin()
   357  			c.Assert(err, IsNil)
   358  			for i, key := range oldData.keys {
   359  				err = txnPrepare.Set(key, oldData.values[i])
   360  				c.Assert(err, IsNil)
   361  			}
   362  			err = txnPrepare.Commit(ctx)
   363  			c.Assert(err, IsNil)
   364  			txnCheck, err := se.causetstore.Begin()
   365  			c.Assert(err, IsNil)
   366  			snaFIDelata, err := txnCheck.GetSnapshot().Get(ctx, oldData.keys[0])
   367  			c.Assert(err, IsNil)
   368  			c.Assert(oldData.values[0], BytesEquals, snaFIDelata)
   369  			err = txnCheck.Rollback()
   370  			c.Assert(err, IsNil)
   371  
   372  			// Write data for this new transaction, its memory buffer will be used by schemaReplicant amender.
   373  			txn, err := se.causetstore.Begin()
   374  			c.Assert(err, IsNil)
   375  			se.txn.changeInvalidToValid(txn)
   376  			txn, err = se.Txn(true)
   377  			c.Assert(err, IsNil)
   378  			for i, key := range newData.keys {
   379  				err = txn.Set(key, newData.values[i])
   380  				c.Assert(err, IsNil)
   381  			}
   382  			var oldKeys []ekv.Key
   383  			for i, key := range oldData.keys {
   384  				if oldData.ops[i] == ekvrpcpb.Op_Del {
   385  					err = txn.Delete(key)
   386  					c.Assert(err, IsNil)
   387  				}
   388  				oldKeys = append(oldKeys, key)
   389  			}
   390  			curVer, err := se.causetstore.CurrentVersion()
   391  			c.Assert(err, IsNil)
   392  			se.stochastikVars.TxnCtx.SetForUFIDelateTS(curVer.Ver + 1)
   393  			snap, err := se.causetstore.GetSnapshot(ekv.Version{Ver: se.stochastikVars.TxnCtx.GetForUFIDelateTS()})
   394  			c.Assert(err, IsNil)
   395  			oldVals, err := snap.BatchGet(ctx, oldKeys)
   396  			c.Assert(err, IsNil)
   397  			c.Assert(len(oldVals), Equals, len(oldKeys))
   398  
   399  			schemaAmender := NewSchemaAmenderForEinsteinDBTxn(se)
   400  			// Some noisy index key values.
   401  			for i := 0; i < 4; i++ {
   402  				idxValue := []byte("idxValue")
   403  				idxKey := blockcodec.EncodeIndexSeekKey(oldTbInfo.Meta().ID, oldTbInfo.Indices()[2].Meta().ID, idxValue)
   404  				err = txn.Set(idxKey, idxValue)
   405  				c.Assert(err, IsNil)
   406  				mutations.Push(ekvrpcpb.Op_Put, idxKey, idxValue, false)
   407  			}
   408  
   409  			res, err := schemaAmender.genAllAmendMutations(ctx, mutations, collector)
   410  			c.Assert(err, IsNil)
   411  
   412  			// Validate generated results.
   413  			c.Assert(len(res.GetKeys()), Equals, len(res.GetOps()))
   414  			c.Assert(len(res.GetValues()), Equals, len(res.GetOps()))
   415  			c.Assert(len(res.GetPessimisticFlags()), Equals, len(res.GetOps()))
   416  			mutationsEqual(res, &expectedMutations, c)
   417  			err = txn.Rollback()
   418  			c.Assert(err, IsNil)
   419  		}
   420  	}
   421  }