kvstore: return bool from del, add clear (#574)

This allows making decisions based on whether an element was present
during the `del`
This commit is contained in:
Jacek Sieka 2023-01-03 16:38:27 +01:00 committed by GitHub
parent 40ec601d07
commit 8f0ae55353
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 16 deletions

View File

@ -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].}
@ -42,6 +43,7 @@ type
getProc: GetProc
findProc: FindProc
delProc: DelProc
clearProc: ClearProc
containsProc: ContainsProc
closeProc: CloseProc
@ -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
@ -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)
@ -108,7 +121,7 @@ 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,
@ -116,6 +129,7 @@ func kvStore*[T: RootRef](x: T): KvStoreRef =
getProc: getImpl[T],
findProc: findImpl[T],
delProc: delImpl[T],
clearProc: clearImpl[T],
containsProc: containsImpl[T],
closeProc: closeImpl[T]
)
@ -139,9 +153,19 @@ proc find*(
ok(total)
proc del*(db: MemStoreRef, key: openArray[byte]): KvResult[void] =
proc del*(db: MemStoreRef, key: openArray[byte]): KvResult[bool] =
if @key in db.records:
db.records.del(@key)
ok()
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))

View File

@ -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
@ -433,17 +434,17 @@ 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
@ -451,11 +452,28 @@ proc del*(db: SqKeyspaceRef, key: openArray[byte]): KvResult[void] =
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)
@ -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)
@ -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 >= ?;")

View File

@ -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)[]
@ -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: