From 760acd2767998411df09aa0f33076b51112abea6 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 14 May 2024 09:23:08 +0200 Subject: [PATCH] implements query-iterator object --- .gitignore | 1 + leveldbstatic.nim | 64 ++++++++++++++++++++++++++++++++++++++++++ tests/test.nim | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) diff --git a/.gitignore b/.gitignore index 5536487..8acf99f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ leveldbtool *.css build +*.exe diff --git a/leveldbstatic.nim b/leveldbstatic.nim index 7bc69ab..b4732fc 100644 --- a/leveldbstatic.nim +++ b/leveldbstatic.nim @@ -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 diff --git a/tests/test.nim b/tests/test.nim index 1a7d19f..fed6e69 100644 --- a/tests/test.nim +++ b/tests/test.nim @@ -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: