Skip to content

Commit 8ff6942

Browse files
authored
feat!: Iterator Key() and Value() no longer return a copy (#168)
* updated the Iterator interface docs for methods Key() and Value() * changes in goLevelDBIterator Key() impl: - returns the key itself, not a copy. - updated docs to reflect the change * changes in badgerDBIterator Key() implementation - returns the key itself, not a copy. - updated docs to reflect the change * changes in pebbleDBIterator Key() implementation - returns the key itself, not a copy. - updated docs to reflect the change. * updated backend tests: To reflect the changes, the tests now copy the iterator's key for further use * changes in goLevelDBIterator Value() impl: - returns the value itself, not a copy - updated tests to reflect the change - updated docs to reflect the change * updated docs of badgerdb Key() and Value() APIs implementations. * changes in pebbleDBIterator Value() impl: - returns the value itself, not a copy - updated docs to reflect the change * updated the Iterator interface docs for methods Key() and Value() * updated badgerDBIterator docs for the Value() method * updated badgerdb Value(), and rocksdb Key() and Value() API docs * added changelog entry
1 parent 20b4a09 commit 8ff6942

9 files changed

+47
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Iterator Key and Value APIs now return an object that must be copied before
2+
use ([\#168](https://github.com/cometbft/cometbft-db/pull/168))

.changelog/v0.13.0/summary.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<!--
2+
Add a summary for the release here.
3+
4+
If you don't change this message, or if this file is empty, the release
5+
will not be created. -->
6+
This release changes the contract of the Iterator Key() and Value() APIs.
7+
Namely, the caller is now responsible for creating a copy of their returned value if they want to modify it.

backend_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ func verifyIterator(t *testing.T, itr Iterator, expected []int64, msg string) {
348348

349349
var list []int64
350350
for itr.Valid() {
351-
key := itr.Key()
351+
key := make([]byte, len(itr.Key()))
352+
copy(key, itr.Key())
352353
list = append(list, bytes2Int64(key))
353354
itr.Next()
354355
}
@@ -450,7 +451,10 @@ func assertKeyValues(t *testing.T, db DB, expect map[string][]byte) {
450451
actual := make(map[string][]byte)
451452
for ; iter.Valid(); iter.Next() {
452453
require.NoError(t, iter.Error())
453-
actual[string(iter.Key())] = iter.Value()
454+
455+
value := make([]byte, len(iter.Value()))
456+
copy(value, iter.Value())
457+
actual[string(iter.Key())] = value
454458
}
455459

456460
assert.Equal(t, expect, actual)

badger_db.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -279,19 +279,23 @@ func (i *badgerDBIterator) Valid() bool {
279279
return true
280280
}
281281

282+
// Key implements Iterator.
283+
// The caller should not modify the contents of the returned slice.
284+
// Instead, the caller should make a copy and work on the copy.
282285
func (i *badgerDBIterator) Key() []byte {
283286
if !i.Valid() {
284287
panic("iterator is invalid")
285288
}
286-
// Note that we don't use KeyCopy, so this is only valid until the next
287-
// call to Next.
288-
return i.iter.Item().KeyCopy(nil)
289+
return i.iter.Item().Key()
289290
}
290291

292+
// Value implements Iterator.
293+
// The returned slice is a copy of the original data, therefore it is safe to modify.
291294
func (i *badgerDBIterator) Value() []byte {
292295
if !i.Valid() {
293296
panic("iterator is invalid")
294297
}
298+
295299
val, err := i.iter.Item().ValueCopy(nil)
296300
if err != nil {
297301
i.lastErr = err

common_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ func checkKeyPanics(t *testing.T, itr Iterator) {
7272

7373
func checkValuePanics(t *testing.T, itr Iterator) {
7474
t.Helper()
75-
assert.Panics(t, func() { itr.Value() })
75+
76+
msg := "checkValuePanics expected panic but didn't"
77+
assert.Panics(t, func() { itr.Value() }, msg)
7678
}
7779

7880
func newTempDB(t *testing.T, backend BackendType) (db DB, dbDir string) {

goleveldb_iterator.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,19 @@ func (itr *goLevelDBIterator) Valid() bool {
9393
}
9494

9595
// Key implements Iterator.
96+
// The caller should not modify the contents of the returned slice.
97+
// Instead, the caller should make a copy and work on the copy.
9698
func (itr *goLevelDBIterator) Key() []byte {
97-
// Key returns a copy of the current key.
98-
// See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88
9999
itr.assertIsValid()
100-
return cp(itr.source.Key())
100+
return itr.source.Key()
101101
}
102102

103103
// Value implements Iterator.
104+
// The caller should not modify the contents of the returned slice.
105+
// Instead, the caller should make a copy and work on the copy.
104106
func (itr *goLevelDBIterator) Value() []byte {
105-
// Value returns a copy of the current value.
106-
// See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88
107107
itr.assertIsValid()
108-
return cp(itr.source.Value())
108+
return itr.source.Value()
109109
}
110110

111111
// Next implements Iterator.

pebble.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -396,19 +396,19 @@ func (itr *pebbleDBIterator) Valid() bool {
396396
}
397397

398398
// Key implements Iterator.
399+
// The caller should not modify the contents of the returned slice.
400+
// Instead, the caller should make a copy and work on the copy.
399401
func (itr *pebbleDBIterator) Key() []byte {
400-
// Key returns a copy of the current key.
401-
// See https://github.com/cockroachdb/pebble/blob/v1.0.0/iterator.go#L2106
402402
itr.assertIsValid()
403-
return cp(itr.source.Key())
403+
return itr.source.Key()
404404
}
405405

406406
// Value implements Iterator.
407+
// The caller should not modify the contents of the returned slice.
408+
// Instead, the caller should make a copy and work on the copy.
407409
func (itr *pebbleDBIterator) Value() []byte {
408-
// Value returns a copy of the current value.
409-
// See https://github.com/cockroachdb/pebble/blob/v1.0.0/iterator.go#L2116
410410
itr.assertIsValid()
411-
return cp(itr.source.Value())
411+
return itr.source.Value()
412412
}
413413

414414
// Next implements Iterator.

rocksdb_iterator.go

+2
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@ func (itr *rocksDBIterator) Valid() bool {
9494
}
9595

9696
// Key implements Iterator.
97+
// The returned slice is a copy of the original data, therefore it is safe to modify.
9798
func (itr *rocksDBIterator) Key() []byte {
9899
itr.assertIsValid()
99100
return moveSliceToBytes(itr.source.Key())
100101
}
101102

102103
// Value implements Iterator.
104+
// The returned slice is a copy of the original data, therefore it is safe to modify.
103105
func (itr *rocksDBIterator) Value() []byte {
104106
itr.assertIsValid()
105107
return moveSliceToBytes(itr.source.Value())

types.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,17 @@ type Iterator interface {
136136
Next()
137137

138138
// Key returns the key at the current position. Panics if the iterator is invalid.
139-
// CONTRACT: key readonly []byte
139+
// Key returns the key of the current key/value pair, or nil if done.
140+
// The caller should not modify the contents of the returned slice, and
141+
// its contents may change on the next call to any 'seeks method'.
142+
// Instead, the caller should make a copy and work on the copy.
140143
Key() (key []byte)
141144

142145
// Value returns the value at the current position. Panics if the iterator is invalid.
143-
// CONTRACT: value readonly []byte
146+
// Value returns the value of the current key/value pair, or nil if done.
147+
// The caller should not modify the contents of the returned slice, and
148+
// its contents may change on the next call to any 'seeks method'.
149+
// Instead, the caller should make a copy and work on the copy.
144150
Value() (value []byte)
145151

146152
// Error returns the last error encountered by the iterator, if any.

0 commit comments

Comments
 (0)