Skip to content

Commit

Permalink
Make .seek() work correctly for PrefixedDB iterators
Browse files Browse the repository at this point in the history
This fixes issue #15.
  • Loading branch information
wbolster committed Sep 17, 2013
1 parent 806cb8d commit c89a49e
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 21 deletions.
3 changes: 3 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Version history
Plyvel 0.5 (not yet released)
=============================

* Fix :py:meth:`Iterator.seek()` for :py:class:`PrefixedDB` iterators
(`issue #15 <https://github.com/wbolster/plyvel/issues/15>`_)

* Made some argument type checking a bit stricter (mostly ``None`` checks)

* Support LRU caches larger than 2GB by using the right integer type for the
Expand Down
46 changes: 25 additions & 21 deletions plyvel/_plyvel.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ cdef class DB:
include_key=True, include_value=True, verify_checksums=None,
fill_cache=None):
return Iterator(
db=self, key_prefix=None, reverse=reverse, start=start, stop=stop,
db=self, db_prefix=None, reverse=reverse, start=start, stop=stop,
include_start=include_start, include_stop=include_stop,
prefix=prefix, include_key=include_key,
include_value=include_value, verify_checksums=verify_checksums,
Expand Down Expand Up @@ -448,7 +448,7 @@ cdef class PrefixedDB:
include_key=True, include_value=True, verify_checksums=None,
fill_cache=None):
return Iterator(
db=self.db, key_prefix=self.prefix, reverse=reverse, start=start,
db=self.db, db_prefix=self.prefix, reverse=reverse, start=start,
stop=stop, include_start=include_start, include_stop=include_stop,
prefix=prefix, include_key=include_key,
include_value=include_value, verify_checksums=verify_checksums,
Expand Down Expand Up @@ -610,17 +610,14 @@ cdef class Iterator:
cdef bool include_stop
cdef bool include_key
cdef bool include_value

# This is the number of bytes that should be skipped over when
# returning keys from .current(). This avoids Python byte string
# copying/slicing overhead for PrefixedDB iterators.
cdef int skip_key_bytes
cdef bytes db_prefix
cdef size_t db_prefix_len

# Iterators need to be weak referencable to ensure a proper cleanup
# from DB.close()
cdef object __weakref__

def __init__(self, *, DB db not None, bytes key_prefix, bool reverse,
def __init__(self, *, DB db not None, bytes db_prefix, bool reverse,
bytes start, bytes stop, bool include_start,
bool include_stop, bytes prefix, bool include_key,
bool include_value, bool verify_checksums, bool fill_cache,
Expand All @@ -632,29 +629,32 @@ cdef class Iterator:
self.comparator = <leveldb.Comparator*>db.options.comparator
self.direction = FORWARD if not reverse else REVERSE

if key_prefix is None:
self.skip_key_bytes = 0
if db_prefix is None:
self.db_prefix_len = 0
else:
# This is an iterator on a PrefixedDB. Transform args so
# that the database key prefix is taken into account.
self.skip_key_bytes = len(key_prefix)
# This is an iterator on a PrefixedDB.
self.db_prefix = db_prefix
self.db_prefix_len = len(db_prefix)

# Transform args so that the database key prefix is taken
# into account.
if prefix is not None:
# Both database key prefix and prefix on the iterator
prefix = key_prefix + prefix
prefix = db_prefix + prefix
else:
# Adapt start and stop keys to use the database key
# prefix.
if start is None:
start = key_prefix
start = db_prefix
include_start = True
else:
start = key_prefix + start
start = db_prefix + start

if stop is None:
stop = bytes_increment(key_prefix)
stop = bytes_increment(db_prefix)
include_stop = False
else:
stop = key_prefix + stop
stop = db_prefix + stop

if prefix is not None:
if start is not None or stop is not None:
Expand Down Expand Up @@ -728,10 +728,11 @@ cdef class Iterator:
cdef Slice value_slice
cdef bytes value

# Only build Python strings that will be returned
# Only build Python strings that will be returned. Also chop off
# the db prefix (for PrefixedDB iterators).
if self.include_key:
key_slice = self._iter.key()
key = key_slice.data()[self.skip_key_bytes:key_slice.size()]
key = key_slice.data()[self.db_prefix_len:key_slice.size()]

if self.include_value:
value_slice = self._iter.value()
Expand Down Expand Up @@ -908,6 +909,9 @@ cdef class Iterator:
if self._iter is NULL:
raise RuntimeError("Cannot operate on closed LevelDB database")

if self.db_prefix is not None:
target = self.db_prefix + target

cdef Slice target_slice = Slice(target, len(target))

# Seek only within the start/stop boundaries
Expand Down Expand Up @@ -981,7 +985,7 @@ cdef class Snapshot:
include_key=True, include_value=True, verify_checksums=None,
fill_cache=None):
return Iterator(
db=self.db, key_prefix=self.prefix, reverse=reverse, start=start,
db=self.db, db_prefix=self.prefix, reverse=reverse, start=start,
stop=stop, include_start=include_start, include_stop=include_stop,
prefix=prefix, include_key=include_key,
include_value=include_value, verify_checksums=verify_checksums,
Expand Down
3 changes: 3 additions & 0 deletions test/test_plyvel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,9 @@ def test_prefixed_db():
assert_equal(1000, len(list(it)))
it = db_a.iterator(prefix=b'10')
assert_equal(10, len(list(it)))
it = db_a.iterator()
it.seek(b'500')
assert_equal(500, len(list(it)))

# Snapshots
sn_a = db_a.snapshot()
Expand Down

0 comments on commit c89a49e

Please sign in to comment.