github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/update.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  	"fmt"
    19  	"runtime/trace"
    20  
    21  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    22  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    23  	"github.com/whtcorpsinc/milevadb/causet"
    24  	causetembedded "github.com/whtcorpsinc/milevadb/causet/embedded"
    25  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb"
    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/memory"
    30  	"github.com/whtcorpsinc/milevadb/types"
    31  )
    32  
    33  // UFIDelateInterDirc represents a new uFIDelate interlock.
    34  type UFIDelateInterDirc struct {
    35  	baseInterlockingDirectorate
    36  
    37  	OrderedList []*memex.Assignment
    38  
    39  	// uFIDelatedEventKeys is a map for unique (Block, handle) pair.
    40  	// The value is true if the event is changed, or false otherwise
    41  	uFIDelatedEventKeys map[int64]*ekv.HandleMap
    42  	tblID2block         map[int64]causet.Block
    43  
    44  	matched uint64 // a counter of matched rows during uFIDelate
    45  	// tblDefCausPosInfos stores relationship between defCausumn ordinal to its causet handle.
    46  	// the defCausumns ordinals is present in ordinal range format, @see causetembedded.TblDefCausPosInfos
    47  	tblDefCausPosInfos        causetembedded.TblDefCausPosInfoSlice
    48  	evalBuffer                chunk.MutEvent
    49  	allAssignmentsAreConstant bool
    50  	drained                   bool
    51  	memTracker                *memory.Tracker
    52  
    53  	stats *runtimeStatsWithSnapshot
    54  }
    55  
    56  func (e *UFIDelateInterDirc) exec(ctx context.Context, schemaReplicant *memex.Schema, event, newData []types.Causet) error {
    57  	defer trace.StartRegion(ctx, "UFIDelateInterDirc").End()
    58  	assignFlag, err := causetembedded.GetUFIDelateDeferredCausets(e.ctx, e.OrderedList, schemaReplicant.Len())
    59  	if err != nil {
    60  		return err
    61  	}
    62  	if e.uFIDelatedEventKeys == nil {
    63  		e.uFIDelatedEventKeys = make(map[int64]*ekv.HandleMap)
    64  	}
    65  	for _, content := range e.tblDefCausPosInfos {
    66  		tbl := e.tblID2block[content.TblID]
    67  		if e.uFIDelatedEventKeys[content.TblID] == nil {
    68  			e.uFIDelatedEventKeys[content.TblID] = ekv.NewHandleMap()
    69  		}
    70  		var handle ekv.Handle
    71  		handle, err = content.HandleDefCauss.BuildHandleByCausets(event)
    72  		if err != nil {
    73  			return err
    74  		}
    75  
    76  		oldData := event[content.Start:content.End]
    77  		newBlockData := newData[content.Start:content.End]
    78  		uFIDelablock := false
    79  		flags := assignFlag[content.Start:content.End]
    80  		for _, flag := range flags {
    81  			if flag {
    82  				uFIDelablock = true
    83  				break
    84  			}
    85  		}
    86  		if !uFIDelablock {
    87  			// If there's nothing to uFIDelate, we can just skip current event
    88  			continue
    89  		}
    90  		var changed bool
    91  		v, ok := e.uFIDelatedEventKeys[content.TblID].Get(handle)
    92  		if !ok {
    93  			// Event is matched for the first time, increment `matched` counter
    94  			e.matched++
    95  		} else {
    96  			changed = v.(bool)
    97  		}
    98  		if changed {
    99  			// Each matched event is uFIDelated once, even if it matches the conditions multiple times.
   100  			continue
   101  		}
   102  
   103  		// UFIDelate event
   104  		changed, err1 := uFIDelateRecord(ctx, e.ctx, handle, oldData, newBlockData, flags, tbl, false, e.memTracker)
   105  		if err1 == nil {
   106  			e.uFIDelatedEventKeys[content.TblID].Set(handle, changed)
   107  			continue
   108  		}
   109  
   110  		sc := e.ctx.GetStochastikVars().StmtCtx
   111  		if ekv.ErrKeyExists.Equal(err1) && sc.DupKeyAsWarning {
   112  			sc.AppendWarning(err1)
   113  			continue
   114  		}
   115  		return err1
   116  	}
   117  	return nil
   118  }
   119  
   120  // canNotUFIDelate checks the handle of a record to decide whether that record
   121  // can not be uFIDelated. The handle is NULL only when it is the inner side of an
   122  // outer join: the outer event can not match any inner rows, and in this scenario
   123  // the inner handle field is filled with a NULL value.
   124  //
   125  // This fixes: https://github.com/whtcorpsinc/milevadb/issues/7176.
   126  func (e *UFIDelateInterDirc) canNotUFIDelate(handle types.Causet) bool {
   127  	return handle.IsNull()
   128  }
   129  
   130  // Next implements the InterlockingDirectorate Next interface.
   131  func (e *UFIDelateInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
   132  	req.Reset()
   133  	if !e.drained {
   134  		numEvents, err := e.uFIDelateEvents(ctx)
   135  		if err != nil {
   136  			return err
   137  		}
   138  		e.drained = true
   139  		e.ctx.GetStochastikVars().StmtCtx.AddRecordEvents(uint64(numEvents))
   140  	}
   141  	return nil
   142  }
   143  
   144  func (e *UFIDelateInterDirc) uFIDelateEvents(ctx context.Context) (int, error) {
   145  	fields := retTypes(e.children[0])
   146  	defcausInfo := make([]*causet.DeferredCauset, len(fields))
   147  	for _, content := range e.tblDefCausPosInfos {
   148  		tbl := e.tblID2block[content.TblID]
   149  		for i, c := range tbl.WriblockDefCauss() {
   150  			defcausInfo[content.Start+i] = c
   151  		}
   152  	}
   153  	globalEventIdx := 0
   154  	chk := newFirstChunk(e.children[0])
   155  	if !e.allAssignmentsAreConstant {
   156  		e.evalBuffer = chunk.MutEventFromTypes(fields)
   157  	}
   158  	composeFunc := e.fastComposeNewEvent
   159  	if !e.allAssignmentsAreConstant {
   160  		composeFunc = e.composeNewEvent
   161  	}
   162  	memUsageOfChk := int64(0)
   163  	totalNumEvents := 0
   164  	for {
   165  		e.memTracker.Consume(-memUsageOfChk)
   166  		err := Next(ctx, e.children[0], chk)
   167  		if err != nil {
   168  			return 0, err
   169  		}
   170  
   171  		if chk.NumEvents() == 0 {
   172  			break
   173  		}
   174  		memUsageOfChk = chk.MemoryUsage()
   175  		e.memTracker.Consume(memUsageOfChk)
   176  		if e.defCauslectRuntimeStatsEnabled() {
   177  			txn, err := e.ctx.Txn(false)
   178  			if err == nil && txn.GetSnapshot() != nil {
   179  				txn.GetSnapshot().SetOption(ekv.DefCauslectRuntimeStats, e.stats.SnapshotRuntimeStats)
   180  			}
   181  		}
   182  		for rowIdx := 0; rowIdx < chk.NumEvents(); rowIdx++ {
   183  			chunkEvent := chk.GetEvent(rowIdx)
   184  			datumEvent := chunkEvent.GetCausetEvent(fields)
   185  			newEvent, err1 := composeFunc(globalEventIdx, datumEvent, defcausInfo)
   186  			if err1 != nil {
   187  				return 0, err1
   188  			}
   189  			if err := e.exec(ctx, e.children[0].Schema(), datumEvent, newEvent); err != nil {
   190  				return 0, err
   191  			}
   192  		}
   193  		totalNumEvents += chk.NumEvents()
   194  		chk = chunk.Renew(chk, e.maxChunkSize)
   195  	}
   196  	return totalNumEvents, nil
   197  }
   198  
   199  func (e *UFIDelateInterDirc) handleErr(defCausName perceptron.CIStr, rowIdx int, err error) error {
   200  	if err == nil {
   201  		return nil
   202  	}
   203  
   204  	if types.ErrDataTooLong.Equal(err) {
   205  		return resetErrDataTooLong(defCausName.O, rowIdx+1, err)
   206  	}
   207  
   208  	if types.ErrOverflow.Equal(err) {
   209  		return types.ErrWarnDataOutOfRange.GenWithStackByArgs(defCausName.O, rowIdx+1)
   210  	}
   211  
   212  	return err
   213  }
   214  
   215  func (e *UFIDelateInterDirc) fastComposeNewEvent(rowIdx int, oldEvent []types.Causet, defcaus []*causet.DeferredCauset) ([]types.Causet, error) {
   216  	newEventData := types.CloneEvent(oldEvent)
   217  	for _, assign := range e.OrderedList {
   218  		handleIdx, handleFound := e.tblDefCausPosInfos.FindHandle(assign.DefCaus.Index)
   219  		if handleFound && e.canNotUFIDelate(oldEvent[handleIdx]) {
   220  			continue
   221  		}
   222  
   223  		con := assign.Expr.(*memex.Constant)
   224  		val, err := con.Eval(emptyEvent)
   225  		if err = e.handleErr(assign.DefCausName, rowIdx, err); err != nil {
   226  			return nil, err
   227  		}
   228  
   229  		// info of `_milevadb_rowid` defCausumn is nil.
   230  		// No need to cast `_milevadb_rowid` defCausumn value.
   231  		if defcaus[assign.DefCaus.Index] != nil {
   232  			val, err = causet.CastValue(e.ctx, val, defcaus[assign.DefCaus.Index].DeferredCausetInfo, false, false)
   233  			if err = e.handleErr(assign.DefCausName, rowIdx, err); err != nil {
   234  				return nil, err
   235  			}
   236  		}
   237  
   238  		val.Copy(&newEventData[assign.DefCaus.Index])
   239  	}
   240  	return newEventData, nil
   241  }
   242  
   243  func (e *UFIDelateInterDirc) composeNewEvent(rowIdx int, oldEvent []types.Causet, defcaus []*causet.DeferredCauset) ([]types.Causet, error) {
   244  	newEventData := types.CloneEvent(oldEvent)
   245  	e.evalBuffer.SetCausets(newEventData...)
   246  	for _, assign := range e.OrderedList {
   247  		handleIdx, handleFound := e.tblDefCausPosInfos.FindHandle(assign.DefCaus.Index)
   248  		if handleFound && e.canNotUFIDelate(oldEvent[handleIdx]) {
   249  			continue
   250  		}
   251  		val, err := assign.Expr.Eval(e.evalBuffer.ToEvent())
   252  		if err = e.handleErr(assign.DefCausName, rowIdx, err); err != nil {
   253  			return nil, err
   254  		}
   255  
   256  		// info of `_milevadb_rowid` defCausumn is nil.
   257  		// No need to cast `_milevadb_rowid` defCausumn value.
   258  		if defcaus[assign.DefCaus.Index] != nil {
   259  			val, err = causet.CastValue(e.ctx, val, defcaus[assign.DefCaus.Index].DeferredCausetInfo, false, false)
   260  			if err = e.handleErr(assign.DefCausName, rowIdx, err); err != nil {
   261  				return nil, err
   262  			}
   263  		}
   264  
   265  		val.Copy(&newEventData[assign.DefCaus.Index])
   266  		e.evalBuffer.SetCauset(assign.DefCaus.Index, val)
   267  	}
   268  	return newEventData, nil
   269  }
   270  
   271  // Close implements the InterlockingDirectorate Close interface.
   272  func (e *UFIDelateInterDirc) Close() error {
   273  	e.setMessage()
   274  	if e.runtimeStats != nil && e.stats != nil {
   275  		txn, err := e.ctx.Txn(false)
   276  		if err == nil && txn.GetSnapshot() != nil {
   277  			txn.GetSnapshot().DelOption(ekv.DefCauslectRuntimeStats)
   278  		}
   279  	}
   280  	return e.children[0].Close()
   281  }
   282  
   283  // Open implements the InterlockingDirectorate Open interface.
   284  func (e *UFIDelateInterDirc) Open(ctx context.Context) error {
   285  	e.memTracker = memory.NewTracker(e.id, -1)
   286  	e.memTracker.AttachTo(e.ctx.GetStochastikVars().StmtCtx.MemTracker)
   287  
   288  	return e.children[0].Open(ctx)
   289  }
   290  
   291  // setMessage sets info message(ERR_UFIDelATE_INFO) generated by UFIDelATE memex
   292  func (e *UFIDelateInterDirc) setMessage() {
   293  	stmtCtx := e.ctx.GetStochastikVars().StmtCtx
   294  	numMatched := e.matched
   295  	numChanged := stmtCtx.UFIDelatedEvents()
   296  	numWarnings := stmtCtx.WarningCount()
   297  	msg := fmt.Sprintf(allegrosql.MyALLEGROSQLErrName[allegrosql.ErrUFIDelateInfo], numMatched, numChanged, numWarnings)
   298  	stmtCtx.SetMessage(msg)
   299  }
   300  
   301  func (e *UFIDelateInterDirc) defCauslectRuntimeStatsEnabled() bool {
   302  	if e.runtimeStats != nil {
   303  		if e.stats == nil {
   304  			snapshotStats := &einsteindb.SnapshotRuntimeStats{}
   305  			e.stats = &runtimeStatsWithSnapshot{
   306  				SnapshotRuntimeStats: snapshotStats,
   307  			}
   308  			e.ctx.GetStochastikVars().StmtCtx.RuntimeStatsDefCausl.RegisterStats(e.id, e.stats)
   309  		}
   310  		return true
   311  	}
   312  	return false
   313  }