implements query-iterator object

This commit is contained in:
Ben 2024-05-14 09:23:08 +02:00
parent afab7b9fa7
commit 760acd2767
No known key found for this signature in database
GPG Key ID: 541B9D8C9F1426A1
3 changed files with 136 additions and 0 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ leveldbtool
*.css
build
*.exe

View File

@ -76,6 +76,11 @@ type
LevelDbException* = object of CatchableError
IterNext* = proc(): (string, string) {.gcsafe, closure.}
LevelDbQueryIter* = ref object
finished*: bool
next*: IterNext
const
version* = block:
const configFile = "leveldbstatic.nimble"
@ -406,6 +411,65 @@ iterator iterRange*(self: LevelDb, start, limit: string): (string, string) =
break
yield (key, value)
proc getIterKey(iterPtr: ptr leveldb_iterator_t): string =
var len: csize_t
var str: cstring
str = leveldb_iter_key(iterPtr, addr len)
return newString(str, len)
proc getIterValue(iterPtr: ptr leveldb_iterator_t): string =
var len: csize_t
var str: cstring
str = leveldb_iter_value(iterPtr, addr len)
return newString(str, len)
proc queryIter*(self: LevelDb, prefix: string = "", keysOnly: bool = false): LevelDbQueryIter =
var iterPtr = leveldb_create_iterator(self.db, self.readOptions)
if prefix.len > 0:
leveldb_iter_seek(iterPtr, prefix, prefix.len.csize_t)
else:
leveldb_iter_seek_to_first(iterPtr)
var iter = LevelDbQueryIter()
let emptyResponse = ("", "")
proc getNext(): (string, string) {.gcsafe, closure.} =
if iter.finished:
return emptyResponse
if leveldb_iter_valid(iterPtr) == levelDbFalse:
iter.finished = true
leveldb_iter_destroy(iterPtr)
return emptyResponse
let
keyStr = getIterKey(iterPtr)
valueStr = if keysOnly: "" else: getIterValue(iterPtr)
var err: cstring = nil
leveldb_iter_get_error(iterPtr, addr err)
checkError(err)
leveldb_iter_next(iterPtr)
if prefix.len > 0:
if keyStr.startsWith(prefix):
return (keyStr, valueStr)
else:
iter.finished = true
leveldb_iter_destroy(iterPtr)
return emptyResponse
else:
return (keyStr, valueStr)
iter.finished = false
iter.next = getNext
return iter
proc removeDb*(name: string) =
## Remove the database `name`.
var err: cstring = nil

View File

@ -205,6 +205,77 @@ suite "leveldb":
nc.put("a", "1")
check(toSeq(nc.iter()) == @[("a", "1")])
suite "leveldb queryIter":
setup:
let env = leveldb_create_default_env()
let dbName = $(leveldb_env_get_test_directory(env))
let db = leveldb.open(dbName)
let
k1 = "k1"
k2 = "k2"
k3 = "l3"
v1 = "v1"
v2 = "v2"
v3 = "v3"
empty = ("", "")
db.put(k1, v1)
db.put(k2, v2)
db.put(k3, v3)
teardown:
db.close()
removeDb(dbName)
test "iterates all keys and values":
let iter = db.queryIter()
check:
not iter.finished
iter.next() == (k1, v1)
not iter.finished
iter.next() == (k2, v2)
not iter.finished
iter.next() == (k3, v3)
not iter.finished
iter.next() == empty
iter.finished
test "iterates only keys":
let iter = db.queryIter(keysOnly = true)
check:
not iter.finished
iter.next() == (k1, "")
not iter.finished
iter.next() == (k2, "")
not iter.finished
iter.next() == (k3, "")
not iter.finished
iter.next() == empty
iter.finished
test "iterates only 'k', both keys and values":
let iter = db.queryIter(prefix = "k")
check:
not iter.finished
iter.next() == (k1, v1)
not iter.finished
iter.next() == (k2, v2)
not iter.finished
iter.next() == empty
iter.finished
test "iterates only 'k', only keys":
let iter = db.queryIter(prefix = "k", keysOnly = true)
check:
not iter.finished
iter.next() == (k1, "")
not iter.finished
iter.next() == (k2, "")
not iter.finished
iter.next() == empty
iter.finished
suite "package":
setup: