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 : "bytes"
9 : "fmt"
10 : "go/scanner"
11 : "go/token"
12 : "reflect"
13 : "slices"
14 : "strconv"
15 :
16 : "github.com/cockroachdb/errors"
17 : "github.com/cockroachdb/pebble"
18 : "github.com/cockroachdb/pebble/sstable/block"
19 : )
20 :
21 : type methodInfo struct {
22 : constructor func() op
23 : validTags uint32
24 : }
25 :
26 1 : func makeMethod(i interface{}, tags ...objTag) *methodInfo {
27 1 : var validTags uint32
28 1 : for _, tag := range tags {
29 1 : validTags |= 1 << tag
30 1 : }
31 :
32 1 : t := reflect.TypeOf(i)
33 1 : return &methodInfo{
34 1 : constructor: func() op {
35 1 : return reflect.New(t).Interface().(op)
36 1 : },
37 : validTags: validTags,
38 : }
39 : }
40 :
41 : // args returns the receiverID, targetID and arguments for the op. The
42 : // receiverID is the ID of the object the op will be applied to. The targetID
43 : // is the ID of the object for assignment. If the method does not return a new
44 : // object, then targetID will be nil.
45 : //
46 : // The argument list returns pointers to operation fields that map to arguments
47 : // for the operation. The last argument can be a pointer to a slice,
48 : // corresponding to a variable number of arguments.
49 1 : func opArgs(op op) (receiverID *objID, targetID *objID, args []interface{}) {
50 1 : switch t := op.(type) {
51 1 : case *applyOp:
52 1 : return &t.writerID, nil, []interface{}{&t.batchID}
53 1 : case *checkpointOp:
54 1 : return &t.dbID, nil, []interface{}{&t.spans}
55 1 : case *closeOp:
56 1 : return &t.objID, nil, nil
57 1 : case *compactOp:
58 1 : return &t.dbID, nil, []interface{}{&t.start, &t.end, &t.parallelize}
59 1 : case *batchCommitOp:
60 1 : return &t.batchID, nil, nil
61 1 : case *dbRatchetFormatMajorVersionOp:
62 1 : return &t.dbID, nil, []interface{}{&t.vers}
63 1 : case *dbRestartOp:
64 1 : return &t.dbID, nil, nil
65 1 : case *deleteOp:
66 1 : return &t.writerID, nil, []interface{}{&t.key}
67 1 : case *deleteRangeOp:
68 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end}
69 1 : case *downloadOp:
70 1 : return &t.dbID, nil, []interface{}{&t.spans}
71 1 : case *iterFirstOp:
72 1 : return &t.iterID, nil, nil
73 1 : case *flushOp:
74 1 : return &t.db, nil, nil
75 1 : case *getOp:
76 1 : return &t.readerID, nil, []interface{}{&t.key}
77 1 : case *ingestOp:
78 1 : return &t.dbID, nil, []interface{}{&t.batchIDs}
79 1 : case *ingestAndExciseOp:
80 1 : return &t.dbID, nil, []interface{}{&t.batchID, &t.exciseStart, &t.exciseEnd}
81 1 : case *ingestExternalFilesOp:
82 1 : return &t.dbID, nil, []interface{}{&t.objs}
83 1 : case *initOp:
84 1 : return nil, nil, []interface{}{&t.dbSlots, &t.batchSlots, &t.iterSlots, &t.snapshotSlots, &t.externalObjSlots}
85 1 : case *iterLastOp:
86 1 : return &t.iterID, nil, nil
87 1 : case *logDataOp:
88 1 : return &t.writerID, nil, []interface{}{&t.data}
89 1 : case *mergeOp:
90 1 : return &t.writerID, nil, []interface{}{&t.key, &t.value}
91 1 : case *newBatchOp:
92 1 : return &t.dbID, &t.batchID, nil
93 1 : case *newIndexedBatchOp:
94 1 : return &t.dbID, &t.batchID, nil
95 1 : case *newIterOp:
96 1 : return &t.readerID, &t.iterID, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
97 1 : case *newIterUsingCloneOp:
98 1 : return &t.existingIterID, &t.iterID, []interface{}{&t.refreshBatch, &t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
99 1 : case *newSnapshotOp:
100 1 : return &t.dbID, &t.snapID, []interface{}{&t.bounds}
101 1 : case *newExternalObjOp:
102 1 : return &t.batchID, &t.externalObjID, nil
103 1 : case *iterNextOp:
104 1 : return &t.iterID, nil, []interface{}{&t.limit}
105 1 : case *iterNextPrefixOp:
106 1 : return &t.iterID, nil, nil
107 1 : case *iterCanSingleDelOp:
108 1 : return &t.iterID, nil, []interface{}{}
109 1 : case *iterPrevOp:
110 1 : return &t.iterID, nil, []interface{}{&t.limit}
111 1 : case *iterSeekLTOp:
112 1 : return &t.iterID, nil, []interface{}{&t.key, &t.limit}
113 1 : case *iterSeekGEOp:
114 1 : return &t.iterID, nil, []interface{}{&t.key, &t.limit}
115 1 : case *iterSeekPrefixGEOp:
116 1 : return &t.iterID, nil, []interface{}{&t.key}
117 1 : case *setOp:
118 1 : return &t.writerID, nil, []interface{}{&t.key, &t.value}
119 1 : case *iterSetBoundsOp:
120 1 : return &t.iterID, nil, []interface{}{&t.lower, &t.upper}
121 1 : case *iterSetOptionsOp:
122 1 : return &t.iterID, nil, []interface{}{&t.lower, &t.upper, &t.keyTypes, &t.filterMin, &t.filterMax, &t.useL6Filters, &t.maskSuffix}
123 1 : case *singleDeleteOp:
124 1 : return &t.writerID, nil, []interface{}{&t.key, &t.maybeReplaceDelete}
125 1 : case *rangeKeyDeleteOp:
126 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end}
127 1 : case *rangeKeySetOp:
128 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix, &t.value}
129 1 : case *rangeKeyUnsetOp:
130 1 : return &t.writerID, nil, []interface{}{&t.start, &t.end, &t.suffix}
131 1 : case *replicateOp:
132 1 : return &t.source, nil, []interface{}{&t.dest, &t.start, &t.end}
133 : }
134 0 : panic(fmt.Sprintf("unsupported op type: %T", op))
135 : }
136 :
137 : var methods = map[string]*methodInfo{
138 : "Apply": makeMethod(applyOp{}, dbTag, batchTag),
139 : "Checkpoint": makeMethod(checkpointOp{}, dbTag),
140 : "Clone": makeMethod(newIterUsingCloneOp{}, iterTag),
141 : "Close": makeMethod(closeOp{}, dbTag, batchTag, iterTag, snapTag),
142 : "Commit": makeMethod(batchCommitOp{}, batchTag),
143 : "Compact": makeMethod(compactOp{}, dbTag),
144 : "Delete": makeMethod(deleteOp{}, dbTag, batchTag),
145 : "DeleteRange": makeMethod(deleteRangeOp{}, dbTag, batchTag),
146 : "Download": makeMethod(downloadOp{}, dbTag),
147 : "First": makeMethod(iterFirstOp{}, iterTag),
148 : "Flush": makeMethod(flushOp{}, dbTag),
149 : "Get": makeMethod(getOp{}, dbTag, batchTag, snapTag),
150 : "Ingest": makeMethod(ingestOp{}, dbTag),
151 : "IngestAndExcise": makeMethod(ingestAndExciseOp{}, dbTag),
152 : "IngestExternalFiles": makeMethod(ingestExternalFilesOp{}, dbTag),
153 : "Init": makeMethod(initOp{}, dbTag),
154 : "Last": makeMethod(iterLastOp{}, iterTag),
155 : "LogData": makeMethod(logDataOp{}, dbTag, batchTag),
156 : "Merge": makeMethod(mergeOp{}, dbTag, batchTag),
157 : "NewBatch": makeMethod(newBatchOp{}, dbTag),
158 : "NewIndexedBatch": makeMethod(newIndexedBatchOp{}, dbTag),
159 : "NewIter": makeMethod(newIterOp{}, dbTag, batchTag, snapTag),
160 : "NewSnapshot": makeMethod(newSnapshotOp{}, dbTag),
161 : "NewExternalObj": makeMethod(newExternalObjOp{}, batchTag),
162 : "Next": makeMethod(iterNextOp{}, iterTag),
163 : "NextPrefix": makeMethod(iterNextPrefixOp{}, iterTag),
164 : "InternalNext": makeMethod(iterCanSingleDelOp{}, iterTag),
165 : "Prev": makeMethod(iterPrevOp{}, iterTag),
166 : "RangeKeyDelete": makeMethod(rangeKeyDeleteOp{}, dbTag, batchTag),
167 : "RangeKeySet": makeMethod(rangeKeySetOp{}, dbTag, batchTag),
168 : "RangeKeyUnset": makeMethod(rangeKeyUnsetOp{}, dbTag, batchTag),
169 : "RatchetFormatMajorVersion": makeMethod(dbRatchetFormatMajorVersionOp{}, dbTag),
170 : "Replicate": makeMethod(replicateOp{}, dbTag),
171 : "Restart": makeMethod(dbRestartOp{}, dbTag),
172 : "SeekGE": makeMethod(iterSeekGEOp{}, iterTag),
173 : "SeekLT": makeMethod(iterSeekLTOp{}, iterTag),
174 : "SeekPrefixGE": makeMethod(iterSeekPrefixGEOp{}, iterTag),
175 : "Set": makeMethod(setOp{}, dbTag, batchTag),
176 : "SetBounds": makeMethod(iterSetBoundsOp{}, iterTag),
177 : "SetOptions": makeMethod(iterSetOptionsOp{}, iterTag),
178 : "SingleDelete": makeMethod(singleDeleteOp{}, dbTag, batchTag),
179 : }
180 :
181 : type parser struct {
182 : opts parserOpts
183 : fset *token.FileSet
184 : s scanner.Scanner
185 : objs map[objID]bool
186 : }
187 :
188 : type parserOpts struct {
189 : allowUndefinedObjs bool
190 : parseFormattedUserKey func(string) (UserKey, error)
191 : parseFormattedUserKeySuffix func(string) (UserKeySuffix, error)
192 : }
193 :
194 1 : func parse(src []byte, opts parserOpts) (_ []op, err error) {
195 1 : // Various bits of magic incantation to set up a scanner for Go compatible
196 1 : // syntax. We arranged for the textual format of ops (e.g. op.String()) to
197 1 : // look like Go which allows us to use the Go scanner for parsing.
198 1 : p := &parser{
199 1 : opts: opts,
200 1 : fset: token.NewFileSet(),
201 1 : objs: map[objID]bool{makeObjID(dbTag, 1): true, makeObjID(dbTag, 2): true},
202 1 : }
203 1 : file := p.fset.AddFile("", -1, len(src))
204 1 : p.s.Init(file, src, nil /* no error handler */, 0)
205 1 : return p.parse()
206 1 : }
207 :
208 1 : func (p *parser) parse() (_ []op, err error) {
209 1 : defer func() {
210 1 : if r := recover(); r != nil {
211 1 : var ok bool
212 1 : if err, ok = r.(error); ok {
213 1 : return
214 1 : }
215 0 : err = errors.Errorf("%v", r)
216 : }
217 : }()
218 :
219 1 : var ops []op
220 1 : for {
221 1 : op := p.parseOp()
222 1 : if op == nil {
223 1 : computeDerivedFields(ops)
224 1 : return ops, nil
225 1 : }
226 1 : ops = append(ops, op)
227 : }
228 : }
229 :
230 1 : func (p *parser) parseOp() op {
231 1 : destPos, destTok, destLit := p.s.Scan()
232 1 : if destTok == token.EOF {
233 1 : return nil
234 1 : }
235 1 : if destTok != token.IDENT {
236 1 : panic(p.errorf(destPos, "unexpected token: %s %q", destTok, destLit))
237 : }
238 1 : if destLit == "Init" {
239 1 : // <op>(<args>)
240 1 : return p.makeOp(destLit, makeObjID(dbTag, 1), 0, destPos)
241 1 : }
242 :
243 1 : destID := p.parseObjID(destPos, destLit)
244 1 :
245 1 : pos, tok, lit := p.s.Scan()
246 1 : switch tok {
247 1 : case token.PERIOD:
248 1 : // <obj>.<op>(<args>)
249 1 : if !p.objs[destID] {
250 1 : if p.opts.allowUndefinedObjs {
251 1 : p.objs[destID] = true
252 1 : } else {
253 0 : panic(p.errorf(destPos, "unknown object: %s", destID))
254 : }
255 : }
256 1 : _, methodLit := p.scanToken(token.IDENT)
257 1 : return p.makeOp(methodLit, destID, 0, destPos)
258 :
259 1 : case token.ASSIGN:
260 1 : // <obj> = <obj>.<op>(<args>)
261 1 : srcPos, srcLit := p.scanToken(token.IDENT)
262 1 : srcID := p.parseObjID(srcPos, srcLit)
263 1 : if !p.objs[srcID] {
264 0 : if p.opts.allowUndefinedObjs {
265 0 : p.objs[srcID] = true
266 0 : } else {
267 0 : panic(p.errorf(srcPos, "unknown object %q", srcLit))
268 : }
269 : }
270 1 : p.scanToken(token.PERIOD)
271 1 : _, methodLit := p.scanToken(token.IDENT)
272 1 : p.objs[destID] = true
273 1 : return p.makeOp(methodLit, srcID, destID, srcPos)
274 : }
275 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
276 : }
277 :
278 1 : func (p *parser) parseObjID(pos token.Pos, str string) objID {
279 1 : id, err := parseObjID(str)
280 1 : if err != nil {
281 1 : panic(p.errorf(pos, "%s", err))
282 : }
283 1 : return id
284 : }
285 :
286 1 : func (p *parser) parseUserKey(pos token.Pos, lit string) UserKey {
287 1 : s, err := strconv.Unquote(lit)
288 1 : if err != nil {
289 0 : panic(p.errorf(pos, "%s", err))
290 : }
291 1 : if len(s) == 0 {
292 1 : return nil
293 1 : }
294 1 : if p.opts.parseFormattedUserKey == nil {
295 1 : return []byte(s)
296 1 : }
297 0 : key, err := p.opts.parseFormattedUserKey(s)
298 0 : if err != nil {
299 0 : panic(p.errorf(pos, "%s", err))
300 : }
301 0 : return key
302 : }
303 :
304 1 : func (p *parser) parseUserKeySuffix(pos token.Pos, lit string) UserKeySuffix {
305 1 : s, err := strconv.Unquote(lit)
306 1 : if err != nil {
307 0 : panic(p.errorf(pos, "%s", err))
308 : }
309 1 : if len(s) == 0 {
310 1 : return nil
311 1 : }
312 1 : if p.opts.parseFormattedUserKeySuffix == nil {
313 1 : return []byte(s)
314 1 : }
315 0 : suffix, err := p.opts.parseFormattedUserKeySuffix(s)
316 0 : if err != nil {
317 0 : panic(p.errorf(pos, "%s", err))
318 : }
319 0 : return suffix
320 : }
321 :
322 1 : func unquoteBytes(lit string) []byte {
323 1 : s, err := strconv.Unquote(lit)
324 1 : if err != nil {
325 0 : panic(err)
326 : }
327 1 : if len(s) == 0 {
328 1 : return nil
329 1 : }
330 1 : return []byte(s)
331 : }
332 :
333 1 : func (p *parser) parseArgs(op op, methodName string, args []interface{}) {
334 1 : pos, list := p.parseList()
335 1 : p.scanToken(token.SEMICOLON)
336 1 :
337 1 : // The last argument can have variable length.
338 1 : var varArg interface{}
339 1 : if len(args) > 0 {
340 1 : switch args[len(args)-1].(type) {
341 1 : case *[]objID, *[]pebble.KeyRange, *[]pebble.CheckpointSpan, *[]pebble.DownloadSpan, *[]externalObjWithBounds:
342 1 : varArg = args[len(args)-1]
343 1 : args = args[:len(args)-1]
344 : }
345 : }
346 :
347 1 : if len(list) < len(args) {
348 1 : panic(p.errorf(pos, "%s: not enough arguments", methodName))
349 : }
350 1 : if len(list) > len(args) && varArg == nil {
351 0 : panic(p.errorf(pos, "%s: too many arguments", methodName))
352 : }
353 :
354 1 : for i := range args {
355 1 : elem := list[i]
356 1 : switch t := args[i].(type) {
357 1 : case *uint32:
358 1 : elem.expectToken(p, token.INT)
359 1 : val, err := strconv.ParseUint(elem.lit, 10, 32)
360 1 : if err != nil {
361 0 : panic(p.errorf(elem.pos, "error parsing %q: %s", elem.lit, err))
362 : }
363 1 : *t = uint32(val)
364 :
365 0 : case *uint64:
366 0 : elem.expectToken(p, token.INT)
367 0 : val, err := strconv.ParseUint(elem.lit, 10, 64)
368 0 : if err != nil {
369 0 : panic(p.errorf(elem.pos, "error parsing %q: %s", elem.lit, err))
370 : }
371 0 : *t = val
372 :
373 1 : case *UserKey:
374 1 : elem.expectToken(p, token.STRING)
375 1 : *t = p.parseUserKey(elem.pos, elem.lit)
376 :
377 1 : case *UserKeySuffix:
378 1 : elem.expectToken(p, token.STRING)
379 1 : *t = p.parseUserKeySuffix(elem.pos, elem.lit)
380 :
381 1 : case *[]byte:
382 1 : elem.expectToken(p, token.STRING)
383 1 : *t = unquoteBytes(elem.lit)
384 :
385 1 : case *bool:
386 1 : elem.expectToken(p, token.IDENT)
387 1 : b, err := strconv.ParseBool(elem.lit)
388 1 : if err != nil {
389 0 : panic(p.errorf(elem.pos, "error parsing %q: %s", elem.lit, err))
390 : }
391 1 : *t = b
392 :
393 1 : case *objID:
394 1 : elem.expectToken(p, token.IDENT)
395 1 : *t = p.parseObjID(elem.pos, elem.lit)
396 :
397 1 : case *pebble.FormatMajorVersion:
398 1 : elem.expectToken(p, token.INT)
399 1 : val, err := strconv.ParseUint(elem.lit, 10, 64)
400 1 : if err != nil {
401 0 : panic(p.errorf(elem.pos, "error parsing %q: %s", elem.lit, err))
402 : }
403 1 : *t = pebble.FormatMajorVersion(val)
404 :
405 0 : default:
406 0 : panic(p.errorf(pos, "%s: unsupported arg[%d] type: %T", methodName, i, args[i]))
407 : }
408 : }
409 :
410 1 : if varArg != nil {
411 1 : list = list[len(args):]
412 1 : switch t := varArg.(type) {
413 1 : case *[]objID:
414 1 : *t = p.parseObjIDs(list)
415 1 : case *[]pebble.KeyRange:
416 1 : *t = p.parseKeyRanges(list)
417 1 : case *[]pebble.CheckpointSpan:
418 1 : *t = p.parseCheckpointSpans(list)
419 1 : case *[]pebble.DownloadSpan:
420 1 : *t = p.parseDownloadSpans(list)
421 1 : case *[]externalObjWithBounds:
422 1 : *t = p.parseExternalObjsWithBounds(list)
423 0 : default:
424 0 : // We already checked for these types when we set varArgs.
425 0 : panic("unreachable")
426 : }
427 : }
428 : }
429 :
430 : type listElem struct {
431 : pos token.Pos
432 : tok token.Token
433 : lit string
434 : }
435 :
436 1 : func (e listElem) expectToken(p *parser, expTok token.Token) {
437 1 : if e.tok != expTok {
438 0 : panic(p.errorf(e.pos, "unexpected token: %q", p.tokenf(e.tok, e.lit)))
439 : }
440 : }
441 :
442 : // pop checks that the first element of the list matches the expected token,
443 : // removes it from the list and returns the pos and literal.
444 1 : func (p *parser) pop(list *[]listElem, expTok token.Token) (token.Pos, string) {
445 1 : (*list)[0].expectToken(p, expTok)
446 1 : pos := (*list)[0].pos
447 1 : lit := (*list)[0].lit
448 1 : (*list) = (*list)[1:]
449 1 : return pos, lit
450 1 : }
451 :
452 : // popLit checks that the first element of the list matches the expected token,
453 : // removes it from the list and returns the pos and literal.
454 1 : func (p *parser) popLit(list *[]listElem, expTok token.Token) string {
455 1 : (*list)[0].expectToken(p, expTok)
456 1 : lit := (*list)[0].lit
457 1 : (*list) = (*list)[1:]
458 1 : return lit
459 1 : }
460 :
461 : // parseKeyRange parses an arbitrary number of comma separated STRING/IDENT/INT
462 : // tokens surrounded by parens.
463 1 : func (p *parser) parseList() (token.Pos, []listElem) {
464 1 : p.scanToken(token.LPAREN)
465 1 : var list []listElem
466 1 : for {
467 1 : pos, tok, lit := p.s.Scan()
468 1 : if len(list) == 0 && tok == token.RPAREN {
469 1 : return pos, nil
470 1 : }
471 :
472 1 : switch tok {
473 1 : case token.STRING, token.IDENT, token.INT:
474 1 : list = append(list, listElem{
475 1 : pos: pos,
476 1 : tok: tok,
477 1 : lit: lit,
478 1 : })
479 1 : pos, tok, lit = p.s.Scan()
480 1 : switch tok {
481 1 : case token.COMMA:
482 1 : continue
483 1 : case token.RPAREN:
484 1 : return pos, list
485 : }
486 : }
487 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
488 : }
489 : }
490 :
491 1 : func (p *parser) parseObjIDs(list []listElem) []objID {
492 1 : res := make([]objID, len(list))
493 1 : for i, elem := range list {
494 1 : res[i] = p.parseObjID(elem.pos, elem.lit)
495 1 : }
496 1 : return res
497 : }
498 :
499 1 : func (p *parser) parseKeys(list []listElem) []UserKey {
500 1 : res := make([]UserKey, len(list))
501 1 : for i := range res {
502 1 : res[i] = p.parseUserKey(p.pop(&list, token.STRING))
503 1 : }
504 1 : return res
505 : }
506 :
507 1 : func (p *parser) parseKeyRanges(list []listElem) []pebble.KeyRange {
508 1 : keys := p.parseKeys(list)
509 1 : if len(keys)%2 != 0 {
510 0 : panic(p.errorf(list[0].pos, "expected even number of keys"))
511 : }
512 1 : res := make([]pebble.KeyRange, len(keys)/2)
513 1 : for i := range res {
514 1 : res[i].Start = keys[2*i]
515 1 : res[i].End = keys[2*i+1]
516 1 : }
517 1 : return res
518 : }
519 :
520 1 : func (p *parser) parseCheckpointSpans(list []listElem) []pebble.CheckpointSpan {
521 1 : keys := p.parseKeys(list)
522 1 : if len(keys)%2 == 1 {
523 0 : panic(p.errorf(list[0].pos, "expected even number of keys"))
524 : }
525 1 : if len(keys) == 0 {
526 1 : // Necessary for round-trip tests which differentiate between nil and empty slice.
527 1 : return nil
528 1 : }
529 1 : res := make([]pebble.CheckpointSpan, len(keys)/2)
530 1 : for i := range res {
531 1 : res[i] = pebble.CheckpointSpan{
532 1 : Start: keys[i*2],
533 1 : End: keys[i*2+1],
534 1 : }
535 1 : }
536 1 : return res
537 : }
538 :
539 1 : func (p *parser) parseDownloadSpans(list []listElem) []pebble.DownloadSpan {
540 1 : if len(list)%3 != 0 {
541 0 : panic(p.errorf(list[0].pos, "expected 3k args"))
542 : }
543 1 : res := make([]pebble.DownloadSpan, len(list)/3)
544 1 : for i := range res {
545 1 : res[i] = pebble.DownloadSpan{
546 1 : StartKey: p.parseUserKey(p.pop(&list, token.STRING)),
547 1 : EndKey: p.parseUserKey(p.pop(&list, token.STRING)),
548 1 : ViaBackingFileDownload: p.popLit(&list, token.IDENT) == "true",
549 1 : }
550 1 : }
551 1 : return res
552 : }
553 :
554 1 : func (p *parser) parseExternalObjsWithBounds(list []listElem) []externalObjWithBounds {
555 1 : const numArgs = 5
556 1 : if len(list)%5 != 0 {
557 0 : panic(p.errorf(list[0].pos, "expected number of arguments to be multiple of %d", numArgs))
558 : }
559 1 : objs := make([]externalObjWithBounds, len(list)/numArgs)
560 1 : for i := range objs {
561 1 : objs[i] = externalObjWithBounds{
562 1 : externalObjID: p.parseObjID(p.pop(&list, token.IDENT)),
563 1 : bounds: pebble.KeyRange{
564 1 : Start: p.parseUserKey(p.pop(&list, token.STRING)),
565 1 : End: p.parseUserKey(p.pop(&list, token.STRING)),
566 1 : },
567 1 : }
568 1 : if syntheticSuffix := p.parseUserKeySuffix(p.pop(&list, token.STRING)); len(syntheticSuffix) > 0 {
569 1 : objs[i].syntheticSuffix = block.SyntheticSuffix(syntheticSuffix)
570 1 : }
571 :
572 1 : syntheticPrefix := p.parseUserKey(p.pop(&list, token.STRING))
573 1 : if len(syntheticPrefix) > 0 {
574 1 : if !bytes.HasPrefix(objs[i].bounds.Start, syntheticPrefix) {
575 0 : panic(fmt.Sprintf("invalid synthetic prefix %q %q", objs[i].bounds.Start, syntheticPrefix))
576 : }
577 1 : if !bytes.HasPrefix(objs[i].bounds.End, syntheticPrefix) {
578 0 : panic(fmt.Sprintf("invalid synthetic prefix %q %q", objs[i].bounds.End, syntheticPrefix))
579 : }
580 1 : objs[i].syntheticPrefix = block.SyntheticPrefix(syntheticPrefix)
581 : }
582 : }
583 1 : return objs
584 : }
585 :
586 1 : func (p *parser) scanToken(expected token.Token) (pos token.Pos, lit string) {
587 1 : pos, tok, lit := p.s.Scan()
588 1 : if tok != expected {
589 0 : panic(p.errorf(pos, "unexpected token: %q", p.tokenf(tok, lit)))
590 : }
591 1 : return pos, lit
592 : }
593 :
594 1 : func (p *parser) makeOp(methodName string, receiverID, targetID objID, pos token.Pos) op {
595 1 : info := methods[methodName]
596 1 : if info == nil {
597 1 : panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName))
598 : }
599 1 : if info.validTags&(1<<receiverID.tag()) == 0 {
600 1 : panic(p.errorf(pos, "%s.%s: %s is not a method on %s",
601 1 : receiverID, methodName, methodName, receiverID))
602 : }
603 :
604 1 : op := info.constructor()
605 1 : receiver, target, args := opArgs(op)
606 1 :
607 1 : // The form of an operation is:
608 1 : // [target =] receiver.method(args)
609 1 : //
610 1 : // The receiver is the object the operation will be called on, which can be
611 1 : // any valid ID. Certain operations such as Ingest are only valid on the DB
612 1 : // object. That is indicated by opArgs returning a nil receiver.
613 1 : if receiver != nil {
614 1 : *receiver = receiverID
615 1 : } else if receiverID.tag() != dbTag {
616 0 : panic(p.errorf(pos, "unknown op %s.%s", receiverID, methodName))
617 : }
618 :
619 : // The target is the object that will be assigned the result of an object
620 : // creation operation such as newBatchOp or newIterOp.
621 1 : if target != nil {
622 1 : // It is invalid to not have a targetID for a method which generates a new
623 1 : // object.
624 1 : if targetID == 0 {
625 1 : panic(p.errorf(pos, "assignment expected for %s.%s", receiverID, methodName))
626 : }
627 : // It is invalid to try to assign to the DB object.
628 1 : if targetID.tag() == dbTag {
629 0 : panic(p.errorf(pos, "cannot use %s as target of assignment", targetID))
630 : }
631 1 : *target = targetID
632 1 : } else if targetID != 0 {
633 1 : panic(p.errorf(pos, "cannot use %s.%s in assignment", receiverID, methodName))
634 : }
635 :
636 1 : p.parseArgs(op, methodName, args)
637 1 : return op
638 : }
639 :
640 0 : func (p *parser) tokenf(tok token.Token, lit string) string {
641 0 : if tok.IsLiteral() {
642 0 : return lit
643 0 : }
644 0 : return tok.String()
645 : }
646 :
647 1 : func (p *parser) errorf(pos token.Pos, format string, args ...interface{}) error {
648 1 : return errors.New("metamorphic test internal error: " + p.fset.Position(pos).String() + ": " + fmt.Sprintf(format, args...))
649 1 : }
650 :
651 : // computeDerivedFields makes one pass through the provided operations, filling
652 : // any derived fields. This pass must happen before execution because concurrent
653 : // execution depends on these fields.
654 1 : func computeDerivedFields(ops []op) {
655 1 : iterToReader := make(map[objID]objID)
656 1 : objToDB := make(map[objID]objID)
657 1 : for i := range ops {
658 1 : switch v := ops[i].(type) {
659 1 : case *newSnapshotOp:
660 1 : objToDB[v.snapID] = v.dbID
661 1 : case *newIterOp:
662 1 : iterToReader[v.iterID] = v.readerID
663 1 : dbReaderID := v.readerID
664 1 : if dbReaderID.tag() != dbTag {
665 1 : dbReaderID = objToDB[dbReaderID]
666 1 : }
667 1 : objToDB[v.iterID] = dbReaderID
668 1 : v.derivedDBID = dbReaderID
669 1 : case *newIterUsingCloneOp:
670 1 : v.derivedReaderID = iterToReader[v.existingIterID]
671 1 : iterToReader[v.iterID] = v.derivedReaderID
672 1 : objToDB[v.iterID] = objToDB[v.existingIterID]
673 1 : case *iterSetOptionsOp:
674 1 : v.derivedReaderID = iterToReader[v.iterID]
675 1 : case *iterFirstOp:
676 1 : v.derivedReaderID = iterToReader[v.iterID]
677 1 : case *iterLastOp:
678 1 : v.derivedReaderID = iterToReader[v.iterID]
679 1 : case *iterSeekGEOp:
680 1 : v.derivedReaderID = iterToReader[v.iterID]
681 1 : case *iterSeekPrefixGEOp:
682 1 : v.derivedReaderID = iterToReader[v.iterID]
683 1 : case *iterSeekLTOp:
684 1 : v.derivedReaderID = iterToReader[v.iterID]
685 1 : case *iterNextOp:
686 1 : v.derivedReaderID = iterToReader[v.iterID]
687 1 : case *iterNextPrefixOp:
688 1 : v.derivedReaderID = iterToReader[v.iterID]
689 1 : case *iterCanSingleDelOp:
690 1 : v.derivedReaderID = iterToReader[v.iterID]
691 1 : case *iterPrevOp:
692 1 : v.derivedReaderID = iterToReader[v.iterID]
693 1 : case *newBatchOp:
694 1 : objToDB[v.batchID] = v.dbID
695 1 : case *newIndexedBatchOp:
696 1 : objToDB[v.batchID] = v.dbID
697 1 : case *applyOp:
698 1 : if derivedDBID, ok := objToDB[v.batchID]; ok && v.writerID.tag() != dbTag {
699 0 : objToDB[v.writerID] = derivedDBID
700 0 : }
701 1 : case *getOp:
702 1 : if derivedDBID, ok := objToDB[v.readerID]; ok {
703 1 : v.derivedDBID = derivedDBID
704 1 : }
705 1 : case *batchCommitOp:
706 1 : v.dbID = objToDB[v.batchID]
707 1 : case *closeOp:
708 1 : if v.objID.tag() == dbTag {
709 1 : // Find all objects that use this db.
710 1 : v.affectedObjects = nil
711 1 : for obj, db := range objToDB {
712 1 : if db == v.objID {
713 1 : v.affectedObjects = append(v.affectedObjects, obj)
714 1 : }
715 : }
716 : // Sort so the output is deterministic.
717 1 : slices.Sort(v.affectedObjects)
718 1 : } else if dbID, ok := objToDB[v.objID]; ok {
719 1 : v.affectedObjects = objIDSlice{dbID}
720 1 : }
721 1 : case *dbRestartOp:
722 1 : // Find all objects that use this db.
723 1 : v.affectedObjects = nil
724 1 : for obj, db := range objToDB {
725 1 : if db == v.dbID {
726 1 : v.affectedObjects = append(v.affectedObjects, obj)
727 1 : }
728 : }
729 : // Sort so the output is deterministic.
730 1 : slices.Sort(v.affectedObjects)
731 1 : case *ingestOp:
732 1 : v.derivedDBIDs = make([]objID, len(v.batchIDs))
733 1 : for i := range v.batchIDs {
734 1 : v.derivedDBIDs[i] = objToDB[v.batchIDs[i]]
735 1 : }
736 1 : case *ingestAndExciseOp:
737 1 : v.derivedDBID = objToDB[v.batchID]
738 1 : case *deleteOp:
739 1 : derivedDBID := v.writerID
740 1 : if v.writerID.tag() != dbTag {
741 1 : derivedDBID = objToDB[v.writerID]
742 1 : }
743 1 : v.derivedDBID = derivedDBID
744 : }
745 : }
746 : }
|