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 }