github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/tablewriter_delete.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package sql 12 13 import ( 14 "context" 15 "fmt" 16 17 "github.com/cockroachdb/cockroach/pkg/kv" 18 "github.com/cockroachdb/cockroach/pkg/roachpb" 19 "github.com/cockroachdb/cockroach/pkg/sql/row" 20 "github.com/cockroachdb/cockroach/pkg/sql/rowcontainer" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 23 "github.com/cockroachdb/cockroach/pkg/util" 24 "github.com/cockroachdb/cockroach/pkg/util/log" 25 "github.com/cockroachdb/errors" 26 ) 27 28 // tableDeleter handles writing kvs and forming table rows for deletes. 29 type tableDeleter struct { 30 tableWriterBase 31 32 rd row.Deleter 33 alloc *sqlbase.DatumAlloc 34 } 35 36 var _ tableWriter = &tableDeleter{} 37 38 // desc is part of the tableWriter interface. 39 func (*tableDeleter) desc() string { return "deleter" } 40 41 // walkExprs is part of the tableWriter interface. 42 func (td *tableDeleter) walkExprs(_ func(desc string, index int, expr tree.TypedExpr)) {} 43 44 // init is part of the tableWriter interface. 45 func (td *tableDeleter) init(_ context.Context, txn *kv.Txn, _ *tree.EvalContext) error { 46 td.tableWriterBase.init(txn) 47 return nil 48 } 49 50 // flushAndStartNewBatch is part of the tableWriter interface. 51 func (td *tableDeleter) flushAndStartNewBatch(ctx context.Context) error { 52 return td.tableWriterBase.flushAndStartNewBatch(ctx, td.rd.Helper.TableDesc) 53 } 54 55 // finalize is part of the tableWriter interface. 56 func (td *tableDeleter) finalize(ctx context.Context, _ bool) (*rowcontainer.RowContainer, error) { 57 return nil, td.tableWriterBase.finalize(ctx, td.rd.Helper.TableDesc) 58 } 59 60 // atBatchEnd is part of the tableWriter interface. 61 func (td *tableDeleter) atBatchEnd(_ context.Context, _ bool) error { return nil } 62 63 // row is part of the tableWriter interface. 64 // TODO(mgartner): Pass ignoreIndexes to DeleteRow and do not delete index 65 // entries for indexes in the set. 66 func (td *tableDeleter) row( 67 ctx context.Context, values tree.Datums, ignoreIndexes util.FastIntSet, traceKV bool, 68 ) error { 69 td.batchSize++ 70 return td.rd.DeleteRow(ctx, td.b, values, row.CheckFKs, traceKV) 71 } 72 73 // deleteAllRows runs the kv operations necessary to delete all sql rows in the 74 // table passed at construction. This may require a scan. 75 // 76 // resume is the resume-span which should be used for the table deletion when 77 // the table deletion is chunked. The first call to this method should use a 78 // zero resume-span. After a chunk is deleted a new resume-span is returned. 79 // 80 // limit is a limit on either the number of keys or table-rows (for 81 // interleaved tables) deleted in the operation. 82 func (td *tableDeleter) deleteAllRows( 83 ctx context.Context, resume roachpb.Span, limit int64, traceKV bool, 84 ) (roachpb.Span, error) { 85 if td.rd.Helper.TableDesc.IsInterleaved() { 86 log.VEvent(ctx, 2, "delete forced to scan: table is interleaved") 87 return td.deleteAllRowsScan(ctx, resume, limit, traceKV) 88 } 89 // TODO(pbardea): Is this ever called anymore? 90 return td.deleteAllRowsFast(ctx, resume, limit, traceKV) 91 } 92 93 // deleteAllRowsFast uses the DelRange KV request to delete data quickly, 94 // relative to deleteAllRowsScan. 95 // 96 // Note that this method leaves a RocksDB deletion tombstone on every key in the 97 // table, resulting in substantial write amplification. When possible, the 98 // schema changer avoids using a tableDeleter entirely in favor of the 99 // ClearRange KV request, which uses RocksDB range deletion tombstones to avoid 100 // write amplification. 101 func (td *tableDeleter) deleteAllRowsFast( 102 ctx context.Context, resume roachpb.Span, limit int64, traceKV bool, 103 ) (roachpb.Span, error) { 104 if resume.Key == nil { 105 tablePrefix := td.rd.Helper.Codec.TablePrefix(uint32(td.rd.Helper.TableDesc.ID)) 106 // Delete rows and indexes starting with the table's prefix. 107 resume = roachpb.Span{ 108 Key: tablePrefix, 109 EndKey: tablePrefix.PrefixEnd(), 110 } 111 } 112 113 log.VEventf(ctx, 2, "DelRange %s - %s", resume.Key, resume.EndKey) 114 td.b.DelRange(resume.Key, resume.EndKey, false /* returnKeys */) 115 td.b.Header.MaxSpanRequestKeys = limit 116 if _, err := td.finalize(ctx, traceKV); err != nil { 117 return resume, err 118 } 119 if l := len(td.b.Results); l != 1 { 120 panic(fmt.Sprintf("%d results returned", l)) 121 } 122 return td.b.Results[0].ResumeSpanAsValue(), nil 123 } 124 125 func (td *tableDeleter) deleteAllRowsScan( 126 ctx context.Context, resume roachpb.Span, limit int64, traceKV bool, 127 ) (roachpb.Span, error) { 128 if resume.Key == nil { 129 resume = td.rd.Helper.TableDesc.PrimaryIndexSpan(td.rd.Helper.Codec) 130 } 131 132 var valNeededForCol util.FastIntSet 133 for _, idx := range td.rd.FetchColIDtoRowIndex { 134 valNeededForCol.Add(idx) 135 } 136 137 var rf row.Fetcher 138 tableArgs := row.FetcherTableArgs{ 139 Desc: td.rd.Helper.TableDesc, 140 Index: &td.rd.Helper.TableDesc.PrimaryIndex, 141 ColIdxMap: td.rd.FetchColIDtoRowIndex, 142 Cols: td.rd.FetchCols, 143 ValNeededForCol: valNeededForCol, 144 } 145 if err := rf.Init( 146 td.rd.Helper.Codec, 147 false, /* reverse */ 148 // TODO(nvanbenschoten): it might make sense to use a FOR_UPDATE locking 149 // strength here. Consider hooking this in to the same knob that will 150 // control whether we perform locking implicitly during DELETEs. 151 sqlbase.ScanLockingStrength_FOR_NONE, 152 false, /* returnRangeInfo */ 153 false, /* isCheck */ 154 td.alloc, 155 tableArgs, 156 ); err != nil { 157 return resume, err 158 } 159 if err := rf.StartScan(ctx, td.txn, roachpb.Spans{resume}, true /* limit batches */, 0, traceKV); err != nil { 160 return resume, err 161 } 162 163 for i := int64(0); i < limit; i++ { 164 datums, _, _, err := rf.NextRowDecoded(ctx) 165 if err != nil { 166 return resume, err 167 } 168 if datums == nil { 169 // Done deleting all rows. 170 resume = roachpb.Span{} 171 break 172 } 173 // TODO(mgartner): Add partial index IDs to ignoreIndexes that we should 174 // not delete entries from. 175 var ignoreIndexes util.FastIntSet 176 if err = td.row(ctx, datums, ignoreIndexes, traceKV); err != nil { 177 return resume, err 178 } 179 } 180 if resume.Key != nil { 181 // Update the resume start key for the next iteration. 182 resume.Key = rf.Key() 183 } 184 _, err := td.finalize(ctx, traceKV) 185 return resume, err 186 } 187 188 // deleteIndex runs the kv operations necessary to delete all kv entries in the 189 // given index. This may require a scan. 190 // 191 // resume is the resume-span which should be used for the index deletion 192 // when the index deletion is chunked. The first call to this method should 193 // use a zero resume-span. After a chunk of the index is deleted a new resume- 194 // span is returned. 195 // 196 // limit is a limit on the number of index entries deleted in the operation. 197 func (td *tableDeleter) deleteIndex( 198 ctx context.Context, idx *sqlbase.IndexDescriptor, resume roachpb.Span, limit int64, traceKV bool, 199 ) (roachpb.Span, error) { 200 if idx.IsInterleaved() { 201 if log.V(2) { 202 log.Info(ctx, "delete forced to scan: table is interleaved") 203 } 204 return td.deleteIndexScan(ctx, idx, resume, limit, traceKV) 205 } 206 return td.deleteIndexFast(ctx, idx, resume, limit, traceKV) 207 } 208 209 func (td *tableDeleter) deleteIndexFast( 210 ctx context.Context, idx *sqlbase.IndexDescriptor, resume roachpb.Span, limit int64, traceKV bool, 211 ) (roachpb.Span, error) { 212 if resume.Key == nil { 213 resume = td.rd.Helper.TableDesc.IndexSpan(td.rd.Helper.Codec, idx.ID) 214 } 215 216 if traceKV { 217 log.VEventf(ctx, 2, "DelRange %s - %s", resume.Key, resume.EndKey) 218 } 219 td.b.DelRange(resume.Key, resume.EndKey, false /* returnKeys */) 220 td.b.Header.MaxSpanRequestKeys = limit 221 if _, err := td.finalize(ctx, traceKV); err != nil { 222 return resume, err 223 } 224 if l := len(td.b.Results); l != 1 { 225 panic(fmt.Sprintf("%d results returned, expected 1", l)) 226 } 227 return td.b.Results[0].ResumeSpanAsValue(), nil 228 } 229 230 func (td *tableDeleter) clearIndex(ctx context.Context, idx *sqlbase.IndexDescriptor) error { 231 if idx.IsInterleaved() { 232 return errors.Errorf("unexpected interleaved index %d", idx.ID) 233 } 234 235 sp := td.rd.Helper.TableDesc.IndexSpan(td.rd.Helper.Codec, idx.ID) 236 237 // ClearRange cannot be run in a transaction, so create a 238 // non-transactional batch to send the request. 239 b := &kv.Batch{} 240 b.AddRawRequest(&roachpb.ClearRangeRequest{ 241 RequestHeader: roachpb.RequestHeader{ 242 Key: sp.Key, 243 EndKey: sp.EndKey, 244 }, 245 }) 246 return td.txn.DB().Run(ctx, b) 247 } 248 249 func (td *tableDeleter) deleteIndexScan( 250 ctx context.Context, idx *sqlbase.IndexDescriptor, resume roachpb.Span, limit int64, traceKV bool, 251 ) (roachpb.Span, error) { 252 if resume.Key == nil { 253 resume = td.rd.Helper.TableDesc.PrimaryIndexSpan(td.rd.Helper.Codec) 254 } 255 256 var valNeededForCol util.FastIntSet 257 for _, idx := range td.rd.FetchColIDtoRowIndex { 258 valNeededForCol.Add(idx) 259 } 260 261 var rf row.Fetcher 262 tableArgs := row.FetcherTableArgs{ 263 Desc: td.rd.Helper.TableDesc, 264 Index: &td.rd.Helper.TableDesc.PrimaryIndex, 265 ColIdxMap: td.rd.FetchColIDtoRowIndex, 266 Cols: td.rd.FetchCols, 267 ValNeededForCol: valNeededForCol, 268 } 269 if err := rf.Init( 270 td.rd.Helper.Codec, 271 false, /* reverse */ 272 // TODO(nvanbenschoten): it might make sense to use a FOR_UPDATE locking 273 // strength here. Consider hooking this in to the same knob that will 274 // control whether we perform locking implicitly during DELETEs. 275 sqlbase.ScanLockingStrength_FOR_NONE, 276 false, /* returnRangeInfo */ 277 false, /* isCheck */ 278 td.alloc, 279 tableArgs, 280 ); err != nil { 281 return resume, err 282 } 283 if err := rf.StartScan(ctx, td.txn, roachpb.Spans{resume}, true /* limit batches */, 0, traceKV); err != nil { 284 return resume, err 285 } 286 287 for i := int64(0); i < limit; i++ { 288 datums, _, _, err := rf.NextRowDecoded(ctx) 289 if err != nil { 290 return resume, err 291 } 292 if datums == nil { 293 // Done deleting all rows. 294 resume = roachpb.Span{} 295 break 296 } 297 if err := td.rd.DeleteIndexRow(ctx, td.b, idx, datums, traceKV); err != nil { 298 return resume, err 299 } 300 } 301 if resume.Key != nil { 302 // Update the resume start key for the next iteration. 303 resume.Key = rf.Key() 304 } 305 _, err := td.finalize(ctx, traceKV) 306 return resume, err 307 } 308 309 func (td *tableDeleter) tableDesc() *sqlbase.ImmutableTableDescriptor { 310 return td.rd.Helper.TableDesc 311 } 312 313 func (td *tableDeleter) close(_ context.Context) {}