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 }