mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-28 22:57:16 +00:00
1028 lines
19 KiB
Go
1028 lines
19 KiB
Go
//nolint:goconst
|
|
package mdbx
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"runtime"
|
|
"testing"
|
|
)
|
|
|
|
func TestCursor_Txn(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var db DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
db, err = txn.OpenRoot(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_txn := cur.Txn()
|
|
if _txn == nil {
|
|
t.Errorf("nil cursor txn")
|
|
}
|
|
|
|
cur.Close()
|
|
|
|
_txn = cur.Txn()
|
|
if _txn != nil {
|
|
t.Errorf("non-nil cursor txn")
|
|
}
|
|
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestCursor_DBI(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
db, err := txn.OpenDBI("db", Create, nil, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dbcur := cur.DBI()
|
|
if dbcur != db {
|
|
cur.Close()
|
|
return fmt.Errorf("unequal db: %v != %v", dbcur, db)
|
|
}
|
|
cur.Close()
|
|
dbcur = cur.DBI()
|
|
if dbcur == db {
|
|
return fmt.Errorf("db: %v", dbcur)
|
|
}
|
|
if dbcur != ^DBI(0) {
|
|
return fmt.Errorf("db: %v", dbcur)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestCursor_Close(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
|
|
txn, err := env.BeginTxn(nil, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer txn.Abort()
|
|
|
|
db, err := txn.OpenDBI("testing", Create, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cur.Close()
|
|
cur.Close()
|
|
err = cur.Put([]byte("closedput"), []byte("shouldfail"), 0)
|
|
if err == nil {
|
|
t.Fatalf("expected error: put on closed cursor")
|
|
}
|
|
}
|
|
|
|
func TestCursor_bytesBuffer(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
db, err := openRoot(env, 0)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
err = env.Update(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
k := new(bytes.Buffer)
|
|
k.WriteString("hello")
|
|
v := new(bytes.Buffer)
|
|
v.WriteString("world")
|
|
return cur.Put(k.Bytes(), v.Bytes(), 0)
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
k := new(bytes.Buffer)
|
|
k.WriteString("hello")
|
|
_k, v, err := cur.Get(k.Bytes(), nil, SetKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(_k, k.Bytes()) {
|
|
return fmt.Errorf("unexpected key: %q", _k)
|
|
}
|
|
if !bytes.Equal(v, []byte("world")) {
|
|
return fmt.Errorf("unexpected value: %q", v)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestCursor_PutReserve(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var db DBI
|
|
key := "reservekey"
|
|
val := "reserveval"
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
db, err = txn.CreateDBI("testing")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
|
|
p, err := cur.PutReserve([]byte(key), len(val), 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
copy(p, val)
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
dbval, err := txn.Get(db, []byte(key))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(dbval, []byte(val)) {
|
|
return fmt.Errorf("unexpected val %q != %q", dbval, val)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCursor_Get_KV(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var dbi DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
dbi, err = txn.OpenDBI("testdb", Create|DupSort, nil, nil)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
return
|
|
}
|
|
|
|
err = env.Update(func(txn *Txn) (err error) {
|
|
put := func(k, v []byte) {
|
|
if err == nil {
|
|
err = txn.Put(dbi, k, v, 0)
|
|
}
|
|
}
|
|
put([]byte("k1"), []byte("v1"))
|
|
put([]byte("k1"), []byte("v2"))
|
|
put([]byte("k1"), []byte("v3"))
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(dbi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
|
|
k, v, err := cur.Get([]byte("k1"), []byte("v0"), GetBothRange)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(k) != "k1" {
|
|
t.Errorf("unexpected key: %q (not %q)", k, "k1")
|
|
}
|
|
if string(v) != "v1" {
|
|
t.Errorf("unexpected value: %q (not %q)", k, "1")
|
|
}
|
|
|
|
_, _, err = cur.Get([]byte("k0"), []byte("v0"), GetBothRange)
|
|
if !IsErrno(err, NotFound) {
|
|
t.Errorf("unexpected error: %s", err)
|
|
}
|
|
|
|
_, _, err = cur.Get([]byte("k1"), []byte("v1"), GetBoth)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
}
|
|
}
|
|
|
|
func FromHex(in string) []byte {
|
|
out, err := hex.DecodeString(in)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestDupCmpExcludeSuffix32(t *testing.T) {
|
|
hash32Bytes := FromHex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
|
env := setup(t)
|
|
|
|
var dbi DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
dcmp := txn.GetCmpExcludeSuffix32()
|
|
dbi, err = txn.OpenDBI("testdb", Create|DupSort, nil, dcmp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if txn.DCmp(dbi, []byte{0}, append([]byte{0}, hash32Bytes...)) != 0 {
|
|
t.Errorf("broken order")
|
|
}
|
|
if txn.DCmp(dbi, []byte{0, 0}, append([]byte{0}, hash32Bytes...)) != 1 {
|
|
t.Errorf("broken order")
|
|
}
|
|
if txn.DCmp(dbi, hash32Bytes, append([]byte{0}, hash32Bytes...)) != -1 {
|
|
t.Errorf("broken order")
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
return
|
|
}
|
|
|
|
err = env.Update(func(txn *Txn) (err error) {
|
|
err = txn.Put(dbi, []byte{0}, hash32Bytes, Append|AppendDup)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
err = txn.Put(dbi, []byte{0}, append([]byte{0}, hash32Bytes...), AppendDup)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
err = txn.Put(dbi, []byte{0}, append([]byte{0, 0}, hash32Bytes...), AppendDup)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
err = txn.Put(dbi, []byte{1}, hash32Bytes, Append|AppendDup)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(dbi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer cur.Close()
|
|
k, v, err := cur.Get(nil, nil, Last)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(k, []byte{1}) {
|
|
t.Errorf("unexpected order: %x (not %x)", k, []byte{1})
|
|
}
|
|
if !bytes.Equal(v, hash32Bytes) {
|
|
t.Errorf("unexpected order: %x (not %x)", v, hash32Bytes)
|
|
}
|
|
|
|
_, _, err = cur.Get([]byte{0}, nil, First)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, v, err = cur.Get(nil, nil, FirstDup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(v, hash32Bytes) {
|
|
t.Errorf("unexpected order: %x (not %x)", v, hash32Bytes)
|
|
}
|
|
|
|
_, v, err = cur.Get(nil, nil, NextDup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(v, append([]byte{0}, hash32Bytes...)) {
|
|
t.Errorf("unexpected order: %x (not %x)", v, append([]byte{0}, hash32Bytes...))
|
|
}
|
|
_, v, err = cur.Get(nil, nil, NextDup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(v, append([]byte{0, 0}, hash32Bytes...)) {
|
|
t.Errorf("unexpected order: %x (not %x)", v, append([]byte{0, 0}, hash32Bytes...))
|
|
}
|
|
|
|
k, v, err = cur.Get(nil, nil, Next)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(k, []byte{1}) {
|
|
t.Errorf("unexpected order: %x (not %x)", k, []byte{1})
|
|
}
|
|
if !bytes.Equal(v, hash32Bytes) {
|
|
t.Errorf("unexpected order: %x (not %x)", v, hash32Bytes)
|
|
}
|
|
|
|
k, _, err = cur.Get([]byte{0}, []byte{40}, GetBothRange)
|
|
if !IsNotFound(err) {
|
|
t.Errorf("unexpected error: %v (key %x)", err, k)
|
|
}
|
|
|
|
_, v, err = cur.Get([]byte{0}, []byte{0}, GetBothRange)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(v, append([]byte{0}, hash32Bytes...)) {
|
|
t.Errorf("unexpected order: %x (not %x)", v, append([]byte{0}, hash32Bytes...))
|
|
}
|
|
|
|
_, v, err = cur.Get([]byte{0}, nil, SetRange)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(v, hash32Bytes) {
|
|
t.Errorf("unexpected order: %x (not %x)", v, hash32Bytes)
|
|
}
|
|
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
}
|
|
}
|
|
|
|
func TestCursor_Get_op_Set_bytesBuffer(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var dbi DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
dbi, err = txn.OpenDBI("testdb", Create|DupSort, nil, nil)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
return
|
|
}
|
|
|
|
err = env.Update(func(txn *Txn) (err error) {
|
|
put := func(k, v []byte) {
|
|
if err == nil {
|
|
err = txn.Put(dbi, k, v, 0)
|
|
}
|
|
}
|
|
put([]byte("k1"), []byte("v11"))
|
|
put([]byte("k1"), []byte("v12"))
|
|
put([]byte("k1"), []byte("v13"))
|
|
put([]byte("k2"), []byte("v21"))
|
|
put([]byte("k2"), []byte("v22"))
|
|
put([]byte("k2"), []byte("v23"))
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(dbi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
|
|
// Create bytes.Buffer values containing a amount of bytes. Byte
|
|
// slices returned from buf.Bytes() have a history of tricking the cgo
|
|
// argument checker.
|
|
var kbuf bytes.Buffer
|
|
kbuf.WriteString("k2")
|
|
|
|
k, _, err := cur.Get(kbuf.Bytes(), nil, Set)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(k) != kbuf.String() {
|
|
t.Errorf("unexpected key: %q (not %q)", k, kbuf.String())
|
|
}
|
|
|
|
// No guarantee is made about the return value of mdb_cursor_get when
|
|
// MDB_SET is the op, so its value is not checked as part of this test.
|
|
// That said, it is important that Cursor.Get not panic if given a
|
|
// short buffer as an input value for a Set op (despite that not really
|
|
// having any significance)
|
|
var vbuf bytes.Buffer
|
|
vbuf.WriteString("v22")
|
|
|
|
k, _, err = cur.Get(kbuf.Bytes(), vbuf.Bytes(), Set)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(k) != kbuf.String() {
|
|
t.Errorf("unexpected key: %q (not %q)", k, kbuf.String())
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Errorf("%s", err)
|
|
}
|
|
}
|
|
|
|
func TestCursor_Get_DupFixed(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
const datasize = 16
|
|
pagesize := os.Getpagesize()
|
|
numitems := (2 * pagesize / datasize) + 1
|
|
|
|
var dbi DBI
|
|
key := []byte("key")
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
dbi, err = txn.OpenDBI("test", DupSort|DupFixed|Create, nil, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := int64(0); i < int64(numitems); i++ {
|
|
err = txn.Put(dbi, key, []byte(fmt.Sprintf("%016x", i)), 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
var items [][]byte
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(dbi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
|
|
for {
|
|
k, first, err := cur.Get(nil, nil, NextNoDup)
|
|
if IsNotFound(err) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if string(k) != string(key) {
|
|
return fmt.Errorf("key: %s", k)
|
|
}
|
|
|
|
stride := len(first)
|
|
|
|
for {
|
|
_, v, err := cur.Get(nil, nil, NextMultiple)
|
|
if IsNotFound(err) {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
multi := WrapMulti(v, stride)
|
|
for i := 0; i < multi.Len(); i++ {
|
|
items = append(items, multi.Val(i))
|
|
}
|
|
}
|
|
}
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if len(items) != numitems {
|
|
t.Errorf("unexpected number of items: %d (!= %d)", len(items), numitems)
|
|
}
|
|
|
|
for i, b := range items {
|
|
expect := fmt.Sprintf("%016x", i)
|
|
if string(b) != expect {
|
|
t.Errorf("unexpected value: %q (!= %q)", b, expect)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCursor_Get_reverse(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var dbi DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
dbi, err = txn.OpenRoot(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = txn.Put(dbi, []byte("k0"), []byte("v0"), 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = txn.Put(dbi, []byte("k1"), []byte("v1"), 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
type Item struct{ k, v []byte }
|
|
var items []Item
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(dbi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
|
|
for {
|
|
k, v, err := cur.Get(nil, nil, Prev)
|
|
if IsNotFound(err) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
items = append(items, Item{k, v})
|
|
}
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expect := []Item{
|
|
{[]byte("k1"), []byte("v1")},
|
|
{[]byte("k0"), []byte("v0")},
|
|
}
|
|
if !reflect.DeepEqual(items, expect) {
|
|
t.Errorf("unexpected items %q (!= %q)", items, expect)
|
|
}
|
|
}
|
|
|
|
//func TestCursor_PutMulti(t *testing.T) {
|
|
// env := setup(t)
|
|
//
|
|
//
|
|
// key := []byte("k")
|
|
// items := [][]byte{
|
|
// []byte("v0"),
|
|
// []byte("v2"),
|
|
// []byte("v1"),
|
|
// }
|
|
// page := bytes.Join(items, nil)
|
|
// stride := 2
|
|
//
|
|
// var dbi DBI
|
|
// err := env.Update(func(txn *Txn) (err error) {
|
|
// dbi, err = txn.OpenDBISimple("test2", Create|DupSort|DupFixed)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
//
|
|
// cur, err := txn.OpenCursor(dbi)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// defer cur.Close()
|
|
//
|
|
// return cur.PutMulti(key, page, stride, 0)
|
|
// })
|
|
// if err != nil {
|
|
// t.Error(err)
|
|
// }
|
|
//
|
|
// expect := [][]byte{
|
|
// []byte("v0"),
|
|
// []byte("v1"),
|
|
// []byte("v2"),
|
|
// }
|
|
// var dbitems [][]byte
|
|
// err = env.View(func(txn *Txn) (err error) {
|
|
// cur, err := txn.OpenCursor(dbi)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// defer cur.Close()
|
|
//
|
|
// for {
|
|
// k, v, err := cur.Get(nil, nil, Next)
|
|
// if IsNotFound(err) {
|
|
// return nil
|
|
// }
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// if string(k) != "k" {
|
|
// return fmt.Errorf("key: %q", k)
|
|
// }
|
|
// dbitems = append(dbitems, v)
|
|
// }
|
|
// })
|
|
// if err != nil {
|
|
// t.Error(err)
|
|
// }
|
|
// if !reflect.DeepEqual(dbitems, expect) {
|
|
// t.Errorf("unexpected items: %q (!= %q)", dbitems, items)
|
|
// }
|
|
//}
|
|
|
|
func TestCursor_Del(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var db DBI
|
|
type Item struct{ k, v string }
|
|
items := []Item{
|
|
{"k0", "k0"},
|
|
{"k1", "k1"},
|
|
{"k2", "k2"},
|
|
}
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
db, err = txn.CreateDBI("testing")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, item := range items {
|
|
err := txn.Put(db, []byte(item.k), []byte(item.v), 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
err = env.Update(func(txn *Txn) (err error) {
|
|
txn.RawRead = true
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
item := items[1]
|
|
k, v, err := cur.Get([]byte(item.k), nil, SetKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(k, []byte(item.k)) {
|
|
return fmt.Errorf("found key %q (!= %q)", k, item.k)
|
|
}
|
|
if !bytes.Equal(v, []byte(item.v)) {
|
|
return fmt.Errorf("found value %q (!= %q)", k, item.v)
|
|
}
|
|
|
|
err = cur.Del(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
k, v, err = cur.Get(nil, nil, Next)
|
|
if err != nil {
|
|
return fmt.Errorf("post-delete: %v", err)
|
|
}
|
|
item = items[2]
|
|
if !bytes.Equal(k, []byte(item.k)) {
|
|
return fmt.Errorf("found key %q (!= %q)", k, item.k)
|
|
}
|
|
if !bytes.Equal(v, []byte(item.v)) {
|
|
return fmt.Errorf("found value %q (!= %q)", k, item.v)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
var newitems []Item
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
txn.RawRead = true
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
next := func(cur *Cursor) (k, v []byte, err error) { return cur.Get(nil, nil, Next) }
|
|
for k, v, err := next(cur); !IsNotFound(err); k, v, err = next(cur) {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newitems = append(newitems, Item{string(k), string(v)})
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
expectitems := []Item{
|
|
items[0],
|
|
items[2],
|
|
}
|
|
if !reflect.DeepEqual(newitems, expectitems) {
|
|
t.Errorf("unexpected items %q (!= %q)", newitems, expectitems)
|
|
}
|
|
}
|
|
|
|
// This test verifies the behavior of Cursor.Count when DupSort is provided.
|
|
func TestCursor_Count_DupSort(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var db DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
db, err = txn.OpenDBI("testingdup", Create|DupSort, nil, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
put := func(k, v string) {
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = txn.Put(db, []byte(k), []byte(v), 0)
|
|
}
|
|
put("k", "v0")
|
|
put("k", "v1")
|
|
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
|
|
_, _, err = cur.Get(nil, nil, First)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
numdup, err := cur.Count()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if numdup != 2 {
|
|
t.Errorf("unexpected count: %d != %d", numdup, 2)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
// This test verifies the behavior of Cursor.Count when DupSort is not enabled
|
|
// on the database.
|
|
func TestCursor_Count_noDupSort(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var db DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
db, err = txn.OpenDBISimple("testingnodup", Create)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return txn.Put(db, []byte("k"), []byte("v1"), 0)
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// it is an error to call Count if the underlying database does not allow
|
|
// duplicate keys.
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cur.Close()
|
|
|
|
_, _, err = cur.Get(nil, nil, First)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = cur.Count()
|
|
if err != nil {
|
|
t.Errorf("expected no error: %v", err)
|
|
return nil
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestCursor_Renew(t *testing.T) {
|
|
env := setup(t)
|
|
|
|
var db DBI
|
|
err := env.Update(func(txn *Txn) (err error) {
|
|
db, err = txn.OpenRoot(0)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
err = env.Update(func(txn *Txn) (err error) {
|
|
put := func(k, v string) {
|
|
if err == nil {
|
|
err = txn.Put(db, []byte(k), []byte(v), 0)
|
|
}
|
|
}
|
|
put("k1", "v1")
|
|
put("k2", "v2")
|
|
put("k3", "v3")
|
|
return err
|
|
})
|
|
if err != nil {
|
|
t.Error("err")
|
|
}
|
|
|
|
var cur *Cursor
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
cur, err = txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
k, v, err := cur.Get(nil, nil, Next)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(k) != "k1" {
|
|
return fmt.Errorf("key: %q", k)
|
|
}
|
|
if string(v) != "v1" {
|
|
return fmt.Errorf("val: %q", v)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
err = cur.Renew(txn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
k, v, err := cur.Get(nil, nil, Next)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(k) != "k1" {
|
|
return fmt.Errorf("key: %q", k)
|
|
}
|
|
if string(v) != "v1" {
|
|
return fmt.Errorf("val: %q", v)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func BenchmarkCursor(b *testing.B) {
|
|
env := setup(b)
|
|
|
|
var db DBI
|
|
err := env.View(func(txn *Txn) (err error) {
|
|
db, err = txn.OpenRoot(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
b.Error(err)
|
|
return
|
|
}
|
|
|
|
err = env.View(func(txn *Txn) (err error) {
|
|
b.ResetTimer()
|
|
defer b.StopTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
cur, err := txn.OpenCursor(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cur.Close()
|
|
}
|
|
return
|
|
})
|
|
if err != nil {
|
|
b.Error(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func BenchmarkCursor_Renew(b *testing.B) {
|
|
env := setup(b)
|
|
|
|
var cur *Cursor
|
|
err := env.View(func(txn *Txn) (err error) {
|
|
db, err := txn.OpenRoot(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cur, err = txn.OpenCursor(db)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
b.Error(err)
|
|
return
|
|
}
|
|
|
|
_ = env.View(func(txn *Txn) (err error) {
|
|
b.ResetTimer()
|
|
defer b.StopTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err = cur.Renew(txn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|