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  }