Example #1
0
func (sm *StackMachine) popRangeOptions() fdb.RangeOptions {
	var limit int
	switch l := sm.waitAndPop().item.(type) {
	case int64:
		limit = int(l)
	}
	ro := fdb.RangeOptions{Limit: limit, Reverse: int64ToBool(sm.waitAndPop().item.(int64)), Mode: fdb.StreamingMode(sm.waitAndPop().item.(int64) + 1)}
	return ro
}
Example #2
0
func (sm *StackMachine) processInst(idx int, inst tuple.Tuple) {
	defer func() {
		if r := recover(); r != nil {
			switch r := r.(type) {
			case fdb.Error:
				sm.store(idx, tuple.Tuple{[]byte("ERROR"), []byte(fmt.Sprintf("%d", int(r)))}.Pack())
			default:
				panic(r)
			}
		}
	}()

	var e error

	op := string(inst[0].([]byte))
	if sm.verbose {
		fmt.Printf("Instruction is %s (%v)\n", op, sm.prefix)
		fmt.Printf("Stack from [")
		sm.dumpStack()
		fmt.Printf(" ]\n")
	}

	var obj interface{}
	switch {
	case strings.HasSuffix(op, "_SNAPSHOT"):
		obj = sm.tr.Snapshot()
		op = op[:len(op)-9]
	case strings.HasSuffix(op, "_DATABASE"):
		obj = db
		op = op[:len(op)-9]
	default:
		obj = sm.tr
	}

	switch string(op) {
	case "PUSH":
		sm.store(idx, inst[1])
	case "DUP":
		entry := sm.stack[len(sm.stack)-1]
		sm.store(entry.idx, entry.item)
	case "EMPTY_STACK":
		sm.stack = []stackEntry{}
		sm.stack = make([]stackEntry, 0)
	case "SWAP":
		idx := sm.waitAndPop().item.(int64)
		sm.stack[len(sm.stack)-1], sm.stack[len(sm.stack)-1-int(idx)] = sm.stack[len(sm.stack)-1-int(idx)], sm.stack[len(sm.stack)-1]
	case "POP":
		sm.stack = sm.stack[:len(sm.stack)-1]
	case "SUB":
		sm.store(idx, sm.waitAndPop().item.(int64)-sm.waitAndPop().item.(int64))
	case "NEW_TRANSACTION":
		sm.tr, e = db.CreateTransaction()
		if e != nil {
			panic(e)
		}
	case "ON_ERROR":
		sm.store(idx, sm.tr.OnError(fdb.Error(int(sm.waitAndPop().item.(int64)))))
	case "GET_READ_VERSION":
		sm.lastVersion = obj.(fdb.ReadTransaction).GetReadVersion().GetOrPanic()
		sm.store(idx, []byte("GOT_READ_VERSION"))
	case "SET":
		switch o := obj.(type) {
		case fdb.Database:
			e = o.Set(fdb.Key(sm.waitAndPop().item.([]byte)), sm.waitAndPop().item.([]byte))
			if e != nil {
				panic(e)
			}
			sm.store(idx, []byte("RESULT_NOT_PRESENT"))
		case fdb.Transaction:
			o.Set(fdb.Key(sm.waitAndPop().item.([]byte)), sm.waitAndPop().item.([]byte))
		}
	case "LOG_STACK":
		prefix := sm.waitAndPop().item.([]byte)
		for i := len(sm.stack) - 1; i >= 0; i-- {
			if i%100 == 0 {
				sm.tr.Commit().GetOrPanic()
			}

			el := sm.waitAndPop()

			var keyt tuple.Tuple
			keyt = append(keyt, int64(i))
			keyt = append(keyt, int64(el.idx))
			pk := append(prefix, keyt.Pack()...)

			var valt tuple.Tuple
			valt = append(valt, el.item)
			pv := valt.Pack()

			vl := 40000
			if len(pv) < vl {
				vl = len(pv)
			}

			sm.tr.Set(fdb.Key(pk), pv[:vl])
		}
		sm.tr.Commit().GetOrPanic()
	case "GET":
		switch o := obj.(type) {
		case fdb.Database:
			v, e := db.Get(fdb.Key(sm.waitAndPop().item.([]byte)))
			if e != nil {
				panic(e)
			}
			if v != nil {
				sm.store(idx, v)
			} else {
				sm.store(idx, []byte("RESULT_NOT_PRESENT"))
			}
		case fdb.ReadTransaction:
			sm.store(idx, o.Get(fdb.Key(sm.waitAndPop().item.([]byte))))
		}
	case "COMMIT":
		sm.store(idx, sm.tr.Commit())
	case "RESET":
		sm.tr.Reset()
	case "CLEAR":
		switch o := obj.(type) {
		case fdb.Database:
			e := db.Clear(fdb.Key(sm.waitAndPop().item.([]byte)))
			if e != nil {
				panic(e)
			}
			sm.store(idx, []byte("RESULT_NOT_PRESENT"))
		case fdb.Transaction:
			o.Clear(fdb.Key(sm.waitAndPop().item.([]byte)))
		}
	case "SET_READ_VERSION":
		sm.tr.SetReadVersion(sm.lastVersion)
	case "WAIT_FUTURE":
		entry := sm.waitAndPop()
		sm.store(entry.idx, entry.item)
	case "GET_COMMITTED_VERSION":
		sm.lastVersion, e = sm.tr.GetCommittedVersion()
		if e != nil {
			panic(e)
		}
		sm.store(idx, []byte("GOT_COMMITTED_VERSION"))
	case "GET_KEY":
		sel := fdb.KeySelector{fdb.Key(sm.waitAndPop().item.([]byte)), int64ToBool(sm.waitAndPop().item.(int64)), int(sm.waitAndPop().item.(int64))}
		switch o := obj.(type) {
		case fdb.Database:
			v, e := o.GetKey(sel)
			if e != nil {
				panic(e)
			}
			sm.store(idx, []byte(v))
		case fdb.ReadTransaction:
			sm.store(idx, o.GetKey(sel))
		}
	case "GET_RANGE":
		begin := fdb.Key(sm.waitAndPop().item.([]byte))
		end := fdb.Key(sm.waitAndPop().item.([]byte))
		var limit int
		switch l := sm.waitAndPop().item.(type) {
		case int64:
			limit = int(l)
		}
		reverse := int64ToBool(sm.waitAndPop().item.(int64))
		mode := sm.waitAndPop().item.(int64)
		switch o := obj.(type) {
		case fdb.Database:
			v, e := db.GetRange(fdb.KeyRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)})
			if e != nil {
				panic(e)
			}
			sm.pushRange(idx, v)
		case fdb.ReadTransaction:
			sm.pushRange(idx, o.GetRange(fdb.KeyRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}).GetSliceOrPanic())
		}
	case "CLEAR_RANGE":
		switch o := obj.(type) {
		case fdb.Database:
			e := o.ClearRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))})
			if e != nil {
				panic(e)
			}
			sm.store(idx, []byte("RESULT_NOT_PRESENT"))
		case fdb.Transaction:
			o.ClearRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))})
		}
	case "GET_RANGE_STARTS_WITH":
		prefix := sm.waitAndPop().item.([]byte)
		var limit int
		switch l := sm.waitAndPop().item.(type) {
		case int64:
			limit = int(l)
		}
		reverse := int64ToBool(sm.waitAndPop().item.(int64))
		mode := sm.waitAndPop().item.(int64)
		er, e := fdb.PrefixRange(prefix)
		if e != nil {
			panic(e)
		}
		switch o := obj.(type) {
		case fdb.Database:
			v, e := db.GetRange(er, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)})
			if e != nil {
				panic(e)
			}
			sm.pushRange(idx, v)
		case fdb.ReadTransaction:
			sm.pushRange(idx, o.GetRange(er, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}).GetSliceOrPanic())
		}
	case "GET_RANGE_SELECTOR":
		begin := fdb.KeySelector{Key: fdb.Key(sm.waitAndPop().item.([]byte)), OrEqual: int64ToBool(sm.waitAndPop().item.(int64)), Offset: int(sm.waitAndPop().item.(int64))}
		end := fdb.KeySelector{Key: fdb.Key(sm.waitAndPop().item.([]byte)), OrEqual: int64ToBool(sm.waitAndPop().item.(int64)), Offset: int(sm.waitAndPop().item.(int64))}
		var limit int
		switch l := sm.waitAndPop().item.(type) {
		case int64:
			limit = int(l)
		}
		reverse := int64ToBool(sm.waitAndPop().item.(int64))
		mode := sm.waitAndPop().item.(int64)
		switch o := obj.(type) {
		case fdb.Database:
			v, e := db.GetRange(fdb.SelectorRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)})
			if e != nil {
				panic(e)
			}
			sm.pushRange(idx, v)
		case fdb.ReadTransaction:
			sm.pushRange(idx, o.GetRange(fdb.SelectorRange{begin, end}, fdb.RangeOptions{Limit: int(limit), Reverse: reverse, Mode: fdb.StreamingMode(mode + 1)}).GetSliceOrPanic())
		}
	case "CLEAR_RANGE_STARTS_WITH":
		prefix := sm.waitAndPop().item.([]byte)
		er, e := fdb.PrefixRange(prefix)
		if e != nil {
			panic(e)
		}
		switch o := obj.(type) {
		case fdb.Database:
			e := o.ClearRange(er)
			if e != nil {
				panic(e)
			}
			sm.store(idx, []byte("RESULT_NOT_PRESENT"))
		case fdb.Transaction:
			o.ClearRange(er)
		}
	case "TUPLE_PACK":
		var t tuple.Tuple
		count := sm.waitAndPop().item.(int64)
		for i := 0; i < int(count); i++ {
			t = append(t, sm.waitAndPop().item)
		}
		sm.store(idx, t.Pack())
	case "TUPLE_UNPACK":
		t, e := tuple.Unpack(sm.waitAndPop().item.([]byte))
		if e != nil {
			panic(e)
		}
		for _, el := range t {
			sm.store(idx, tuple.Tuple{el}.Pack())
		}
	case "TUPLE_RANGE":
		var t tuple.Tuple
		count := sm.waitAndPop().item.(int64)
		for i := 0; i < int(count); i++ {
			t = append(t, sm.waitAndPop().item)
		}
		kr := t.Range()
		sm.store(idx, kr.Begin)
		sm.store(idx, kr.End)
	case "START_THREAD":
		newsm := newStackMachine(sm.waitAndPop().item.([]byte), verbose)
		sm.threads.Add(1)
		go func() {
			newsm.Run()
			sm.threads.Done()
		}()
	case "WAIT_EMPTY":
		prefix := sm.waitAndPop().item.([]byte)
		er, e := fdb.PrefixRange(prefix)
		if e != nil {
			panic(e)
		}
		db.Transact(func(tr fdb.Transaction) (interface{}, error) {
			v := tr.GetRange(er, fdb.RangeOptions{}).GetSliceOrPanic()
			if len(v) != 0 {
				panic(fdb.Error(1020))
			}
			return nil, nil
		})
		sm.store(idx, []byte("WAITED_FOR_EMPTY"))
	case "READ_CONFLICT_RANGE":
		e = sm.tr.AddReadConflictRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))})
		if e != nil {
			panic(e)
		}
		sm.store(idx, []byte("SET_CONFLICT_RANGE"))
	case "WRITE_CONFLICT_RANGE":
		e = sm.tr.AddWriteConflictRange(fdb.KeyRange{fdb.Key(sm.waitAndPop().item.([]byte)), fdb.Key(sm.waitAndPop().item.([]byte))})
		if e != nil {
			panic(e)
		}
		sm.store(idx, []byte("SET_CONFLICT_RANGE"))
	case "READ_CONFLICT_KEY":
		e = sm.tr.AddReadConflictKey(fdb.Key(sm.waitAndPop().item.([]byte)))
		if e != nil {
			panic(e)
		}
		sm.store(idx, []byte("SET_CONFLICT_KEY"))
	case "WRITE_CONFLICT_KEY":
		e = sm.tr.AddWriteConflictKey(fdb.Key(sm.waitAndPop().item.([]byte)))
		if e != nil {
			panic(e)
		}
		sm.store(idx, []byte("SET_CONFLICT_KEY"))
	case "ATOMIC_OP":
		opname := strings.Title(strings.ToLower(string(sm.waitAndPop().item.([]byte))))
		key := fdb.Key(sm.waitAndPop().item.([]byte))
		value := sm.waitAndPop().item.([]byte)
		reflect.ValueOf(obj).MethodByName(opname).Call([]reflect.Value{reflect.ValueOf(key), reflect.ValueOf(value)})
		switch obj.(type) {
		case fdb.Database:
			sm.store(idx, []byte("RESULT_NOT_PRESENT"))
		}
	case "DISABLE_WRITE_CONFLICT":
		sm.tr.Options().SetNextWriteNoWriteConflictRange()
	case "CANCEL":
		sm.tr.Cancel()
	case "UNIT_TESTS":
	default:
		log.Fatalf("Unhandled operation %s\n", string(inst[0].([]byte)))
	}

	if sm.verbose {
		fmt.Printf("        to [")
		sm.dumpStack()
		fmt.Printf(" ]\n\n")
	}

	runtime.Gosched()
}