From 6358511b19db26c317f10a8b187ccabcd67db693 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Fri, 11 Sep 2020 08:33:37 -0500 Subject: [PATCH] finish iterator; unit tests for iterator --- postgres/database.go | 6 +- postgres/iterator.go | 39 +-- postgres/iterator_test.go | 513 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 539 insertions(+), 19 deletions(-) create mode 100644 postgres/iterator_test.go diff --git a/postgres/database.go b/postgres/database.go index 6e661db..1e5a958 100644 --- a/postgres/database.go +++ b/postgres/database.go @@ -200,20 +200,20 @@ func (d *Database) NewBatch() ethdb.Batch { // NewIterator creates a binary-alphabetical iterator over the entire keyspace // contained within the key-value database. func (d *Database) NewIterator() ethdb.Iterator { - return NewIterator([]byte{}, []byte{}, d.db) + return NewIterator(nil, nil, d.db) } // NewIteratorWithStart creates a binary-alphabetical iterator over a subset of // database content starting at a particular initial key (or after, if it does // not exist). func (d *Database) NewIteratorWithStart(start []byte) ethdb.Iterator { - return NewIterator(start, []byte{}, d.db) + return NewIterator(start, nil, d.db) } // NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset // of database content with a particular key prefix. func (d *Database) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator { - return NewIterator([]byte{}, prefix, d.db) + return NewIterator(nil, prefix, d.db) } // Close satisfies the io.Closer interface diff --git a/postgres/iterator.go b/postgres/iterator.go index a5cafb1..120e8fa 100644 --- a/postgres/iterator.go +++ b/postgres/iterator.go @@ -19,15 +19,17 @@ package pgipfsethdb import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/jmoiron/sqlx" - "github.com/sirupsen/logrus" ) const ( - nextPgStr = `SELECT key, data FROM public.blocks + initPgStr = `SELECT eth_key, data FROM public.blocks + INNER JOIN eth.key_preimages ON (ipfs_key = key) + WHERE eth_key = $1` + nextPgStr = `SELECT eth_key, data FROM public.blocks INNER JOIN eth.key_preimages ON (ipfs_key = key) WHERE eth_key > $1 ORDER BY eth_key LIMIT 1` - nextPgStrWithPrefix = `SELECT key, data FROM public.blocks + nextPgStrWithPrefix = `SELECT eth_key, data FROM public.blocks INNER JOIN eth.key_preimages ON (ipfs_key = key) WHERE eth_key > $1 AND prefix = $2 ORDER BY eth_key LIMIT 1` @@ -43,6 +45,7 @@ type Iterator struct { db *sqlx.DB currentKey, prefix, currentValue []byte err error + init bool } // NewIterator returns an ethdb.Iterator interface for PG-IPFS @@ -51,6 +54,7 @@ func NewIterator(start, prefix []byte, db *sqlx.DB) ethdb.Iterator { db: db, prefix: prefix, currentKey: start, + init: start != nil, } } @@ -59,21 +63,24 @@ func NewIterator(start, prefix []byte, db *sqlx.DB) ethdb.Iterator { // It returns whether the iterator is exhausted func (i *Iterator) Next() bool { next := new(nextModel) - if i.prefix != nil { - if err := i.db.Get(next, nextPgStrWithPrefix, i.currentKey, i.prefix); err != nil { - logrus.Errorf("iterator.Next() error: %v", err) - i.currentKey, i.currentValue = nil, nil + if i.init { + i.init = false + if err := i.db.Get(next, initPgStr, i.currentKey); err != nil { + i.currentKey, i.currentValue, i.err = nil, nil, err + return false + } + } else if i.prefix != nil { + if err := i.db.Get(next, nextPgStrWithPrefix, i.currentKey, i.prefix); err != nil { + i.currentKey, i.currentValue, i.err = nil, nil, err + return false + } + } else { + if err := i.db.Get(next, nextPgStr, i.currentKey); err != nil { + i.currentKey, i.currentValue, i.err = nil, nil, err return false } - i.currentKey, i.currentValue = next.Key, next.Value - return true } - if err := i.db.Get(next, nextPgStr, i.currentKey); err != nil { - logrus.Errorf("iterator.Next() error: %v", err) - i.currentKey, i.currentValue = nil, nil - return false - } - i.currentKey, i.currentValue = next.Key, next.Value + i.currentKey, i.currentValue, i.err = next.Key, next.Value, nil return true } @@ -104,5 +111,5 @@ func (i *Iterator) Value() []byte { // Release releases associated resources // Release should always succeed and can be called multiple times without causing error func (i *Iterator) Release() { - i.db.Close() + i.db, i.currentKey, i.currentValue, i.err, i.prefix = nil, nil, nil, nil, nil } diff --git a/postgres/iterator_test.go b/postgres/iterator_test.go new file mode 100644 index 0000000..7952e74 --- /dev/null +++ b/postgres/iterator_test.go @@ -0,0 +1,513 @@ +// VulcanizeDB +// Copyright © 2020 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package pgipfsethdb_test + +import ( + "database/sql" + + "github.com/ethereum/go-ethereum/ethdb" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/vulcanize/ipfs-ethdb/postgres" +) + +var ( + iterator ethdb.Iterator + testPrefix = []byte("testPrefix") + testEthKey1 = []byte{'\x01'} + testEthKey2 = []byte{'\x01', '\x01'} + testEthKey3 = []byte{'\x01', '\x02'} + testEthKey4 = []byte{'\x01', '\x0e'} + testEthKey5 = []byte{'\x01', '\x02', '\x01'} + testEthKey6 = []byte{'\x01', '\x0e', '\x01'} + prefixedTestEthKey1 = append(append(testPrefix, pgipfsethdb.KeyDelineation...), testEthKey1...) + prefixedTestEthKey2 = append(append(testPrefix, pgipfsethdb.KeyDelineation...), testEthKey2...) + prefixedTestEthKey3 = append(append(testPrefix, pgipfsethdb.KeyDelineation...), testEthKey3...) + prefixedTestEthKey4 = append(append(testPrefix, pgipfsethdb.KeyDelineation...), testEthKey4...) + prefixedTestEthKey5 = append(append(testPrefix, pgipfsethdb.KeyDelineation...), testEthKey5...) + prefixedTestEthKey6 = append(append(testPrefix, pgipfsethdb.KeyDelineation...), testEthKey6...) + mockValue1 = []byte{1} + mockValue2 = []byte{2} + mockValue3 = []byte{3} + mockValue4 = []byte{4} + mockValue5 = []byte{5} + mockValue6 = []byte{6} +) + +var _ = Describe("Iterator", func() { + BeforeEach(func() { + db, err = pgipfsethdb.TestDB() + Expect(err).ToNot(HaveOccurred()) + database = pgipfsethdb.NewDatabase(db) + // non-prefixed entries + err = database.Put(testEthKey1, mockValue1) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(testEthKey2, mockValue2) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(testEthKey3, mockValue3) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(testEthKey4, mockValue4) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(testEthKey5, mockValue5) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(testEthKey6, mockValue6) + Expect(err).ToNot(HaveOccurred()) + // prefixed entries + err = database.Put(prefixedTestEthKey1, mockValue1) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(prefixedTestEthKey2, mockValue2) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(prefixedTestEthKey3, mockValue3) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(prefixedTestEthKey4, mockValue4) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(prefixedTestEthKey5, mockValue5) + Expect(err).ToNot(HaveOccurred()) + err = database.Put(prefixedTestEthKey6, mockValue6) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + err = pgipfsethdb.ResetTestDB(db) + Expect(err).ToNot(HaveOccurred()) + }) + + Describe("NewIterator", func() { + It("iterates over the entire key-set (prefixed or not)", func() { + iterator = database.NewIterator() + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(BeNil()) + + more := iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey1)) + Expect(iterator.Value()).To(Equal(mockValue1)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey1)) + Expect(iterator.Value()).To(Equal(mockValue1)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).ToNot(BeTrue()) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(Equal(sql.ErrNoRows)) + }) + }) + + Describe("NewIteratorWithPrefix", func() { + It("iterates over all db entries that have the provided prefix", func() { + iterator = database.NewIteratorWithPrefix(testPrefix) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(BeNil()) + + more := iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey1)) + Expect(iterator.Value()).To(Equal(mockValue1)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).ToNot(BeTrue()) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(Equal(sql.ErrNoRows)) + }) + + It("behaves as no prefix is provided if prefix is nil", func() { + iterator = database.NewIteratorWithPrefix(nil) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(BeNil()) + + more := iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey1)) + Expect(iterator.Value()).To(Equal(mockValue1)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey1)) + Expect(iterator.Value()).To(Equal(mockValue1)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).ToNot(BeTrue()) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(Equal(sql.ErrNoRows)) + }) + + It("considers empty but non-nil []byte a valid prefix, which precludes iteration over any other prefixed keys", func() { + iterator = database.NewIteratorWithPrefix([]byte{}) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(BeNil()) + + more := iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey1)) + Expect(iterator.Value()).To(Equal(mockValue1)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).ToNot(BeTrue()) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(Equal(sql.ErrNoRows)) + }) + }) + + Describe("NewIteratorWithStart", func() { + It("iterates over the entire key-set (prefixed or not) starting with at the provided path", func() { + iterator = database.NewIteratorWithStart(testEthKey2) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Error()).To(BeNil()) + + more := iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey1)) + Expect(iterator.Value()).To(Equal(mockValue1)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).ToNot(BeTrue()) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(Equal(sql.ErrNoRows)) + }) + + It("iterates over the entire key-set (prefixed or not) starting with at the provided path", func() { + iterator = database.NewIteratorWithStart(prefixedTestEthKey3) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey3)) + Expect(iterator.Error()).To(BeNil()) + + more := iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey3)) + Expect(iterator.Value()).To(Equal(mockValue3)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey5)) + Expect(iterator.Value()).To(Equal(mockValue5)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey4)) + Expect(iterator.Value()).To(Equal(mockValue4)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(prefixedTestEthKey6)) + Expect(iterator.Value()).To(Equal(mockValue6)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).ToNot(BeTrue()) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(Equal(sql.ErrNoRows)) + }) + }) + + Describe("Release", func() { + It("releases resources associated with the Iterator", func() { + iterator = database.NewIteratorWithStart(testEthKey2) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Error()).To(BeNil()) + + more := iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + + iterator.Release() + iterator.Release() // check that we don't panic if called multiple times + + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(BeNil()) + Expect(iterator.Error()).To(BeNil()) + Expect(func() { iterator.Next() }).To(Panic()) // check that we panic if we try to use released iterator + + // We can still create a new iterator from the same backing db + iterator = database.NewIteratorWithStart(testEthKey2) + Expect(iterator.Value()).To(BeNil()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Error()).To(BeNil()) + + more = iterator.Next() + Expect(more).To(BeTrue()) + Expect(iterator.Key()).To(Equal(testEthKey2)) + Expect(iterator.Value()).To(Equal(mockValue2)) + Expect(iterator.Error()).To(BeNil()) + }) + }) +})