github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/insert.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  	"encoding/hex"
    19  	"fmt"
    20  	"runtime/trace"
    21  
    22  	"github.com/opentracing/opentracing-go"
    23  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    24  	"github.com/whtcorpsinc/milevadb/blockcodec"
    25  	"github.com/whtcorpsinc/milevadb/causet"
    26  	"github.com/whtcorpsinc/milevadb/ekv"
    27  	"github.com/whtcorpsinc/milevadb/memex"
    28  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    29  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    30  	"github.com/whtcorpsinc/milevadb/soliton/memory"
    31  	"github.com/whtcorpsinc/milevadb/soliton/stringutil"
    32  	"github.com/whtcorpsinc/milevadb/types"
    33  	"go.uber.org/zap"
    34  )
    35  
    36  // InsertInterDirc represents an insert interlock.
    37  type InsertInterDirc struct {
    38  	*InsertValues
    39  	OnDuplicate    []*memex.Assignment
    40  	evalBuffer4Dup chunk.MutEvent
    41  	curInsertVals  chunk.MutEvent
    42  	row4UFIDelate  []types.Causet
    43  
    44  	Priority allegrosql.PriorityEnum
    45  }
    46  
    47  func (e *InsertInterDirc) exec(ctx context.Context, rows [][]types.Causet) error {
    48  	defer trace.StartRegion(ctx, "InsertInterDirc").End()
    49  	logutil.Eventf(ctx, "insert %d rows into causet `%s`", len(rows), stringutil.MemoizeStr(func() string {
    50  		var tblName string
    51  		if spacetime := e.Block.Meta(); spacetime != nil {
    52  			tblName = spacetime.Name.L
    53  		}
    54  		return tblName
    55  	}))
    56  	// If milevadb_batch_insert is ON and not in a transaction, we could use BatchInsert mode.
    57  	sessVars := e.ctx.GetStochastikVars()
    58  	defer sessVars.CleanBuffers()
    59  	ignoreErr := sessVars.StmtCtx.DupKeyAsWarning
    60  
    61  	txn, err := e.ctx.Txn(true)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	txnSize := txn.Size()
    66  	sessVars.StmtCtx.AddRecordEvents(uint64(len(rows)))
    67  	// If you use the IGNORE keyword, duplicate-key error that occurs while executing the INSERT memex are ignored.
    68  	// For example, without IGNORE, a event that duplicates an existing UNIQUE index or PRIMARY KEY value in
    69  	// the causet causes a duplicate-key error and the memex is aborted. With IGNORE, the event is discarded and no error occurs.
    70  	// However, if the `on duplicate uFIDelate` is also specified, the duplicated event will be uFIDelated.
    71  	// Using BatchGet in insert ignore to mark rows as duplicated before we add records to the causet.
    72  	// If `ON DUPLICATE KEY UFIDelATE` is specified, and no `IGNORE` keyword,
    73  	// the to-be-insert rows will be check on duplicate keys and uFIDelate to the new rows.
    74  	if len(e.OnDuplicate) > 0 {
    75  		err := e.batchUFIDelateDupEvents(ctx, rows)
    76  		if err != nil {
    77  			return err
    78  		}
    79  	} else if ignoreErr {
    80  		err := e.batchCheckAndInsert(ctx, rows, e.addRecord)
    81  		if err != nil {
    82  			return err
    83  		}
    84  	} else {
    85  		for i, event := range rows {
    86  			var err error
    87  			sizeHintStep := int(sessVars.ShardAllocateStep)
    88  			if i%sizeHintStep == 0 {
    89  				sizeHint := sizeHintStep
    90  				remain := len(rows) - i
    91  				if sizeHint > remain {
    92  					sizeHint = remain
    93  				}
    94  				err = e.addRecordWithAutoIDHint(ctx, event, sizeHint)
    95  			} else {
    96  				err = e.addRecord(ctx, event)
    97  			}
    98  			if err != nil {
    99  				return err
   100  			}
   101  		}
   102  	}
   103  	e.memTracker.Consume(int64(txn.Size() - txnSize))
   104  	return nil
   105  }
   106  
   107  func prefetchUniqueIndices(ctx context.Context, txn ekv.Transaction, rows []toBeCheckedEvent) (map[string][]byte, error) {
   108  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
   109  		span1 := span.Tracer().StartSpan("prefetchUniqueIndices", opentracing.ChildOf(span.Context()))
   110  		defer span1.Finish()
   111  		ctx = opentracing.ContextWithSpan(ctx, span1)
   112  	}
   113  
   114  	nKeys := 0
   115  	for _, r := range rows {
   116  		if r.handleKey != nil {
   117  			nKeys++
   118  		}
   119  		nKeys += len(r.uniqueKeys)
   120  	}
   121  	batchKeys := make([]ekv.Key, 0, nKeys)
   122  	for _, r := range rows {
   123  		if r.handleKey != nil {
   124  			batchKeys = append(batchKeys, r.handleKey.newKey)
   125  		}
   126  		for _, k := range r.uniqueKeys {
   127  			batchKeys = append(batchKeys, k.newKey)
   128  		}
   129  	}
   130  	return txn.BatchGet(ctx, batchKeys)
   131  }
   132  
   133  func prefetchConflictedOldEvents(ctx context.Context, txn ekv.Transaction, rows []toBeCheckedEvent, values map[string][]byte) error {
   134  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
   135  		span1 := span.Tracer().StartSpan("prefetchConflictedOldEvents", opentracing.ChildOf(span.Context()))
   136  		defer span1.Finish()
   137  		ctx = opentracing.ContextWithSpan(ctx, span1)
   138  	}
   139  
   140  	batchKeys := make([]ekv.Key, 0, len(rows))
   141  	for _, r := range rows {
   142  		for _, uk := range r.uniqueKeys {
   143  			if val, found := values[string(uk.newKey)]; found {
   144  				handle, err := blockcodec.DecodeHandleInUniqueIndexValue(val, uk.commonHandle)
   145  				if err != nil {
   146  					return err
   147  				}
   148  				batchKeys = append(batchKeys, r.t.RecordKey(handle))
   149  			}
   150  		}
   151  	}
   152  	_, err := txn.BatchGet(ctx, batchKeys)
   153  	return err
   154  }
   155  
   156  func prefetchDataCache(ctx context.Context, txn ekv.Transaction, rows []toBeCheckedEvent) error {
   157  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
   158  		span1 := span.Tracer().StartSpan("prefetchDataCache", opentracing.ChildOf(span.Context()))
   159  		defer span1.Finish()
   160  		ctx = opentracing.ContextWithSpan(ctx, span1)
   161  	}
   162  	values, err := prefetchUniqueIndices(ctx, txn, rows)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	return prefetchConflictedOldEvents(ctx, txn, rows, values)
   167  }
   168  
   169  // uFIDelateDupEvent uFIDelates a duplicate event to a new event.
   170  func (e *InsertInterDirc) uFIDelateDupEvent(ctx context.Context, txn ekv.Transaction, event toBeCheckedEvent, handle ekv.Handle, onDuplicate []*memex.Assignment) error {
   171  	oldEvent, err := getOldEvent(ctx, e.ctx, txn, event.t, handle, e.GenExprs)
   172  	if err != nil {
   173  		return err
   174  	}
   175  
   176  	err = e.doDupEventUFIDelate(ctx, handle, oldEvent, event.event, e.OnDuplicate)
   177  	if e.ctx.GetStochastikVars().StmtCtx.DupKeyAsWarning && ekv.ErrKeyExists.Equal(err) {
   178  		e.ctx.GetStochastikVars().StmtCtx.AppendWarning(err)
   179  		return nil
   180  	}
   181  	return err
   182  }
   183  
   184  // batchUFIDelateDupEvents uFIDelates multi-rows in batch if they are duplicate with rows in causet.
   185  func (e *InsertInterDirc) batchUFIDelateDupEvents(ctx context.Context, newEvents [][]types.Causet) error {
   186  	// Get keys need to be checked.
   187  	toBeCheckedEvents, err := getKeysNeedCheck(ctx, e.ctx, e.Block, newEvents)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	txn, err := e.ctx.Txn(true)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	if e.defCauslectRuntimeStatsEnabled() {
   198  		if snapshot := txn.GetSnapshot(); snapshot != nil {
   199  			snapshot.SetOption(ekv.DefCauslectRuntimeStats, e.stats.SnapshotRuntimeStats)
   200  			defer snapshot.DelOption(ekv.DefCauslectRuntimeStats)
   201  		}
   202  	}
   203  
   204  	// Use BatchGet to fill cache.
   205  	// It's an optimization and could be removed without affecting correctness.
   206  	if err = prefetchDataCache(ctx, txn, toBeCheckedEvents); err != nil {
   207  		return err
   208  	}
   209  
   210  	for i, r := range toBeCheckedEvents {
   211  		if r.handleKey != nil {
   212  			handle, err := blockcodec.DecodeEventKey(r.handleKey.newKey)
   213  			if err != nil {
   214  				return err
   215  			}
   216  
   217  			err = e.uFIDelateDupEvent(ctx, txn, r, handle, e.OnDuplicate)
   218  			if err == nil {
   219  				continue
   220  			}
   221  			if !ekv.IsErrNotFound(err) {
   222  				return err
   223  			}
   224  		}
   225  
   226  		for _, uk := range r.uniqueKeys {
   227  			val, err := txn.Get(ctx, uk.newKey)
   228  			if err != nil {
   229  				if ekv.IsErrNotFound(err) {
   230  					continue
   231  				}
   232  				return err
   233  			}
   234  			handle, err := blockcodec.DecodeHandleInUniqueIndexValue(val, uk.commonHandle)
   235  			if err != nil {
   236  				return err
   237  			}
   238  
   239  			err = e.uFIDelateDupEvent(ctx, txn, r, handle, e.OnDuplicate)
   240  			if err != nil {
   241  				if ekv.IsErrNotFound(err) {
   242  					// Data index inconsistent? A unique key provide the handle information, but the
   243  					// handle points to nothing.
   244  					logutil.BgLogger().Error("get old event failed when insert on dup",
   245  						zap.String("uniqueKey", hex.EncodeToString(uk.newKey)),
   246  						zap.Stringer("handle", handle),
   247  						zap.String("toBeInsertedEvent", types.CausetsToStrNoErr(r.event)))
   248  				}
   249  				return err
   250  			}
   251  
   252  			newEvents[i] = nil
   253  			break
   254  		}
   255  
   256  		// If event was checked with no duplicate keys,
   257  		// we should do insert the event,
   258  		// and key-values should be filled back to dupOldEventValues for the further event check,
   259  		// due to there may be duplicate keys inside the insert memex.
   260  		if newEvents[i] != nil {
   261  			err := e.addRecord(ctx, newEvents[i])
   262  			if err != nil {
   263  				return err
   264  			}
   265  		}
   266  	}
   267  	return nil
   268  }
   269  
   270  // Next implements the InterlockingDirectorate Next interface.
   271  func (e *InsertInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
   272  	req.Reset()
   273  	if len(e.children) > 0 && e.children[0] != nil {
   274  		return insertEventsFromSelect(ctx, e)
   275  	}
   276  	return insertEvents(ctx, e)
   277  }
   278  
   279  // Close implements the InterlockingDirectorate Close interface.
   280  func (e *InsertInterDirc) Close() error {
   281  	e.ctx.GetStochastikVars().CurrInsertValues = chunk.Event{}
   282  	e.setMessage()
   283  	if e.SelectInterDirc != nil {
   284  		return e.SelectInterDirc.Close()
   285  	}
   286  	return nil
   287  }
   288  
   289  // Open implements the InterlockingDirectorate Open interface.
   290  func (e *InsertInterDirc) Open(ctx context.Context) error {
   291  	e.memTracker = memory.NewTracker(e.id, -1)
   292  	e.memTracker.AttachTo(e.ctx.GetStochastikVars().StmtCtx.MemTracker)
   293  
   294  	if e.OnDuplicate != nil {
   295  		e.initEvalBuffer4Dup()
   296  	}
   297  	if e.SelectInterDirc != nil {
   298  		return e.SelectInterDirc.Open(ctx)
   299  	}
   300  	if !e.allAssignmentsAreConstant {
   301  		e.initEvalBuffer()
   302  	}
   303  	return nil
   304  }
   305  
   306  func (e *InsertInterDirc) initEvalBuffer4Dup() {
   307  	// Use public defCausumns for new event.
   308  	numDefCauss := len(e.Block.DefCauss())
   309  	// Use wriblock defCausumns for old event for uFIDelate.
   310  	numWriblockDefCauss := len(e.Block.WriblockDefCauss())
   311  
   312  	evalBufferTypes := make([]*types.FieldType, 0, numDefCauss+numWriblockDefCauss)
   313  
   314  	// Append the old event before the new event, to be consistent with "Schema4OnDuplicate" in the "Insert" PhysicalCauset.
   315  	for _, defCaus := range e.Block.WriblockDefCauss() {
   316  		evalBufferTypes = append(evalBufferTypes, &defCaus.FieldType)
   317  	}
   318  	for _, defCaus := range e.Block.DefCauss() {
   319  		evalBufferTypes = append(evalBufferTypes, &defCaus.FieldType)
   320  	}
   321  	if e.hasExtraHandle {
   322  		evalBufferTypes = append(evalBufferTypes, types.NewFieldType(allegrosql.TypeLonglong))
   323  	}
   324  	e.evalBuffer4Dup = chunk.MutEventFromTypes(evalBufferTypes)
   325  	e.curInsertVals = chunk.MutEventFromTypes(evalBufferTypes[numWriblockDefCauss:])
   326  	e.row4UFIDelate = make([]types.Causet, 0, len(evalBufferTypes))
   327  }
   328  
   329  // doDupEventUFIDelate uFIDelates the duplicate event.
   330  func (e *InsertInterDirc) doDupEventUFIDelate(ctx context.Context, handle ekv.Handle, oldEvent []types.Causet, newEvent []types.Causet,
   331  	defcaus []*memex.Assignment) error {
   332  	assignFlag := make([]bool, len(e.Block.WriblockDefCauss()))
   333  	// See http://dev.allegrosql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_values
   334  	e.curInsertVals.SetCausets(newEvent...)
   335  	e.ctx.GetStochastikVars().CurrInsertValues = e.curInsertVals.ToEvent()
   336  
   337  	// NOTE: In order to execute the memex inside the defCausumn assignment,
   338  	// we have to put the value of "oldEvent" before "newEvent" in "row4UFIDelate" to
   339  	// be consistent with "Schema4OnDuplicate" in the "Insert" PhysicalCauset.
   340  	e.row4UFIDelate = e.row4UFIDelate[:0]
   341  	e.row4UFIDelate = append(e.row4UFIDelate, oldEvent...)
   342  	e.row4UFIDelate = append(e.row4UFIDelate, newEvent...)
   343  
   344  	// UFIDelate old event when the key is duplicated.
   345  	e.evalBuffer4Dup.SetCausets(e.row4UFIDelate...)
   346  	for _, defCaus := range defcaus {
   347  		val, err1 := defCaus.Expr.Eval(e.evalBuffer4Dup.ToEvent())
   348  		if err1 != nil {
   349  			return err1
   350  		}
   351  		e.row4UFIDelate[defCaus.DefCaus.Index], err1 = causet.CastValue(e.ctx, val, defCaus.DefCaus.ToInfo(), false, false)
   352  		if err1 != nil {
   353  			return err1
   354  		}
   355  		e.evalBuffer4Dup.SetCauset(defCaus.DefCaus.Index, e.row4UFIDelate[defCaus.DefCaus.Index])
   356  		assignFlag[defCaus.DefCaus.Index] = true
   357  	}
   358  
   359  	newData := e.row4UFIDelate[:len(oldEvent)]
   360  	_, err := uFIDelateRecord(ctx, e.ctx, handle, oldEvent, newData, assignFlag, e.Block, true, e.memTracker)
   361  	if err != nil {
   362  		return err
   363  	}
   364  	return nil
   365  }
   366  
   367  // setMessage sets info message(ERR_INSERT_INFO) generated by INSERT memex
   368  func (e *InsertInterDirc) setMessage() {
   369  	stmtCtx := e.ctx.GetStochastikVars().StmtCtx
   370  	numRecords := stmtCtx.RecordEvents()
   371  	if e.SelectInterDirc != nil || numRecords > 1 {
   372  		numWarnings := stmtCtx.WarningCount()
   373  		var numDuplicates uint64
   374  		if stmtCtx.DupKeyAsWarning {
   375  			// if ignoreErr
   376  			numDuplicates = numRecords - stmtCtx.CopiedEvents()
   377  		} else {
   378  			if e.ctx.GetStochastikVars().ClientCapability&allegrosql.ClientFoundEvents > 0 {
   379  				numDuplicates = stmtCtx.TouchedEvents()
   380  			} else {
   381  				numDuplicates = stmtCtx.UFIDelatedEvents()
   382  			}
   383  		}
   384  		msg := fmt.Sprintf(allegrosql.MyALLEGROSQLErrName[allegrosql.ErrInsertInfo], numRecords, numDuplicates, numWarnings)
   385  		stmtCtx.SetMessage(msg)
   386  	}
   387  }