diff --git a/engines/leveldb/iter.go b/engines/leveldb/iter.go index 66a0790..bb0ed7c 100644 --- a/engines/leveldb/iter.go +++ b/engines/leveldb/iter.go @@ -23,10 +23,11 @@ type ldbIterator struct { // Type check for the ldbIterator var _ honuiter.Iterator = &ldbIterator{} -func (i *ldbIterator) Next() bool { return i.ldb.Next() } -func (i *ldbIterator) Prev() bool { return i.ldb.Prev() } -func (i *ldbIterator) Error() error { return i.ldb.Error() } -func (i *ldbIterator) Release() { i.ldb.Release() } +func (i *ldbIterator) Next() bool { return i.ldb.Next() } +func (i *ldbIterator) Prev() bool { return i.ldb.Prev() } +func (i *ldbIterator) Seek(key []byte) bool { return i.ldb.Seek(key) } +func (i *ldbIterator) Error() error { return i.ldb.Error() } +func (i *ldbIterator) Release() { i.ldb.Release() } func (i *ldbIterator) Key() []byte { // Fetch the key then split the namespace from the key diff --git a/iterator/empty.go b/iterator/empty.go index a77a9c6..70025e3 100644 --- a/iterator/empty.go +++ b/iterator/empty.go @@ -23,6 +23,7 @@ func (i *emptyIterator) rErr() { func (i *emptyIterator) Next() bool { i.rErr(); return false } func (i *emptyIterator) Prev() bool { i.rErr(); return false } +func (i *emptyIterator) Seek(key []byte) bool { i.rErr(); return false } func (*emptyIterator) Key() []byte { return nil } func (*emptyIterator) Value() []byte { return nil } func (*emptyIterator) Object() (*pb.Object, error) { return nil, nil } diff --git a/iterator/empty_test.go b/iterator/empty_test.go new file mode 100644 index 0000000..82b8e1d --- /dev/null +++ b/iterator/empty_test.go @@ -0,0 +1,57 @@ +package iterator_test + +import ( + "errors" + "testing" + + . "github.com/rotationalio/honu/iterator" + "github.com/stretchr/testify/require" +) + +func TestEmptyIterator(t *testing.T) { + // Check that the empty iterator returns expected values + iter := NewEmptyIterator(nil) + require.False(t, iter.Next()) + require.False(t, iter.Prev()) + require.False(t, iter.Seek([]byte("foo"))) + require.Nil(t, iter.Key()) + require.Nil(t, iter.Value()) + require.NoError(t, iter.Error()) + + obj, err := iter.Object() + require.NoError(t, err) + require.Nil(t, obj) + + // After calling release the empty iterator should still have no error + iter.Release() + require.NoError(t, iter.Error()) + + // However if next is called after release, then the iterator should error + require.False(t, iter.Next()) + require.EqualError(t, iter.Error(), ErrIterReleased.Error()) + + // Check that the empty iterator can be initialized with an error + iter = NewEmptyIterator(errors.New("something bad happened")) + require.EqualError(t, iter.Error(), "something bad happened") + + // Ensure that calling any of the iterator methods do not change the error + require.False(t, iter.Next()) + require.False(t, iter.Prev()) + require.False(t, iter.Seek([]byte("foo"))) + require.Nil(t, iter.Key()) + require.Nil(t, iter.Value()) + + obj, err = iter.Object() + require.NoError(t, err) + require.Nil(t, obj) + + require.EqualError(t, iter.Error(), "something bad happened") + + // Ensure calling Release doesn't change the error + iter.Release() + require.EqualError(t, iter.Error(), "something bad happened") + + // Ensure calling Next after Release doesn't change the error + iter.Next() + require.EqualError(t, iter.Error(), "something bad happened") +} diff --git a/iterator/iterator.go b/iterator/iterator.go index dbf1efa..0c6e346 100644 --- a/iterator/iterator.go +++ b/iterator/iterator.go @@ -28,6 +28,10 @@ type Iterator interface { // It returns false if the iterator has been exhausted. Next() bool + // Prev moves the iterator to the previous key/value pair or row. + // It returns false if the iterator has been exhausted. + Prev() bool + // Error returns any accumulated error. Exhausting all rows or key/value pairs is // not considered to be an error. Error() error @@ -52,4 +56,8 @@ type Iterator interface { // iterator. Release can be called multiple times without error but after it has // been called, no Iterator methods will return data. Release() + + // Seek moves the iterator to the first key/value pair whose key is greater than or + // equal to the given key. It returns whether such pair exists. + Seek(key []byte) bool }