lnd.xprv/kvdb/etcd/readwrite_cursor.go
Joost Jager f592375d1b
kvdb/test: fix cursor tests to match bbolt semantics
From bbolt docs:
// Seek positions the cursor at the passed seek key. If the key does not exist,
// the cursor is moved to the next key after seek. Returns the new pair.
2021-07-12 11:31:09 +02:00

139 lines
3.3 KiB
Go

// +build kvdb_etcd
package etcd
// readWriteCursor holds a reference to the cursors bucket, the value
// prefix and the current key used while iterating.
type readWriteCursor struct {
// bucket holds the reference to the parent bucket.
bucket *readWriteBucket
// prefix holds the value prefix which is in front of each
// value key in the bucket.
prefix string
// currKey holds the current key of the cursor.
currKey string
}
func newReadWriteCursor(bucket *readWriteBucket) *readWriteCursor {
return &readWriteCursor{
bucket: bucket,
prefix: string(bucket.id),
}
}
// First positions the cursor at the first key/value pair and returns
// the pair.
func (c *readWriteCursor) First() (key, value []byte) {
// Get the first key with the value prefix.
kv, err := c.bucket.tx.stm.First(c.prefix)
if err != nil {
// TODO: revise this once kvdb interface supports errors
return nil, nil
}
if kv != nil {
c.currKey = kv.key
return getKeyVal(kv)
}
return nil, nil
}
// Last positions the cursor at the last key/value pair and returns the
// pair.
func (c *readWriteCursor) Last() (key, value []byte) {
kv, err := c.bucket.tx.stm.Last(c.prefix)
if err != nil {
// TODO: revise this once kvdb interface supports errors
return nil, nil
}
if kv != nil {
c.currKey = kv.key
return getKeyVal(kv)
}
return nil, nil
}
// Next moves the cursor one key/value pair forward and returns the new
// pair.
func (c *readWriteCursor) Next() (key, value []byte) {
kv, err := c.bucket.tx.stm.Next(c.prefix, c.currKey)
if err != nil {
// TODO: revise this once kvdb interface supports errors
return nil, nil
}
if kv != nil {
c.currKey = kv.key
return getKeyVal(kv)
}
return nil, nil
}
// Prev moves the cursor one key/value pair backward and returns the new
// pair.
func (c *readWriteCursor) Prev() (key, value []byte) {
kv, err := c.bucket.tx.stm.Prev(c.prefix, c.currKey)
if err != nil {
// TODO: revise this once kvdb interface supports errors
return nil, nil
}
if kv != nil {
c.currKey = kv.key
return getKeyVal(kv)
}
return nil, nil
}
// Seek positions the cursor at the passed seek key. If the key does
// not exist, the cursor is moved to the next key after seek. Returns
// the new pair.
func (c *readWriteCursor) Seek(seek []byte) (key, value []byte) {
// Seek to the first key with prefix + seek. If that key is not present
// STM will seek to the next matching key with prefix.
kv, err := c.bucket.tx.stm.Seek(c.prefix, c.prefix+string(seek))
if err != nil {
// TODO: revise this once kvdb interface supports errors
return nil, nil
}
if kv != nil {
c.currKey = kv.key
return getKeyVal(kv)
}
return nil, nil
}
// Delete removes the current key/value pair the cursor is at without
// invalidating the cursor. Returns ErrIncompatibleValue if attempted
// when the cursor points to a nested bucket.
func (c *readWriteCursor) Delete() error {
// Get the next key after the current one. We could do this
// after deletion too but it's one step more efficient here.
nextKey, err := c.bucket.tx.stm.Next(c.prefix, c.currKey)
if err != nil {
return err
}
if isBucketKey(c.currKey) {
c.bucket.DeleteNestedBucket(getKey(c.currKey))
} else {
c.bucket.Delete(getKey(c.currKey))
}
if nextKey != nil {
// Set current key to the next one.
c.currKey = nextKey.key
}
return nil
}