github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/stochastik/schema_amender.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 "encoding/hex" 20 "fmt" 21 "reflect" 22 23 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 24 pb "github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb" 25 "github.com/whtcorpsinc/errors" 26 "github.com/whtcorpsinc/milevadb/blockcodec" 27 "github.com/whtcorpsinc/milevadb/causet" 28 "github.com/whtcorpsinc/milevadb/causet/blocks" 29 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb" 30 "github.com/whtcorpsinc/milevadb/dbs" 31 "github.com/whtcorpsinc/milevadb/ekv" 32 "github.com/whtcorpsinc/milevadb/interlock" 33 "github.com/whtcorpsinc/milevadb/memex" 34 "github.com/whtcorpsinc/milevadb/schemareplicant" 35 "github.com/whtcorpsinc/milevadb/soliton/chunk" 36 "github.com/whtcorpsinc/milevadb/soliton/logutil" 37 "github.com/whtcorpsinc/milevadb/soliton/rowcodec" 38 "github.com/whtcorpsinc/milevadb/stochastikctx" 39 "github.com/whtcorpsinc/milevadb/types" 40 "go.uber.org/zap" 41 ) 42 43 const amendableType = nonMemAmendType | memBufAmendType 44 const nonMemAmendType = (1 << perceptron.CausetActionAddDeferredCauset) | (1 << perceptron.CausetActionDropDeferredCauset) | (1 << perceptron.CausetActionDropIndex) 45 const memBufAmendType = uint64(1<<perceptron.CausetActionAddIndex) | (1 << perceptron.CausetActionModifyDeferredCauset) 46 47 // Amend operation types. 48 const ( 49 AmendNone int = iota 50 51 // For add index. 52 AmendNeedAddDelete 53 AmendNeedAddDeleteAndInsert 54 AmendNeedAddInsert 55 ) 56 57 // ConstOpAddIndex is the possible dbs state changes, and related amend action types. 58 var ConstOpAddIndex = map[perceptron.SchemaState]map[perceptron.SchemaState]int{ 59 perceptron.StateNone: { 60 perceptron.StateDeleteOnly: AmendNeedAddDelete, 61 perceptron.StateWriteOnly: AmendNeedAddDeleteAndInsert, 62 perceptron.StateWriteReorganization: AmendNeedAddDeleteAndInsert, 63 perceptron.StatePublic: AmendNeedAddDeleteAndInsert, 64 }, 65 perceptron.StateDeleteOnly: { 66 perceptron.StateWriteOnly: AmendNeedAddInsert, 67 perceptron.StateWriteReorganization: AmendNeedAddInsert, 68 perceptron.StatePublic: AmendNeedAddInsert, 69 }, 70 perceptron.StateWriteOnly: { 71 perceptron.StateWriteReorganization: AmendNone, 72 perceptron.StatePublic: AmendNone, 73 }, 74 perceptron.StateWriteReorganization: { 75 perceptron.StatePublic: AmendNone, 76 }, 77 } 78 79 type schemaAndCausetDecoder struct { 80 schemaReplicant *memex.Schema 81 causetDecoder *rowcodec.ChunkCausetDecoder 82 } 83 84 // amendDefCauslector collects all amend operations. 85 type amendDefCauslector struct { 86 tblAmendOpMap map[int64][]amendOp 87 } 88 89 func newAmendDefCauslector() *amendDefCauslector { 90 res := &amendDefCauslector{ 91 tblAmendOpMap: make(map[int64][]amendOp), 92 } 93 return res 94 } 95 96 func findIndexByID(tbl causet.Block, ID int64) causet.Index { 97 for _, indexInfo := range tbl.Indices() { 98 if indexInfo.Meta().ID == ID { 99 return indexInfo 100 } 101 } 102 return nil 103 } 104 105 func findDefCausByID(tbl causet.Block, colID int64) *causet.DeferredCauset { 106 for _, colInfo := range tbl.DefCauss() { 107 if colInfo.ID == colID { 108 return colInfo 109 } 110 } 111 return nil 112 } 113 114 func addIndexNeedRemoveOp(amendOp int) bool { 115 if amendOp == AmendNeedAddDelete || amendOp == AmendNeedAddDeleteAndInsert { 116 return true 117 } 118 return false 119 } 120 121 func addIndexNeedAddOp(amendOp int) bool { 122 if amendOp == AmendNeedAddDeleteAndInsert || amendOp == AmendNeedAddInsert { 123 return true 124 } 125 return false 126 } 127 128 func (a *amendDefCauslector) keyHasAmendOp(key []byte) bool { 129 tblID := blockcodec.DecodeTableID(key) 130 ops := a.tblAmendOpMap[tblID] 131 return len(ops) > 0 132 } 133 134 func needDefCauslectIndexOps(actionType uint64) bool { 135 return actionType&(1<<perceptron.CausetActionAddIndex) != 0 136 } 137 138 func needDefCauslectModifyDefCausOps(actionType uint64) bool { 139 return actionType&(1<<perceptron.CausetActionModifyDeferredCauset) != 0 140 } 141 142 func fieldTypeDeepEquals(ft1 *types.FieldType, ft2 *types.FieldType) bool { 143 if ft1.Tp == ft2.Tp && 144 ft1.Flag == ft2.Flag && 145 ft1.Flen == ft2.Flen && 146 ft1.Decimal == ft2.Decimal && 147 ft1.Charset == ft2.Charset && 148 ft1.DefCauslate == ft2.DefCauslate && 149 len(ft1.Elems) == len(ft2.Elems) { 150 for i, elem := range ft1.Elems { 151 if elem != ft2.Elems[i] { 152 return false 153 } 154 } 155 return true 156 } 157 return false 158 } 159 160 // colChangeAmendable checks whether the column change is amendable, now only increasing column field 161 // length is allowed for committing concurrent pessimistic transactions. 162 func colChangeAmendable(colAtStart *perceptron.DeferredCausetInfo, colAtCommit *perceptron.DeferredCausetInfo) error { 163 // Modifying a stored generated column is not allowed by DBS, the generated related fields are not considered. 164 if !fieldTypeDeepEquals(&colAtStart.FieldType, &colAtCommit.FieldType) { 165 if colAtStart.FieldType.Flag != colAtCommit.FieldType.Flag { 166 return errors.Trace(errors.Errorf("flag is not matched for column=%v, from=%v to=%v", 167 colAtCommit.Name.String(), colAtStart.FieldType.Flag, colAtCommit.FieldType.Flag)) 168 } 169 if colAtStart.Charset != colAtCommit.Charset || colAtStart.DefCauslate != colAtCommit.DefCauslate { 170 return errors.Trace(errors.Errorf("charset or collate is not matched for column=%v", colAtCommit.Name.String())) 171 } 172 _, err := dbs.CheckModifyTypeCompatible(&colAtStart.FieldType, &colAtCommit.FieldType) 173 if err != nil { 174 return errors.Trace(err) 175 } 176 } 177 // TODO Default value change is not supported. 178 if !reflect.DeepEqual(colAtStart.DefaultValue, colAtCommit.DefaultValue) { 179 return errors.Trace(errors.Errorf("default value is not matched for column=%v, from=%v to=%v", 180 colAtCommit.Name.String(), colAtStart.DefaultValue, colAtCommit.DefaultValue)) 181 } 182 if !bytes.Equal(colAtStart.DefaultValueBit, colAtCommit.DefaultValueBit) { 183 return errors.Trace(errors.Errorf("default value bits is not matched for column=%v, from=%v to=%v", 184 colAtCommit.Name.String(), colAtStart.DefaultValueBit, colAtCommit.DefaultValueBit)) 185 } 186 if colAtStart.Version != colAtCommit.Version { 187 return errors.Trace(errors.Errorf("column version is not matched for column=%v, from=%v to=%v", 188 colAtCommit.Name.String(), colAtStart.Version, colAtCommit.Version)) 189 } 190 return nil 191 } 192 193 // collectModifyDefCausAmendOps is used to check if there is column change from nullable to not null by now. 194 // TODO allow column change from nullable to not null, and generate keys check operation. 195 func (a *amendDefCauslector) collectModifyDefCausAmendOps(tblAtStart, tblAtCommit causet.Block) ([]amendOp, error) { 196 for _, colAtCommit := range tblAtCommit.DefCauss() { 197 colAtStart := findDefCausByID(tblAtStart, colAtCommit.ID) 198 if colAtStart != nil { 199 err := colChangeAmendable(colAtStart.DeferredCausetInfo, colAtCommit.DeferredCausetInfo) 200 if err != nil { 201 return nil, err 202 } 203 } 204 } 205 return nil, nil 206 } 207 208 func (a *amendDefCauslector) collectIndexAmendOps(sctx stochastikctx.Context, tblAtStart, tblAtCommit causet.Block) ([]amendOp, error) { 209 res := make([]amendOp, 0, 4) 210 // Check index having state change, collect index column info. 211 for _, idxInfoAtCommit := range tblAtCommit.Indices() { 212 idxInfoAtStart := findIndexByID(tblAtStart, idxInfoAtCommit.Meta().ID) 213 // Try to find index state change. 214 var amendOpType int 215 if idxInfoAtStart == nil { 216 amendOpType = ConstOpAddIndex[perceptron.StateNone][idxInfoAtCommit.Meta().State] 217 } else if idxInfoAtCommit.Meta().State > idxInfoAtStart.Meta().State { 218 amendOpType = ConstOpAddIndex[idxInfoAtStart.Meta().State][idxInfoAtCommit.Meta().State] 219 } 220 if amendOpType != AmendNone { 221 // TODO unique index amend is not supported by now. 222 if idxInfoAtCommit.Meta().Unique { 223 return nil, errors.Trace(errors.Errorf("amend unique index=%v for causet=%v is not supported now", 224 idxInfoAtCommit.Meta().Name, tblAtCommit.Meta().Name)) 225 } 226 opInfo := &amendOperationAddIndexInfo{} 227 opInfo.AmendOpType = amendOpType 228 opInfo.tblInfoAtStart = tblAtStart 229 opInfo.tblInfoAtCommit = tblAtCommit 230 opInfo.indexInfoAtStart = idxInfoAtStart 231 opInfo.indexInfoAtCommit = idxInfoAtCommit 232 for _, idxDefCaus := range idxInfoAtCommit.Meta().DeferredCausets { 233 colID := tblAtCommit.Meta().DeferredCausets[idxDefCaus.Offset].ID 234 oldDefCausInfo := findDefCausByID(tblAtStart, colID) 235 // TODO: now index column MUST be found in old causet columns, generated column is not supported. 236 if oldDefCausInfo == nil || oldDefCausInfo.IsGenerated() || oldDefCausInfo.Hidden { 237 return nil, errors.Trace(errors.Errorf("amend index column=%v id=%v is not found or generated in causet=%v", 238 idxDefCaus.Name, colID, tblAtCommit.Meta().Name.String())) 239 } 240 opInfo.relatedOldIdxDefCauss = append(opInfo.relatedOldIdxDefCauss, oldDefCausInfo) 241 } 242 opInfo.schemaAndCausetDecoder = newSchemaAndCausetDecoder(sctx, tblAtStart.Meta()) 243 fieldTypes := make([]*types.FieldType, 0, len(tblAtStart.Meta().DeferredCausets)) 244 for _, col := range tblAtStart.Meta().DeferredCausets { 245 fieldTypes = append(fieldTypes, &col.FieldType) 246 } 247 opInfo.chk = chunk.NewChunkWithCapacity(fieldTypes, 4) 248 if addIndexNeedRemoveOp(amendOpType) { 249 removeIndexOp := &amendOperationDeleteOldIndex{ 250 info: opInfo, 251 } 252 res = append(res, removeIndexOp) 253 } 254 if addIndexNeedAddOp(amendOpType) { 255 addNewIndexOp := &amendOperationAddNewIndex{ 256 info: opInfo, 257 } 258 res = append(res, addNewIndexOp) 259 } 260 } 261 } 262 return res, nil 263 } 264 265 // collectTblAmendOps collects amend operations for each causet using the schemaReplicant diff between startTS and commitTS. 266 func (a *amendDefCauslector) collectTblAmendOps(sctx stochastikctx.Context, phyTblID int64, 267 tblInfoAtStart, tblInfoAtCommit causet.Block, actionType uint64) error { 268 if _, ok := a.tblAmendOpMap[phyTblID]; !ok { 269 a.tblAmendOpMap[phyTblID] = make([]amendOp, 0, 4) 270 } 271 if needDefCauslectModifyDefCausOps(actionType) { 272 _, err := a.collectModifyDefCausAmendOps(tblInfoAtStart, tblInfoAtCommit) 273 if err != nil { 274 return err 275 } 276 } 277 if needDefCauslectIndexOps(actionType) { 278 // TODO: currently only "add index" is considered. 279 ops, err := a.collectIndexAmendOps(sctx, tblInfoAtStart, tblInfoAtCommit) 280 if err != nil { 281 return err 282 } 283 a.tblAmendOpMap[phyTblID] = append(a.tblAmendOpMap[phyTblID], ops...) 284 } 285 return nil 286 } 287 288 func isDeleteOp(keyOp pb.Op) bool { 289 return keyOp == pb.Op_Del || keyOp == pb.Op_Put 290 } 291 292 func isInsertOp(keyOp pb.Op) bool { 293 return keyOp == pb.Op_Put || keyOp == pb.Op_Insert 294 } 295 296 // amendOp is an amend operation for a specific schemaReplicant change, new mutations will be generated using input ones. 297 type amendOp interface { 298 genMutations(ctx context.Context, sctx stochastikctx.Context, commitMutations einsteindb.CommitterMutations, ekvMap *rowEkvMap, 299 resultMutations *einsteindb.CommitterMutations) error 300 } 301 302 // amendOperationAddIndex represents one amend operation related to a specific add index change. 303 type amendOperationAddIndexInfo struct { 304 AmendOpType int 305 tblInfoAtStart causet.Block 306 tblInfoAtCommit causet.Block 307 indexInfoAtStart causet.Index 308 indexInfoAtCommit causet.Index 309 relatedOldIdxDefCauss []*causet.DeferredCauset 310 311 schemaAndCausetDecoder *schemaAndCausetDecoder 312 chk *chunk.Chunk 313 } 314 315 // amendOperationDeleteOldIndex represents the remove operation will be performed on old key values for add index amend. 316 type amendOperationDeleteOldIndex struct { 317 info *amendOperationAddIndexInfo 318 } 319 320 // amendOperationAddNewIndex represents the add operation will be performed on new key values for add index amend. 321 type amendOperationAddNewIndex struct { 322 info *amendOperationAddIndexInfo 323 } 324 325 func (a *amendOperationAddIndexInfo) String() string { 326 var colStr string 327 colStr += "[" 328 for _, colInfo := range a.relatedOldIdxDefCauss { 329 colStr += fmt.Sprintf(" %s ", colInfo.Name) 330 } 331 colStr += "]" 332 res := fmt.Sprintf("AmenedOpType=%d phyTblID=%d idxID=%d columns=%v", a.AmendOpType, a.indexInfoAtCommit.Meta().ID, 333 a.indexInfoAtCommit.Meta().ID, colStr) 334 return res 335 } 336 337 func (a *amendOperationDeleteOldIndex) genMutations(ctx context.Context, sctx stochastikctx.Context, 338 commitMutations einsteindb.CommitterMutations, ekvMap *rowEkvMap, resAddMutations *einsteindb.CommitterMutations) error { 339 for i, key := range commitMutations.GetKeys() { 340 keyOp := commitMutations.GetOps()[i] 341 if blockcodec.IsIndexKey(key) || blockcodec.DecodeTableID(key) != a.info.tblInfoAtCommit.Meta().ID { 342 continue 343 } 344 if !isDeleteOp(keyOp) { 345 continue 346 } 347 err := a.processRowKey(ctx, sctx, key, ekvMap.oldRowEkvMap, resAddMutations) 348 if err != nil { 349 return err 350 } 351 } 352 return nil 353 } 354 355 func (a *amendOperationAddNewIndex) genMutations(ctx context.Context, sctx stochastikctx.Context, commitMutations einsteindb.CommitterMutations, 356 ekvMap *rowEkvMap, resAddMutations *einsteindb.CommitterMutations) error { 357 for i, key := range commitMutations.GetKeys() { 358 keyOp := commitMutations.GetOps()[i] 359 if blockcodec.IsIndexKey(key) || blockcodec.DecodeTableID(key) != a.info.tblInfoAtCommit.Meta().ID { 360 continue 361 } 362 if !isInsertOp(keyOp) { 363 continue 364 } 365 err := a.processRowKey(ctx, sctx, key, ekvMap.newRowEkvMap, resAddMutations) 366 if err != nil { 367 return err 368 } 369 } 370 return nil 371 } 372 373 func (a *amendOperationAddIndexInfo) genIndexKeyValue(ctx context.Context, sctx stochastikctx.Context, ekvMap map[string][]byte, 374 key []byte, ekvHandle ekv.Handle, keyOnly bool) ([]byte, []byte, error) { 375 chk := a.chk 376 chk.Reset() 377 val, ok := ekvMap[string(key)] 378 if !ok { 379 // The Op_Put may not exist in old value ekv map. 380 if keyOnly { 381 return nil, nil, nil 382 } 383 return nil, nil, errors.Errorf("key=%v is not found in new event ekv map", ekv.Key(key).String()) 384 } 385 err := interlock.DecodeRowValToChunk(sctx, a.schemaAndCausetDecoder.schemaReplicant, a.tblInfoAtStart.Meta(), ekvHandle, val, chk, a.schemaAndCausetDecoder.causetDecoder) 386 if err != nil { 387 logutil.Logger(ctx).Warn("amend decode value to chunk failed", zap.Error(err)) 388 return nil, nil, errors.Trace(err) 389 } 390 idxVals := make([]types.Causet, 0, len(a.indexInfoAtCommit.Meta().DeferredCausets)) 391 for _, oldDefCaus := range a.relatedOldIdxDefCauss { 392 idxVals = append(idxVals, chk.GetRow(0).GetCauset(oldDefCaus.Offset, &oldDefCaus.FieldType)) 393 } 394 395 // Generate index key buf. 396 newIdxKey, distinct, err := blockcodec.GenIndexKey(sctx.GetStochastikVars().StmtCtx, 397 a.tblInfoAtCommit.Meta(), a.indexInfoAtCommit.Meta(), a.tblInfoAtCommit.Meta().ID, idxVals, ekvHandle, nil) 398 if err != nil { 399 logutil.Logger(ctx).Warn("amend generate index key failed", zap.Error(err)) 400 return nil, nil, errors.Trace(err) 401 } 402 if keyOnly { 403 return newIdxKey, []byte{}, nil 404 } 405 406 // Generate index value buf. 407 containsNonBinaryString := blocks.ContainsNonBinaryString(a.indexInfoAtCommit.Meta().DeferredCausets, a.tblInfoAtCommit.Meta().DeferredCausets) 408 newIdxVal, err := blockcodec.GenIndexValue(sctx.GetStochastikVars().StmtCtx, a.tblInfoAtCommit.Meta(), 409 a.indexInfoAtCommit.Meta(), containsNonBinaryString, distinct, false, idxVals, ekvHandle) 410 if err != nil { 411 logutil.Logger(ctx).Warn("amend generate index values failed", zap.Error(err)) 412 return nil, nil, errors.Trace(err) 413 } 414 return newIdxKey, newIdxVal, nil 415 } 416 417 func (a *amendOperationAddNewIndex) processRowKey(ctx context.Context, sctx stochastikctx.Context, key []byte, 418 ekvMap map[string][]byte, resAddMutations *einsteindb.CommitterMutations) error { 419 ekvHandle, err := blockcodec.DecodeRowKey(key) 420 if err != nil { 421 logutil.Logger(ctx).Error("decode key error", zap.String("key", hex.EncodeToString(key)), zap.Error(err)) 422 return errors.Trace(err) 423 } 424 425 newIdxKey, newIdxValue, err := a.info.genIndexKeyValue(ctx, sctx, ekvMap, key, ekvHandle, false) 426 if err != nil { 427 return errors.Trace(err) 428 } 429 resAddMutations.Push(pb.Op_Put, newIdxKey, newIdxValue, false) 430 return nil 431 } 432 433 func (a *amendOperationDeleteOldIndex) processRowKey(ctx context.Context, sctx stochastikctx.Context, key []byte, 434 oldValEkvMap map[string][]byte, resAddMutations *einsteindb.CommitterMutations) error { 435 ekvHandle, err := blockcodec.DecodeRowKey(key) 436 if err != nil { 437 logutil.Logger(ctx).Error("decode key error", zap.String("key", hex.EncodeToString(key)), zap.Error(err)) 438 return errors.Trace(err) 439 } 440 // Generated delete index key value. 441 newIdxKey, emptyVal, err := a.info.genIndexKeyValue(ctx, sctx, oldValEkvMap, key, ekvHandle, true) 442 if err != nil { 443 return errors.Trace(err) 444 } 445 // For Op_Put the key may not exist in old key value map. 446 if len(newIdxKey) > 0 { 447 resAddMutations.Push(pb.Op_Del, newIdxKey, emptyVal, false) 448 } 449 return nil 450 } 451 452 // SchemaAmender is used to amend pessimistic transactions for schemaReplicant change. 453 type SchemaAmender struct { 454 sess *stochastik 455 } 456 457 // NewSchemaAmenderForEinsteinDBTxn creates a schemaReplicant amender for einsteindbTxn type. 458 func NewSchemaAmenderForEinsteinDBTxn(sess *stochastik) *SchemaAmender { 459 amender := &SchemaAmender{sess: sess} 460 return amender 461 } 462 463 func (s *SchemaAmender) getAmendableKeys(commitMutations einsteindb.CommitterMutations, info *amendDefCauslector) ([]ekv.Key, []ekv.Key) { 464 addKeys := make([]ekv.Key, 0, len(commitMutations.GetKeys())) 465 removeKeys := make([]ekv.Key, 0, len(commitMutations.GetKeys())) 466 for i, byteKey := range commitMutations.GetKeys() { 467 if blockcodec.IsIndexKey(byteKey) || !info.keyHasAmendOp(byteKey) { 468 continue 469 } 470 keyOp := commitMutations.GetOps()[i] 471 if pb.Op_Put == keyOp { 472 addKeys = append(addKeys, byteKey) 473 removeKeys = append(removeKeys, byteKey) 474 } else if pb.Op_Insert == keyOp { 475 addKeys = append(addKeys, byteKey) 476 } else if pb.Op_Del == keyOp { 477 removeKeys = append(removeKeys, byteKey) 478 } // else Do nothing. 479 } 480 return addKeys, removeKeys 481 } 482 483 type rowEkvMap struct { 484 oldRowEkvMap map[string][]byte 485 newRowEkvMap map[string][]byte 486 } 487 488 func (s *SchemaAmender) prepareEkvMap(ctx context.Context, commitMutations einsteindb.CommitterMutations, info *amendDefCauslector) (*rowEkvMap, error) { 489 // Get keys need to be considered for the amend operation, currently only event keys. 490 addKeys, removeKeys := s.getAmendableKeys(commitMutations, info) 491 492 // BatchGet the new key values, the Op_Put and Op_Insert type keys in memory buffer. 493 txn, err := s.sess.Txn(true) 494 if err != nil { 495 return nil, errors.Trace(err) 496 } 497 newValEkvMap, err := txn.BatchGet(ctx, addKeys) 498 if err != nil { 499 logutil.Logger(ctx).Warn("amend failed to batch get ekv new keys", zap.Error(err)) 500 return nil, errors.Trace(err) 501 } 502 if len(newValEkvMap) != len(addKeys) { 503 logutil.Logger(ctx).Error("amend failed to batch get results invalid", 504 zap.Int("addKeys len", len(addKeys)), zap.Int("newValEkvMap", len(newValEkvMap))) 505 return nil, errors.Errorf("add keys has %v values but result ekvMap has %v", len(addKeys), len(newValEkvMap)) 506 } 507 // BatchGet the old key values, the Op_Del and Op_Put types keys in storage using forUFIDelateTS, the Op_put type is for 508 // event uFIDelate using the same event key, it may not exist. 509 snapshot, err := s.sess.GetStore().GetSnapshot(ekv.Version{Ver: s.sess.stochastikVars.TxnCtx.GetForUFIDelateTS()}) 510 if err != nil { 511 logutil.Logger(ctx).Warn("amend failed to get snapshot using forUFIDelateTS", zap.Error(err)) 512 return nil, errors.Trace(err) 513 } 514 oldValEkvMap, err := snapshot.BatchGet(ctx, removeKeys) 515 if err != nil { 516 logutil.Logger(ctx).Warn("amend failed to batch get ekv old keys", zap.Error(err)) 517 return nil, errors.Trace(err) 518 } 519 520 res := &rowEkvMap{ 521 oldRowEkvMap: oldValEkvMap, 522 newRowEkvMap: newValEkvMap, 523 } 524 return res, nil 525 } 526 527 // genAllAmendMutations generates CommitterMutations for all blocks and related amend operations. 528 func (s *SchemaAmender) genAllAmendMutations(ctx context.Context, commitMutations einsteindb.CommitterMutations, 529 info *amendDefCauslector) (*einsteindb.CommitterMutations, error) { 530 rowEkvMap, err := s.prepareEkvMap(ctx, commitMutations, info) 531 if err != nil { 532 return nil, err 533 } 534 // Do generate add/remove mutations processing each key. 535 resultNewMutations := einsteindb.NewCommiterMutations(32) 536 for _, amendOps := range info.tblAmendOpMap { 537 for _, curOp := range amendOps { 538 err := curOp.genMutations(ctx, s.sess, commitMutations, rowEkvMap, &resultNewMutations) 539 if err != nil { 540 return nil, err 541 } 542 } 543 } 544 return &resultNewMutations, nil 545 } 546 547 // AmendTxn does check and generate amend mutations based on input schemaReplicant and mutations, mutations need to prewrite 548 // are returned, the input commitMutations will not be changed. 549 func (s *SchemaAmender) AmendTxn(ctx context.Context, startSchemaReplicant einsteindb.SchemaVer, change *einsteindb.RelatedSchemaChange, 550 commitMutations einsteindb.CommitterMutations) (*einsteindb.CommitterMutations, error) { 551 // Get info schemaReplicant spacetime 552 schemaReplicantAtStart := startSchemaReplicant.(schemareplicant.SchemaReplicant) 553 schemaReplicantAtCheck := change.LatestSchemaReplicant.(schemareplicant.SchemaReplicant) 554 555 // DefCauslect amend operations for each causet by physical causet ID. 556 var needAmendMem bool 557 amendDefCauslector := newAmendDefCauslector() 558 for i, tblID := range change.PhyTblIDS { 559 actionType := change.CausetActionTypes[i] 560 // Check amendable flags, return if not supported flags exist. 561 if actionType&(^amendableType) != 0 { 562 logutil.Logger(ctx).Info("amend action type not supported for txn", zap.Int64("tblID", tblID), zap.Uint64("actionType", actionType)) 563 return nil, errors.Trace(causet.ErrUnsupportedOp) 564 } 565 // Partition causet is not supported now. 566 tblInfoAtStart, ok := schemaReplicantAtStart.TableByID(tblID) 567 if !ok { 568 return nil, errors.Trace(errors.Errorf("blockID=%d is not found in schemaReplicant", tblID)) 569 } 570 if tblInfoAtStart.Meta().Partition != nil { 571 logutil.Logger(ctx).Info("Amend for partition causet is not supported", 572 zap.String("blockName", tblInfoAtStart.Meta().Name.String()), zap.Int64("blockID", tblID)) 573 return nil, errors.Trace(causet.ErrUnsupportedOp) 574 } 575 tblInfoAtCommit, ok := schemaReplicantAtCheck.TableByID(tblID) 576 if !ok { 577 return nil, errors.Trace(errors.Errorf("blockID=%d is not found in schemaReplicant", tblID)) 578 } 579 if actionType&(memBufAmendType) != 0 { 580 needAmendMem = true 581 err := amendDefCauslector.collectTblAmendOps(s.sess, tblID, tblInfoAtStart, tblInfoAtCommit, actionType) 582 if err != nil { 583 return nil, err 584 } 585 } 586 } 587 // After amend operations collect, generate related new mutations based on input commitMutations 588 if needAmendMem { 589 return s.genAllAmendMutations(ctx, commitMutations, amendDefCauslector) 590 } 591 return nil, nil 592 } 593 594 func newSchemaAndCausetDecoder(ctx stochastikctx.Context, tbl *perceptron.TableInfo) *schemaAndCausetDecoder { 595 schemaReplicant := memex.NewSchema(make([]*memex.DeferredCauset, 0, len(tbl.DeferredCausets))...) 596 for _, col := range tbl.DeferredCausets { 597 colExpr := &memex.DeferredCauset{ 598 RetType: &col.FieldType, 599 ID: col.ID, 600 } 601 if col.IsGenerated() && !col.GeneratedStored { 602 // This will not be used since generated column is rejected in collectIndexAmendOps. 603 colExpr.VirtualExpr = &memex.Constant{} 604 } 605 schemaReplicant.Append(colExpr) 606 } 607 return &schemaAndCausetDecoder{schemaReplicant, interlock.NewRowCausetDecoder(ctx, schemaReplicant, tbl)} 608 }