Line data Source code
1 : // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2 : // of this source code is governed by a BSD-style license that can be found in
3 : // the LICENSE file.
4 :
5 : package metamorphic
6 :
7 : import (
8 : "fmt"
9 : "go/scanner"
10 : "go/token"
11 : "reflect"
12 : "strconv"
13 : "strings"
14 :
15 : "github.com/cockroachdb/errors"
16 : "github.com/cockroachdb/pebble"
17 : )
18 :
19 : type methodInfo struct {
20 : constructor func() op
21 : validTags uint32
22 : }
23 :
24 1 : func makeMethod(i interface{}, tags ...objTag) *methodInfo {
25 1 : var validTags uint32
26 1 : for _, tag := range tags {
27 1 : validTags |= 1 << tag
28 1 : }
29 :
30 1 : t := reflect.TypeOf(i)
31 1 : return &methodInfo{
32 1 : constructor: func() op {
33 1 : return reflect.New(t).Interface().(op)
34 1 : },
35 : validTags: validTags,
36 : }
37 : }
38 :
39 : // args returns the receiverID, targetID and arguments for the op. The
40 : // receiverID is the ID of the object the op will be applied to. The targetID
41 : // is the ID of the object for assignment. If the method does not return a new
42 : // object, then targetID will be nil. The argument list is just what it sounds
43 : // like: the list of arguments for the operation.
44 1 : func opArgs(op op) (receiverID *objID, targetID *objID, args []interface{}) {
45 1 : switch t := op.(type) {
46 1 : case *applyOp:
47 1 : return &t.writerID, nil, []interface{}{&t.batchID}
48 1 : case *checkpointOp:
49 1 : return &t.dbID, nil, []interface{}{&t.spans}
50 1 : case *closeOp:
51 1 : return &t.objID, nil, nil
52 1 : case *compactOp:
53 1 : return &t.dbID, nil, []interface{}{&t.start, &t.end, &t.parallelize}
54 1 : case *batchCommitOp:
55 1 : return &t.batchID, nil, nil
56 1 : case *dbRatchetFormatMajorVersionOp:
57 1 : return &t.dbID, nil, []interface{}{&t.vers}
58 1 : case *dbRestartOp:
59 1 : return &t.dbID, nil, nil
60 1 : case *deleteOp:
61 1 : return &t.writerID, nil, []interface{}{&t.key}
62 1 : case *deleteRangeOp:
63 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end}
64 1 : case *iterFirstOp:
65 1 : return &t.iterID, nil, nil
66 1 : case *flushOp:
67 1 : return &t.db, nil, nil
68 1 : case *getOp:
69 1 : return &t.readerID, nil, []interface{}{&t.key}
70 1 : case *ingestOp:
71 1 : return &t.dbID, nil, []interface{}{&t.batchIDs}
72 1 : case *ingestAndExciseOp:
73 1 : return &t.dbID, nil, []interface{}{&t.batchID, &t.exciseStart, &t.exciseEnd}
74 1 : case *initOp:
75 1 : return nil, nil, []interface{}{&t.dbSlots, &t.batchSlots, &t.iterSlots, &t.snapshotSlots}
76 1 : case *iterLastOp:
77 1 : return &t.iterID, nil, nil
78 1 : case *mergeOp:
79 1 : return &t.writerID, nil, []interface{}{&t.key, &t.value}
80 1 : case *newBatchOp:
81 1 : return &t.dbID, &t.batchID, nil
82 1 : case *newIndexedBatchOp:
83 1 : return &t.dbID, &t.batchID, nil
84 1 : case *newIterOp:
85 1 : return &t.readerID, &t.iterID, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
86 1 : case *newIterUsingCloneOp:
87 1 : return &t.existingIterID, &t.iterID, []interface{}{&t.refreshBatch, &t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
88 1 : case *newSnapshotOp:
89 1 : return &t.dbID, &t.snapID, []interface{}{&t.bounds}
90 1 : case *iterNextOp:
91 1 : return &t.iterID, nil, []interface{}{&t.limit}
92 1 : case *iterNextPrefixOp:
93 1 : return &t.iterID, nil, nil
94 1 : case *iterCanSingleDelOp:
95 1 : return &t.iterID, nil, []interface{}{}
96 1 : case *iterPrevOp:
97 1 : return &t.iterID, nil, []interface{}{&t.limit}
98 1 : case *iterSeekLTOp:
99 1 : return &t.iterID, nil, []interface{}{&t.key, &t.limit}
100 1 : case *iterSeekGEOp:
101 1 : return &t.iterID, nil, []interface{}{&t.key, &t.limit}
102 1 : case *iterSeekPrefixGEOp:
103 1 : return &t.iterID, nil, []interface{}{&t.key}
104 1 : case *setOp:
105 1 : return &t.writerID, nil, []interface{}{&t.key, &t.value}
106 1 : case *iterSetBoundsOp:
107 1 : return &t.iterID, nil, []interface{}{&t.lower, &t.upper}
108 1 : case *iterSetOptionsOp:
109 1 : return &t.iterID, nil, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
110 1 : case *singleDeleteOp:
111 1 : return &t.writerID, nil, []interface{}{&t.key, &t.maybeReplaceDelete}
112 1 : case *rangeKeyDeleteOp:
113 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end}
114 1 : case *rangeKeySetOp:
115 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix, &t.value}
116 1 : case *rangeKeyUnsetOp:
117 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix}
118 1 : case *replicateOp:
119 1 : return &t.source, nil, []interface{}{&t.dest, &t.start, &t.end}
120 : }
121 0 : panic(fmt.Sprintf("unsupported op type: %T", op))
122 : }
123 :
124 : var methods = map[string]*methodInfo{
125 : "Apply": makeMethod(applyOp{}, dbTag, batchTag),
126 : "Checkpoint": makeMethod(checkpointOp{}, dbTag),
127 : "Clone": makeMethod(newIterUsingCloneOp{}, iterTag),
128 : "Close": makeMethod(closeOp{}, dbTag, batchTag, iterTag, snapTag),
129 : "Commit": makeMethod(batchCommitOp{}, batchTag),
130 : "Compact": makeMethod(compactOp{}, dbTag),
131 : "Delete": makeMethod(deleteOp{}, dbTag, batchTag),
132 : "DeleteRange": makeMethod(deleteRangeOp{}, dbTag, batchTag),
133 : "First": makeMethod(iterFirstOp{}, iterTag),
134 : "Flush": makeMethod(flushOp{}, dbTag),
135 : "Get": makeMethod(getOp{}, dbTag, batchTag, snapTag),
136 : "Ingest": makeMethod(ingestOp{}, dbTag),
137 : "IngestAndExcise": makeMethod(ingestAndExciseOp{}, dbTag),
138 : "Init": makeMethod(initOp{}, dbTag),
139 : "Last": makeMethod(iterLastOp{}, iterTag),
140 : "Merge": makeMethod(mergeOp{}, dbTag, batchTag),
141 : "NewBatch": makeMethod(newBatchOp{}, dbTag),
142 : "NewIndexedBatch": makeMethod(newIndexedBatchOp{}, dbTag),
143 : "NewIter": makeMethod(newIterOp{}, dbTag, batchTag, snapTag),
144 : "NewSnapshot": makeMethod(newSnapshotOp{}, dbTag),
145 : "Next": makeMethod(iterNextOp{}, iterTag),
146 : "NextPrefix": makeMethod(iterNextPrefixOp{}, iterTag),
147 : "InternalNext": makeMethod(iterCanSingleDelOp{}, iterTag),
148 : "Prev": makeMethod(iterPrevOp{}, iterTag),
149 : "RangeKeyDelete": makeMethod(rangeKeyDeleteOp{}, dbTag, batchTag),
150 : "RangeKeySet": makeMethod(rangeKeySetOp{}, dbTag, batchTag),
151 : "RangeKeyUnset": makeMethod(rangeKeyUnsetOp{}, dbTag, batchTag),
152 : "RatchetFormatMajorVersion": makeMethod(dbRatchetFormatMajorVersionOp{}, dbTag),
153 : "Replicate": makeMethod(replicateOp{}, dbTag),
154 : "Restart": makeMethod(dbRestartOp{}, dbTag),
155 : "SeekGE": makeMethod(iterSeekGEOp{}, iterTag),
156 : "SeekLT": makeMethod(iterSeekLTOp{}, iterTag),
157 : "SeekPrefixGE": makeMethod(iterSeekPrefixGEOp{}, iterTag),
158 : "Set": makeMethod(setOp{}, dbTag, batchTag),
159 : "SetBounds": makeMethod(iterSetBoundsOp{}, iterTag),
160 : "SetOptions": makeMethod(iterSetOptionsOp{}, iterTag),
161 : "SingleDelete": makeMethod(singleDeleteOp{}, dbTag, batchTag),
162 : }
163 :
164 : type parser struct {
165 : opts parserOpts
166 : fset *token.FileSet
167 : s scanner.Scanner
168 : objs map[objID]bool
169 : }
170 :
171 : type parserOpts struct {
172 : allowUndefinedObjs bool
173 : }
174 :
175 1 : func parse(src []byte, opts parserOpts) (_ []op, err error) {
176 1 : // Various bits of magic incantation to set up a scanner for Go compatible
177 1 : // syntax. We arranged for the textual format of ops (e.g. op.String()) to
178 1 : // look like Go which allows us to use the Go scanner for parsing.
179 1 : p := &parser{
180 1 : opts: opts,
181 1 : fset: token.NewFileSet(),
182 1 : objs: map[objID]bool{makeObjID(dbTag, 1): true, makeObjID(dbTag, 2): true},
183 1 : }
184 1 : file := p.fset.AddFile("", -1, len(src))
185 1 : p.s.Init(file, src, nil /* no error handler */, 0)
186 1 : return p.parse()
187 1 : }
188 :
189 1 : func (p *parser) parse() (_ []op, err error) {
190 1 : defer func() {
191 1 : if r := recover(); r != nil {
192 0 : var ok bool
193 0 : if err, ok = r.(error); ok {
194 0 : return
195 0 : }
196 0 : err = errors.Errorf("%v", r)
197 : }
198 : }()
199 :
200 1 : var ops []op
201 1 : for {
202 1 : op := p.parseOp()
203 1 : if op == nil {
204 1 : computeDerivedFields(ops)
205 1 : return ops, nil
206 1 : }
207 1 : ops = append(ops, op)
208 : }
209 : }
210 :
211 1 : func (p *parser) parseOp() op {
212 1 : destPos, destTok, destLit := p.s.Scan()
213 1 : if destTok == token.EOF {
214 1 : return nil
215 1 : }
216 1 : if destTok != token.IDENT {
217 0 : panic(p.errorf(destPos, "unexpected token: %s %q", destTok, destLit))
218 : }
219 1 : if destLit == "Init" {
220 1 : // <op>(<args>)
221 1 : return p.makeOp(destLit, makeObjID(dbTag, 1), 0, destPos)
222 1 : }
223 :
224 1 : destID := p.parseObjID(destPos, destLit)
225 1 :
226 1 : pos, tok, lit := p.s.Scan()
227 1 : switch tok {
228 1 : case token.PERIOD:
229 1 : // <obj>.<op>(<args>)
230 1 : if !p.objs[destID] {
231 0 : if p.opts.allowUndefinedObjs {
232 0 : p.objs[destID] = true
233 0 : } else {
234 0 : panic(p.errorf(destPos, "unknown object: %s", destID))
235 : }
236 : }
237 1 : _, methodLit := p.scanToken(token.IDENT)
238 1 : return p.makeOp(methodLit, destID, 0, destPos)
239 :
240 1 : case token.ASSIGN:
241 1 : // <obj> = <obj>.<op>(<args>)
242 1 : srcPos, srcLit := p.scanToken(token.IDENT)
243 1 : srcID := p.parseObjID(srcPos, srcLit)
244 1 : if !p.objs[srcID] {
245 0 : if p.opts.allowUndefinedObjs {
246 0 : p.objs[srcID] = true
247 0 : } else {
248 0 : panic(p.errorf(srcPos, "unknown object %q", srcLit))
249 : }
250 : }
251 1 : p.scanToken(token.PERIOD)
252 1 : _, methodLit := p.scanToken(token.IDENT)
253 1 : p.objs[destID] = true
254 1 : return p.makeOp(methodLit, srcID, destID, srcPos)
255 : }
256 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
257 : }
258 :
259 1 : func parseObjID(str string) (objID, error) {
260 1 : var tag objTag
261 1 : switch {
262 1 : case strings.HasPrefix(str, "db"):
263 1 : tag, str = dbTag, str[2:]
264 1 : if str == "" {
265 0 : str = "1"
266 0 : }
267 1 : case strings.HasPrefix(str, "batch"):
268 1 : tag, str = batchTag, str[5:]
269 1 : case strings.HasPrefix(str, "iter"):
270 1 : tag, str = iterTag, str[4:]
271 1 : case strings.HasPrefix(str, "snap"):
272 1 : tag, str = snapTag, str[4:]
273 0 : default:
274 0 : return 0, errors.Newf("unable to parse objectID: %q", str)
275 : }
276 1 : id, err := strconv.ParseInt(str, 10, 32)
277 1 : if err != nil {
278 0 : return 0, err
279 0 : }
280 1 : return makeObjID(tag, uint32(id)), nil
281 : }
282 :
283 1 : func (p *parser) parseObjID(pos token.Pos, str string) objID {
284 1 : id, err := parseObjID(str)
285 1 : if err != nil {
286 0 : panic(p.errorf(pos, "%s", err))
287 : }
288 1 : return id
289 : }
290 :
291 1 : func unquoteBytes(lit string) []byte {
292 1 : s, err := strconv.Unquote(lit)
293 1 : if err != nil {
294 0 : panic(err)
295 : }
296 1 : if len(s) == 0 {
297 1 : return nil
298 1 : }
299 1 : return []byte(s)
300 : }
301 :
302 1 : func (p *parser) parseArgs(op op, methodName string, args []interface{}) {
303 1 : pos, _ := p.scanToken(token.LPAREN)
304 1 : for i := range args {
305 1 : if i > 0 {
306 1 : pos, _ = p.scanToken(token.COMMA)
307 1 : }
308 :
309 1 : switch t := args[i].(type) {
310 1 : case *uint32:
311 1 : _, lit := p.scanToken(token.INT)
312 1 : val, err := strconv.ParseUint(lit, 10, 32)
313 1 : if err != nil {
314 0 : panic(err)
315 : }
316 1 : *t = uint32(val)
317 :
318 1 : case *uint64:
319 1 : _, lit := p.scanToken(token.INT)
320 1 : val, err := strconv.ParseUint(lit, 10, 64)
321 1 : if err != nil {
322 0 : panic(err)
323 : }
324 1 : *t = uint64(val)
325 :
326 1 : case *[]byte:
327 1 : _, lit := p.scanToken(token.STRING)
328 1 : *t = unquoteBytes(lit)
329 :
330 1 : case *bool:
331 1 : _, lit := p.scanToken(token.IDENT)
332 1 : b, err := strconv.ParseBool(lit)
333 1 : if err != nil {
334 0 : panic(err)
335 : }
336 1 : *t = b
337 :
338 1 : case *objID:
339 1 : pos, lit := p.scanToken(token.IDENT)
340 1 : *t = p.parseObjID(pos, lit)
341 :
342 1 : case *[]pebble.KeyRange:
343 1 : var pending pebble.KeyRange
344 1 : for {
345 1 : pos, tok, lit := p.s.Scan()
346 1 : switch tok {
347 1 : case token.STRING:
348 1 : x := unquoteBytes(lit)
349 1 : if pending.Start == nil {
350 1 : pending.Start = x
351 1 : } else {
352 1 : pending.End = x
353 1 : *t = append(*t, pending)
354 1 : pending = pebble.KeyRange{}
355 1 : }
356 1 : pos, tok, lit := p.s.Scan()
357 1 : switch tok {
358 1 : case token.COMMA:
359 1 : continue
360 1 : case token.RPAREN:
361 1 : p.scanToken(token.SEMICOLON)
362 1 : return
363 0 : default:
364 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
365 : }
366 0 : case token.RPAREN:
367 0 : p.scanToken(token.SEMICOLON)
368 0 : return
369 0 : default:
370 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
371 : }
372 : }
373 :
374 1 : case *[]objID:
375 1 : for {
376 1 : pos, tok, lit := p.s.Scan()
377 1 : switch tok {
378 1 : case token.IDENT:
379 1 : *t = append(*t, p.parseObjID(pos, lit))
380 1 : pos, tok, lit := p.s.Scan()
381 1 : switch tok {
382 1 : case token.COMMA:
383 1 : continue
384 1 : case token.RPAREN:
385 1 : p.scanToken(token.SEMICOLON)
386 1 : return
387 0 : default:
388 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
389 : }
390 0 : case token.RPAREN:
391 0 : p.scanToken(token.SEMICOLON)
392 0 : return
393 0 : default:
394 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
395 : }
396 : }
397 :
398 1 : case *[]pebble.CheckpointSpan:
399 1 : pos, tok, lit := p.s.Scan()
400 1 : switch tok {
401 1 : case token.RPAREN:
402 1 : // No spans.
403 1 : *t = nil
404 1 : p.scanToken(token.SEMICOLON)
405 1 : return
406 :
407 1 : case token.STRING:
408 1 : var keys [][]byte
409 1 : for {
410 1 : s, err := strconv.Unquote(lit)
411 1 : if err != nil {
412 0 : panic(p.errorf(pos, "unquoting %q: %v", lit, err))
413 : }
414 1 : keys = append(keys, []byte(s))
415 1 :
416 1 : pos, tok, lit = p.s.Scan()
417 1 : switch tok {
418 1 : case token.COMMA:
419 1 : pos, tok, lit = p.s.Scan()
420 1 : if tok != token.STRING {
421 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
422 : }
423 1 : continue
424 :
425 1 : case token.RPAREN:
426 1 : p.scanToken(token.SEMICOLON)
427 1 : if len(keys)%2 == 1 {
428 0 : panic(p.errorf(pos, "expected even number of keys"))
429 : }
430 1 : *t = make([]pebble.CheckpointSpan, len(keys)/2)
431 1 : for i := range *t {
432 1 : (*t)[i] = pebble.CheckpointSpan{
433 1 : Start: keys[i*2],
434 1 : End: keys[i*2+1],
435 1 : }
436 1 : }
437 1 : return
438 :
439 0 : default:
440 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
441 : }
442 : }
443 :
444 0 : default:
445 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
446 : }
447 :
448 1 : case *pebble.FormatMajorVersion:
449 1 : _, lit := p.scanToken(token.INT)
450 1 : val, err := strconv.ParseUint(lit, 10, 64)
451 1 : if err != nil {
452 0 : panic(err)
453 : }
454 1 : *t = pebble.FormatMajorVersion(val)
455 :
456 0 : default:
457 0 : panic(p.errorf(pos, "%s: unsupported arg[%d] type: %T", methodName, i, args[i]))
458 : }
459 : }
460 1 : p.scanToken(token.RPAREN)
461 1 : p.scanToken(token.SEMICOLON)
462 : }
463 :
464 1 : func (p *parser) scanToken(expected token.Token) (pos token.Pos, lit string) {
465 1 : pos, tok, lit := p.s.Scan()
466 1 : if tok != expected {
467 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
468 : }
469 1 : return pos, lit
470 : }
471 :
472 1 : func (p *parser) makeOp(methodName string, receiverID, targetID objID, pos token.Pos) op {
473 1 : info := methods[methodName]
474 1 : if info == nil {
475 0 : panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName))
476 : }
477 1 : if info.validTags&(1<<receiverID.tag()) == 0 {
478 0 : panic(p.errorf(pos, "%s.%s: %s is not a method on %s",
479 0 : receiverID, methodName, methodName, receiverID))
480 : }
481 :
482 1 : op := info.constructor()
483 1 : receiver, target, args := opArgs(op)
484 1 :
485 1 : // The form of an operation is:
486 1 : // [target =] receiver.method(args)
487 1 : //
488 1 : // The receiver is the object the operation will be called on, which can be
489 1 : // any valid ID. Certain operations such as Ingest are only valid on the DB
490 1 : // object. That is indicated by opArgs returning a nil receiver.
491 1 : if receiver != nil {
492 1 : *receiver = receiverID
493 1 : } else if receiverID.tag() != dbTag {
494 0 : panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName))
495 : }
496 :
497 : // The target is the object that will be assigned the result of an object
498 : // creation operation such as newBatchOp or newIterOp.
499 1 : if target != nil {
500 1 : // It is invalid to not have a targetID for a method which generates a new
501 1 : // object.
502 1 : if targetID == 0 {
503 0 : panic(p.errorf(pos, "assignment expected for %s.%s", receiverID, methodName))
504 : }
505 : // It is invalid to try to assign to the DB object.
506 1 : if targetID.tag() == dbTag {
507 0 : panic(p.errorf(pos, "cannot use %s as target of assignment", targetID))
508 : }
509 1 : *target = targetID
510 1 : } else if targetID != 0 {
511 0 : panic(p.errorf(pos, "cannot use %s.%s in assignment", receiverID, methodName))
512 : }
513 :
514 1 : p.parseArgs(op, methodName, args)
515 1 : return op
516 : }
517 :
518 0 : func (p *parser) tokenf(tok token.Token, lit string) string {
519 0 : if tok.IsLiteral() {
520 0 : return lit
521 0 : }
522 0 : return tok.String()
523 : }
524 :
525 0 : func (p *parser) errorf(pos token.Pos, format string, args ...interface{}) error {
526 0 : return errors.New(p.fset.Position(pos).String() + ": " + fmt.Sprintf(format, args...))
527 0 : }
528 :
529 : // computeDerivedFields makes one pass through the provided operations, filling
530 : // any derived fields. This pass must happen before execution because concurrent
531 : // execution depends on these fields.
532 1 : func computeDerivedFields(ops []op) {
533 1 : iterToReader := make(map[objID]objID)
534 1 : objToDB := make(map[objID]objID)
535 1 : for i := range ops {
536 1 : switch v := ops[i].(type) {
537 1 : case *newSnapshotOp:
538 1 : objToDB[v.snapID] = v.dbID
539 1 : case *newIterOp:
540 1 : iterToReader[v.iterID] = v.readerID
541 1 : dbReaderID := v.readerID
542 1 : if dbReaderID.tag() != dbTag {
543 1 : dbReaderID = objToDB[dbReaderID]
544 1 : }
545 1 : objToDB[v.iterID] = dbReaderID
546 1 : v.derivedDBID = dbReaderID
547 1 : case *newIterUsingCloneOp:
548 1 : v.derivedReaderID = iterToReader[v.existingIterID]
549 1 : iterToReader[v.iterID] = v.derivedReaderID
550 1 : objToDB[v.iterID] = objToDB[v.existingIterID]
551 1 : case *iterSetOptionsOp:
552 1 : v.derivedReaderID = iterToReader[v.iterID]
553 1 : case *iterFirstOp:
554 1 : v.derivedReaderID = iterToReader[v.iterID]
555 1 : case *iterLastOp:
556 1 : v.derivedReaderID = iterToReader[v.iterID]
557 1 : case *iterSeekGEOp:
558 1 : v.derivedReaderID = iterToReader[v.iterID]
559 1 : case *iterSeekPrefixGEOp:
560 1 : v.derivedReaderID = iterToReader[v.iterID]
561 1 : case *iterSeekLTOp:
562 1 : v.derivedReaderID = iterToReader[v.iterID]
563 1 : case *iterNextOp:
564 1 : v.derivedReaderID = iterToReader[v.iterID]
565 1 : case *iterNextPrefixOp:
566 1 : v.derivedReaderID = iterToReader[v.iterID]
567 1 : case *iterCanSingleDelOp:
568 1 : v.derivedReaderID = iterToReader[v.iterID]
569 1 : case *iterPrevOp:
570 1 : v.derivedReaderID = iterToReader[v.iterID]
571 1 : case *newBatchOp:
572 1 : objToDB[v.batchID] = v.dbID
573 1 : case *newIndexedBatchOp:
574 1 : objToDB[v.batchID] = v.dbID
575 1 : case *applyOp:
576 1 : if derivedDBID, ok := objToDB[v.batchID]; ok && v.writerID.tag() != dbTag {
577 1 : objToDB[v.writerID] = derivedDBID
578 1 : }
579 1 : case *getOp:
580 1 : if derivedDBID, ok := objToDB[v.readerID]; ok {
581 1 : v.derivedDBID = derivedDBID
582 1 : }
583 1 : case *batchCommitOp:
584 1 : v.dbID = objToDB[v.batchID]
585 1 : case *closeOp:
586 1 : if derivedDBID, ok := objToDB[v.objID]; ok && v.objID.tag() != dbTag {
587 1 : v.derivedDBID = derivedDBID
588 1 : }
589 1 : case *ingestOp:
590 1 : v.derivedDBIDs = make([]objID, len(v.batchIDs))
591 1 : for i := range v.batchIDs {
592 1 : v.derivedDBIDs[i] = objToDB[v.batchIDs[i]]
593 1 : }
594 1 : case *ingestAndExciseOp:
595 1 : v.derivedDBID = objToDB[v.batchID]
596 1 : case *deleteOp:
597 1 : derivedDBID := v.writerID
598 1 : if v.writerID.tag() != dbTag {
599 1 : derivedDBID = objToDB[v.writerID]
600 1 : }
601 1 : v.derivedDBID = derivedDBID
602 : }
603 : }
604 : }
|