github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/replace.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/errors" 23 "github.com/whtcorpsinc/milevadb/blockcodec" 24 "github.com/whtcorpsinc/milevadb/ekv" 25 "github.com/whtcorpsinc/milevadb/soliton/chunk" 26 "github.com/whtcorpsinc/milevadb/soliton/logutil" 27 "github.com/whtcorpsinc/milevadb/soliton/memory" 28 "github.com/whtcorpsinc/milevadb/types" 29 "go.uber.org/zap" 30 ) 31 32 // ReplaceInterDirc represents a replace interlock. 33 type ReplaceInterDirc struct { 34 *InsertValues 35 Priority int 36 } 37 38 // Close implements the InterlockingDirectorate Close interface. 39 func (e *ReplaceInterDirc) Close() error { 40 e.setMessage() 41 if e.SelectInterDirc != nil { 42 return e.SelectInterDirc.Close() 43 } 44 return nil 45 } 46 47 // Open implements the InterlockingDirectorate Open interface. 48 func (e *ReplaceInterDirc) Open(ctx context.Context) error { 49 e.memTracker = memory.NewTracker(e.id, -1) 50 e.memTracker.AttachTo(e.ctx.GetStochastikVars().StmtCtx.MemTracker) 51 52 if e.SelectInterDirc != nil { 53 return e.SelectInterDirc.Open(ctx) 54 } 55 e.initEvalBuffer() 56 return nil 57 } 58 59 // removeEvent removes the duplicate event and cleanup its keys in the key-value map, 60 // but if the to-be-removed event equals to the to-be-added event, no remove or add things to do. 61 func (e *ReplaceInterDirc) removeEvent(ctx context.Context, txn ekv.Transaction, handle ekv.Handle, r toBeCheckedEvent) (bool, error) { 62 newEvent := r.event 63 oldEvent, err := getOldEvent(ctx, e.ctx, txn, r.t, handle, e.GenExprs) 64 if err != nil { 65 logutil.BgLogger().Error("get old event failed when replace", 66 zap.String("handle", handle.String()), 67 zap.String("toBeInsertedEvent", types.CausetsToStrNoErr(r.event))) 68 if ekv.IsErrNotFound(err) { 69 err = errors.NotFoundf("can not be duplicated event, due to old event not found. handle %s", handle) 70 } 71 return false, err 72 } 73 74 rowUnchanged, err := types.EqualCausets(e.ctx.GetStochastikVars().StmtCtx, oldEvent, newEvent) 75 if err != nil { 76 return false, err 77 } 78 if rowUnchanged { 79 e.ctx.GetStochastikVars().StmtCtx.AddAffectedEvents(1) 80 return true, nil 81 } 82 83 err = r.t.RemoveRecord(e.ctx, handle, oldEvent) 84 if err != nil { 85 return false, err 86 } 87 e.ctx.GetStochastikVars().StmtCtx.AddAffectedEvents(1) 88 return false, nil 89 } 90 91 // replaceEvent removes all duplicate rows for one event, then inserts it. 92 func (e *ReplaceInterDirc) replaceEvent(ctx context.Context, r toBeCheckedEvent) error { 93 txn, err := e.ctx.Txn(true) 94 if err != nil { 95 return err 96 } 97 98 if r.handleKey != nil { 99 handle, err := blockcodec.DecodeEventKey(r.handleKey.newKey) 100 if err != nil { 101 return err 102 } 103 104 if _, err := txn.Get(ctx, r.handleKey.newKey); err == nil { 105 rowUnchanged, err := e.removeEvent(ctx, txn, handle, r) 106 if err != nil { 107 return err 108 } 109 if rowUnchanged { 110 return nil 111 } 112 } else { 113 if !ekv.IsErrNotFound(err) { 114 return err 115 } 116 } 117 } 118 119 // Keep on removing duplicated rows. 120 for { 121 rowUnchanged, foundDupKey, err := e.removeIndexEvent(ctx, txn, r) 122 if err != nil { 123 return err 124 } 125 if rowUnchanged { 126 return nil 127 } 128 if foundDupKey { 129 continue 130 } 131 break 132 } 133 134 // No duplicated rows now, insert the event. 135 err = e.addRecord(ctx, r.event) 136 if err != nil { 137 return err 138 } 139 return nil 140 } 141 142 // removeIndexEvent removes the event which has a duplicated key. 143 // the return values: 144 // 1. bool: true when the event is unchanged. This means no need to remove, and then add the event. 145 // 2. bool: true when found the duplicated key. This only means that duplicated key was found, 146 // and the event was removed. 147 // 3. error: the error. 148 func (e *ReplaceInterDirc) removeIndexEvent(ctx context.Context, txn ekv.Transaction, r toBeCheckedEvent) (bool, bool, error) { 149 for _, uk := range r.uniqueKeys { 150 val, err := txn.Get(ctx, uk.newKey) 151 if err != nil { 152 if ekv.IsErrNotFound(err) { 153 continue 154 } 155 return false, false, err 156 } 157 handle, err := blockcodec.DecodeHandleInUniqueIndexValue(val, uk.commonHandle) 158 if err != nil { 159 return false, true, err 160 } 161 rowUnchanged, err := e.removeEvent(ctx, txn, handle, r) 162 if err != nil { 163 return false, true, err 164 } 165 return rowUnchanged, true, nil 166 } 167 return false, false, nil 168 } 169 170 func (e *ReplaceInterDirc) exec(ctx context.Context, newEvents [][]types.Causet) error { 171 /* 172 * MyALLEGROSQL uses the following algorithm for REPLACE (and LOAD DATA ... REPLACE): 173 * 1. Try to insert the new event into the causet 174 * 2. While the insertion fails because a duplicate-key error occurs for a primary key or unique index: 175 * 3. Delete from the causet the conflicting event that has the duplicate key value 176 * 4. Try again to insert the new event into the causet 177 * See http://dev.allegrosql.com/doc/refman/5.7/en/replace.html 178 * 179 * For REPLACE memexs, the affected-rows value is 2 if the new event replaced an old event, 180 * because in this case, one event was inserted after the duplicate was deleted. 181 * See http://dev.allegrosql.com/doc/refman/5.7/en/allegrosql-affected-rows.html 182 */ 183 184 defer trace.StartRegion(ctx, "ReplaceInterDirc").End() 185 // Get keys need to be checked. 186 toBeCheckedEvents, err := getKeysNeedCheck(ctx, e.ctx, e.Block, newEvents) 187 if err != nil { 188 return err 189 } 190 191 txn, err := e.ctx.Txn(true) 192 if err != nil { 193 return err 194 } 195 txnSize := txn.Size() 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 e.ctx.GetStochastikVars().StmtCtx.AddRecordEvents(uint64(len(newEvents))) 211 for _, r := range toBeCheckedEvents { 212 err = e.replaceEvent(ctx, r) 213 if err != nil { 214 return err 215 } 216 } 217 e.memTracker.Consume(int64(txn.Size() - txnSize)) 218 return nil 219 } 220 221 // Next implements the InterlockingDirectorate Next interface. 222 func (e *ReplaceInterDirc) Next(ctx context.Context, req *chunk.Chunk) error { 223 req.Reset() 224 if len(e.children) > 0 && e.children[0] != nil { 225 return insertEventsFromSelect(ctx, e) 226 } 227 return insertEvents(ctx, e) 228 } 229 230 // setMessage sets info message(ERR_INSERT_INFO) generated by REPLACE memex 231 func (e *ReplaceInterDirc) setMessage() { 232 stmtCtx := e.ctx.GetStochastikVars().StmtCtx 233 numRecords := stmtCtx.RecordEvents() 234 if e.SelectInterDirc != nil || numRecords > 1 { 235 numWarnings := stmtCtx.WarningCount() 236 numDuplicates := stmtCtx.AffectedEvents() - numRecords 237 msg := fmt.Sprintf(allegrosql.MyALLEGROSQLErrName[allegrosql.ErrInsertInfo], numRecords, numDuplicates, numWarnings) 238 stmtCtx.SetMessage(msg) 239 } 240 }