Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kvstore: return bool from del, add clear #574

Merged
merged 2 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions eth/db/kvstore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ type
PutProc = proc (db: RootRef, key, val: openArray[byte]): KvResult[void] {.nimcall, gcsafe, raises: [Defect].}
GetProc = proc (db: RootRef, key: openArray[byte], onData: DataProc): KvResult[bool] {.nimcall, gcsafe, raises: [Defect].}
FindProc = proc (db: RootRef, prefix: openArray[byte], onFind: KeyValueProc): KvResult[int] {.nimcall, gcsafe, raises: [Defect].}
DelProc = proc (db: RootRef, key: openArray[byte]): KvResult[void] {.nimcall, gcsafe, raises: [Defect].}
DelProc = proc (db: RootRef, key: openArray[byte]): KvResult[bool] {.nimcall, gcsafe, raises: [Defect].}
ClearProc = proc (db: RootRef): KvResult[bool] {.nimcall, gcsafe, raises: [Defect].}
ContainsProc = proc (db: RootRef, key: openArray[byte]): KvResult[bool] {.nimcall, gcsafe, raises: [Defect].}
CloseProc = proc (db: RootRef): KvResult[void] {.nimcall, gcsafe, raises: [Defect].}

Expand All @@ -42,6 +43,7 @@ type
getProc: GetProc
findProc: FindProc
delProc: DelProc
clearProc: ClearProc
containsProc: ContainsProc
closeProc: CloseProc

Expand All @@ -68,11 +70,18 @@ template find*(
let db = dbParam
db.findProc(db.obj, prefix, onFind)

template del*(dbParam: KvStoreRef, key: openArray[byte]): KvResult[void] =
template del*(dbParam: KvStoreRef, key: openArray[byte]): KvResult[bool] =
## Remove value at ``key`` from store - do nothing if the value is not present
## Returns true iff value was found and deleted
let db = dbParam
db.delProc(db.obj, key)

template clear*(dbParam: KvStoreRef): KvResult[bool] =
## Remove all values from store
## Returns true iff a value was found and deleted
let db = dbParam
db.clearProc(db.obj)

template contains*(dbParam: KvStoreRef, key: openArray[byte]): KvResult[bool] =
## Return true iff ``key`` has a value in store
let db = dbParam
Expand All @@ -95,10 +104,14 @@ proc findImpl[T](db: RootRef, key: openArray[byte], onFind: KeyValueProc): KvRes
mixin get
find(T(db), key, onFind)

proc delImpl[T](db: RootRef, key: openArray[byte]): KvResult[void] {.gcsafe.} =
proc delImpl[T](db: RootRef, key: openArray[byte]): KvResult[bool] {.gcsafe.} =
mixin del
del(T(db), key)

proc clearImpl[T](db: RootRef): KvResult[bool] {.gcsafe.} =
mixin clear
clear(T(db))

proc containsImpl[T](db: RootRef, key: openArray[byte]): KvResult[bool] {.gcsafe.} =
mixin contains
contains(T(db), key)
Expand All @@ -108,14 +121,15 @@ proc closeImpl[T](db: RootRef): KvResult[void] {.gcsafe.} =
close(T(db))

func kvStore*[T: RootRef](x: T): KvStoreRef =
mixin del, get, put, contains, close
mixin del, clear, get, put, contains, close

KvStoreRef(
obj: x,
putProc: putImpl[T],
getProc: getImpl[T],
findProc: findImpl[T],
delProc: delImpl[T],
clearProc: clearImpl[T],
containsProc: containsImpl[T],
closeProc: closeImpl[T]
)
Expand All @@ -139,9 +153,19 @@ proc find*(

ok(total)

proc del*(db: MemStoreRef, key: openArray[byte]): KvResult[void] =
db.records.del(@key)
ok()
proc del*(db: MemStoreRef, key: openArray[byte]): KvResult[bool] =
if @key in db.records:
db.records.del(@key)
ok(true)
else:
ok(false)

proc clear*(db: MemStoreRef): KvResult[bool] =
if db.records.len > 0:
db.records.clear()
ok(true)
else:
ok(false)

proc contains*(db: MemStoreRef, key: openArray[byte]): KvResult[bool] =
ok(db.records.contains(@key))
Expand Down
29 changes: 24 additions & 5 deletions eth/db/kvstore_sqlite3.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ type
# A Keyspace is a single key-value table - it is generally efficient to
# create separate keyspaces for each type of data stored
open: bool
getStmt, putStmt, delStmt, containsStmt,
env: Sqlite
getStmt, putStmt, delStmt, clearStmt, containsStmt,
findStmt0, findStmt1, findStmt2: RawStmtPtr

SqKeyspaceRef* = ref SqKeyspace
Expand Down Expand Up @@ -433,29 +434,46 @@ proc contains*(db: SqKeyspaceRef, key: openArray[byte]): KvResult[bool] =

res

proc del*(db: SqKeyspaceRef, key: openArray[byte]): KvResult[void] =
proc del*(db: SqKeyspaceRef, key: openArray[byte]): KvResult[bool] =
if not db.open: return err("sqlite: database closed")
let delStmt = db.delStmt
if delStmt == nil: return ok() # no such table
if delStmt == nil: return ok(false) # no such table
checkErr bindParam(delStmt, 1, key)

let res =
if (let v = sqlite3_step(delStmt); v != SQLITE_DONE):
err($sqlite3_errstr(v))
else:
ok()
ok(sqlite3_changes(db.env) > 0)

# release implict transaction
discard sqlite3_reset(delStmt) # same return information as step
discard sqlite3_clear_bindings(delStmt) # no errors possible

res

proc clear*(db: SqKeyspaceRef): KvResult[bool] =
if not db.open: return err("sqlite: database closed")
let clearStmt = db.clearStmt
if clearStmt == nil: return ok(false) # no such table

let res =
if (let v = sqlite3_step(clearStmt); v != SQLITE_DONE):
err($sqlite3_errstr(v))
else:
ok(sqlite3_changes(db.env) > 0)

# release implicit transaction
discard sqlite3_reset(clearStmt) # same return information as step

res

proc close*(db: var SqKeyspace) =
# Calling with null stmt is harmless
discard sqlite3_finalize(db.putStmt)
discard sqlite3_finalize(db.getStmt)
discard sqlite3_finalize(db.delStmt)
discard sqlite3_finalize(db.clearStmt)
discard sqlite3_finalize(db.containsStmt)
discard sqlite3_finalize(db.findStmt0)
discard sqlite3_finalize(db.findStmt1)
Expand Down Expand Up @@ -604,7 +622,7 @@ proc openKvStore*(
if withoutRowid: createSql & " WITHOUT ROWID;" else: createSql & ";"
true
var
tmp: SqKeyspace
tmp = SqKeyspace(env: db.env)
defer:
# We'll "move" ownership to the return value, effectively disabling "close"
close(tmp)
Expand All @@ -615,6 +633,7 @@ proc openKvStore*(
tmp.putStmt =
prepare(db.env, "INSERT OR REPLACE INTO '" & name & "'(key, value) VALUES (?, ?);")
tmp.delStmt = prepare(db.env, "DELETE FROM '" & name & "' WHERE key = ?;")
tmp.clearStmt = prepare(db.env, "DELETE FROM '" & name & "';")
tmp.containsStmt = prepare(db.env, "SELECT 1 FROM '" & name & "' WHERE key = ?;")
tmp.findStmt0 = prepare(db.env, "SELECT key, value FROM '" & name & "';")
tmp.findStmt1 = prepare(db.env, "SELECT key, value FROM '" & name & "' WHERE key >= ?;")
Expand Down
14 changes: 10 additions & 4 deletions tests/db/test_kvstore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ proc testKvStore*(db: KvStoreRef, supportsFind: bool) =

not db.get(key, proc(data: openArray[byte]) = discard)[]
not db.contains(key)[]

db.del(key)[] # does nothing
not db.del(key)[] # does nothing

db.put(key, value)[]

Expand All @@ -39,12 +38,19 @@ proc testKvStore*(db: KvStoreRef, supportsFind: bool) =
db.get(key, grab)[]
v == value2

db.del(key)[]
check:
db.del(key)[]
not db.get(key, proc(data: openArray[byte]) = discard)[]
not db.contains(key)[]

db.del(key)[] # does nothing
not db.del(key)[] # does nothing

db.put(key, value2)[] # overwrite old value
check:
db.contains(key)[]
db.clear()[]
not db.contains(key)[]
not db.clear()[]

if supportsFind:
check:
Expand Down