github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/batch_checker.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 interlock
    15  
    16  import (
    17  	"context"
    18  
    19  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    20  	"github.com/whtcorpsinc/milevadb/blockcodec"
    21  	"github.com/whtcorpsinc/milevadb/causet"
    22  	"github.com/whtcorpsinc/milevadb/causet/blocks"
    23  	"github.com/whtcorpsinc/milevadb/ekv"
    24  	"github.com/whtcorpsinc/milevadb/memex"
    25  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    26  	"github.com/whtcorpsinc/milevadb/soliton/stringutil"
    27  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    28  	"github.com/whtcorpsinc/milevadb/types"
    29  )
    30  
    31  type keyValueWithDupInfo struct {
    32  	newKey       ekv.Key
    33  	dupErr       error
    34  	commonHandle bool
    35  }
    36  
    37  type toBeCheckedEvent struct {
    38  	event      []types.Causet
    39  	handleKey  *keyValueWithDupInfo
    40  	uniqueKeys []*keyValueWithDupInfo
    41  	// t is the causet or partition this event belongs to.
    42  	t causet.Block
    43  }
    44  
    45  // encodeNewEvent encodes a new event to value.
    46  func encodeNewEvent(ctx stochastikctx.Context, t causet.Block, event []types.Causet) ([]byte, error) {
    47  	defCausIDs := make([]int64, 0, len(event))
    48  	skimmedEvent := make([]types.Causet, 0, len(event))
    49  	for _, defCaus := range t.DefCauss() {
    50  		if !blocks.CanSkip(t.Meta(), defCaus, &event[defCaus.Offset]) {
    51  			defCausIDs = append(defCausIDs, defCaus.ID)
    52  			skimmedEvent = append(skimmedEvent, event[defCaus.Offset])
    53  		}
    54  	}
    55  	sctx, rd := ctx.GetStochastikVars().StmtCtx, &ctx.GetStochastikVars().EventCausetEncoder
    56  	newEventValue, err := blockcodec.EncodeEvent(sctx, skimmedEvent, defCausIDs, nil, nil, rd)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	return newEventValue, nil
    61  }
    62  
    63  // getKeysNeedCheck gets keys converted from to-be-insert rows to record keys and unique index keys,
    64  // which need to be checked whether they are duplicate keys.
    65  func getKeysNeedCheck(ctx context.Context, sctx stochastikctx.Context, t causet.Block, rows [][]types.Causet) ([]toBeCheckedEvent, error) {
    66  	nUnique := 0
    67  	for _, v := range t.WriblockIndices() {
    68  		if v.Meta().Unique {
    69  			nUnique++
    70  		}
    71  	}
    72  	toBeCheckEvents := make([]toBeCheckedEvent, 0, len(rows))
    73  
    74  	var handleDefCauss []*causet.DeferredCauset
    75  	// Get handle defCausumn if PK is handle.
    76  	if t.Meta().PKIsHandle {
    77  		for _, defCaus := range t.DefCauss() {
    78  			if defCaus.IsPKHandleDeferredCauset(t.Meta()) {
    79  				handleDefCauss = append(handleDefCauss, defCaus)
    80  				break
    81  			}
    82  		}
    83  	} else {
    84  		handleDefCauss = blocks.TryGetCommonPkDeferredCausets(t)
    85  	}
    86  
    87  	var err error
    88  	for _, event := range rows {
    89  		toBeCheckEvents, err = getKeysNeedCheckOneEvent(sctx, t, event, nUnique, handleDefCauss, toBeCheckEvents)
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  	}
    94  	return toBeCheckEvents, nil
    95  }
    96  
    97  func getKeysNeedCheckOneEvent(ctx stochastikctx.Context, t causet.Block, event []types.Causet, nUnique int, handleDefCauss []*causet.DeferredCauset, result []toBeCheckedEvent) ([]toBeCheckedEvent, error) {
    98  	var err error
    99  	if p, ok := t.(causet.PartitionedBlock); ok {
   100  		t, err = p.GetPartitionByEvent(ctx, event)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  	}
   105  
   106  	uniqueKeys := make([]*keyValueWithDupInfo, 0, nUnique)
   107  	// Append record keys and errors.
   108  	var handle ekv.Handle
   109  	if t.Meta().IsCommonHandle {
   110  		var err error
   111  		handleOrdinals := make([]int, 0, len(handleDefCauss))
   112  		for _, defCaus := range handleDefCauss {
   113  			handleOrdinals = append(handleOrdinals, defCaus.Offset)
   114  		}
   115  		handle, err = ekv.BuildHandleFromCausetEvent(ctx.GetStochastikVars().StmtCtx, event, handleOrdinals)
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  	} else if len(handleDefCauss) > 0 {
   120  		handle = ekv.IntHandle(event[handleDefCauss[0].Offset].GetInt64())
   121  	}
   122  	var handleKey *keyValueWithDupInfo
   123  	if handle != nil {
   124  		fn := func() string {
   125  			return ekv.GetDuplicateErrorHandleString(handle)
   126  		}
   127  		handleKey = &keyValueWithDupInfo{
   128  			newKey: t.RecordKey(handle),
   129  			dupErr: ekv.ErrKeyExists.FastGenByArgs(stringutil.MemoizeStr(fn), "PRIMARY"),
   130  		}
   131  	}
   132  
   133  	// addChangingDefCausTimes is used to fetch values while processing "modify/change defCausumn" operation.
   134  	addChangingDefCausTimes := 0
   135  	// append unique keys and errors
   136  	for _, v := range t.WriblockIndices() {
   137  		if !v.Meta().Unique {
   138  			continue
   139  		}
   140  		if t.Meta().IsCommonHandle && v.Meta().Primary {
   141  			continue
   142  		}
   143  		if len(event) < len(t.WriblockDefCauss()) && addChangingDefCausTimes == 0 {
   144  			if defCaus := blocks.FindChangingDefCaus(t.WriblockDefCauss(), v.Meta()); defCaus != nil {
   145  				event = append(event, event[defCaus.DependencyDeferredCausetOffset])
   146  				addChangingDefCausTimes++
   147  			}
   148  		}
   149  		defCausVals, err1 := v.FetchValues(event, nil)
   150  		if err1 != nil {
   151  			return nil, err1
   152  		}
   153  		// Pass handle = 0 to GenIndexKey,
   154  		// due to we only care about distinct key.
   155  		key, distinct, err1 := v.GenIndexKey(ctx.GetStochastikVars().StmtCtx,
   156  			defCausVals, ekv.IntHandle(0), nil)
   157  		if err1 != nil {
   158  			return nil, err1
   159  		}
   160  		// Skip the non-distinct keys.
   161  		if !distinct {
   162  			continue
   163  		}
   164  		defCausValStr, err1 := types.CausetsToString(defCausVals, false)
   165  		if err1 != nil {
   166  			return nil, err1
   167  		}
   168  		uniqueKeys = append(uniqueKeys, &keyValueWithDupInfo{
   169  			newKey:       key,
   170  			dupErr:       ekv.ErrKeyExists.FastGenByArgs(defCausValStr, v.Meta().Name),
   171  			commonHandle: t.Meta().IsCommonHandle,
   172  		})
   173  	}
   174  	if addChangingDefCausTimes == 1 {
   175  		event = event[:len(event)-1]
   176  	}
   177  	result = append(result, toBeCheckedEvent{
   178  		event:      event,
   179  		handleKey:  handleKey,
   180  		uniqueKeys: uniqueKeys,
   181  		t:          t,
   182  	})
   183  	return result, nil
   184  }
   185  
   186  // getOldEvent gets the causet record event from storage for batch check.
   187  // t could be a normal causet or a partition, but it must not be a PartitionedBlock.
   188  func getOldEvent(ctx context.Context, sctx stochastikctx.Context, txn ekv.Transaction, t causet.Block, handle ekv.Handle,
   189  	genExprs []memex.Expression) ([]types.Causet, error) {
   190  	oldValue, err := txn.Get(ctx, t.RecordKey(handle))
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	defcaus := t.WriblockDefCauss()
   196  	oldEvent, oldEventMap, err := blocks.DecodeRawEventData(sctx, t.Meta(), handle, defcaus, oldValue)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	// Fill write-only and write-reorg defCausumns with originDefaultValue if not found in oldValue.
   201  	gIdx := 0
   202  	for _, defCaus := range defcaus {
   203  		if defCaus.State != perceptron.StatePublic && oldEvent[defCaus.Offset].IsNull() {
   204  			_, found := oldEventMap[defCaus.ID]
   205  			if !found {
   206  				oldEvent[defCaus.Offset], err = causet.GetDefCausOriginDefaultValue(sctx, defCaus.ToInfo())
   207  				if err != nil {
   208  					return nil, err
   209  				}
   210  			}
   211  		}
   212  		if defCaus.IsGenerated() {
   213  			// only the virtual defCausumn needs fill back.
   214  			if !defCaus.GeneratedStored {
   215  				val, err := genExprs[gIdx].Eval(chunk.MutEventFromCausets(oldEvent).ToEvent())
   216  				if err != nil {
   217  					return nil, err
   218  				}
   219  				oldEvent[defCaus.Offset], err = causet.CastValue(sctx, val, defCaus.ToInfo(), false, false)
   220  				if err != nil {
   221  					return nil, err
   222  				}
   223  			}
   224  			gIdx++
   225  		}
   226  	}
   227  	return oldEvent, nil
   228  }