mirror of https://github.com/status-im/nim-eth.git
132 lines
3.5 KiB
Nim
132 lines
3.5 KiB
Nim
import
|
|
os, sqlite3, stew/ranges, stew/ranges/ptr_arith, eth/trie/[db_tracing, trie_defs],
|
|
backend_defs
|
|
|
|
type
|
|
SqliteChainDB* = ref object of RootObj
|
|
store: PSqlite3
|
|
selectStmt, insertStmt, deleteStmt: PStmt
|
|
|
|
ChainDB* = SqliteChainDB
|
|
|
|
proc put*(db: ChainDB, key, value: openarray[byte])
|
|
|
|
proc newChainDB*(basePath: string, inMemory = false): ChainDB =
|
|
result.new()
|
|
let dbPath = if inMemory: ":memory:" else: basePath / "nimbus.db"
|
|
var s = sqlite3.open(dbPath, result.store)
|
|
if s != SQLITE_OK:
|
|
raiseStorageInitError()
|
|
|
|
template execQuery(q: string) =
|
|
var s: Pstmt
|
|
if prepare_v2(result.store, q, q.len.int32, s, nil) == SQLITE_OK:
|
|
if step(s) != SQLITE_DONE or finalize(s) != SQLITE_OK:
|
|
raiseStorageInitError()
|
|
else:
|
|
raiseStorageInitError()
|
|
|
|
# TODO: check current version and implement schema versioning
|
|
execQuery "PRAGMA user_version = 1;"
|
|
|
|
execQuery """
|
|
CREATE TABLE IF NOT EXISTS trie_nodes(
|
|
key BLOB PRIMARY KEY,
|
|
value BLOB
|
|
);
|
|
"""
|
|
|
|
template prepare(q: string): PStmt =
|
|
var s: Pstmt
|
|
if prepare_v2(result.store, q, q.len.int32, s, nil) != SQLITE_OK:
|
|
raiseStorageInitError()
|
|
s
|
|
|
|
result.selectStmt = prepare "SELECT value FROM trie_nodes WHERE key = ?;"
|
|
|
|
if sqlite3.libversion_number() < 3024000:
|
|
result.insertStmt = prepare """
|
|
INSERT OR REPLACE INTO trie_nodes(key, value) VALUES (?, ?);
|
|
"""
|
|
else:
|
|
result.insertStmt = prepare """
|
|
INSERT INTO trie_nodes(key, value) VALUES (?, ?)
|
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value;
|
|
"""
|
|
|
|
result.deleteStmt = prepare "DELETE FROM trie_nodes WHERE key = ?;"
|
|
|
|
put(result, emptyRlpHash.data, emptyRlp)
|
|
|
|
proc bindBlob(s: Pstmt, n: int, blob: openarray[byte]): int32 =
|
|
sqlite3.bind_blob(s, n.int32, blob.baseAddr, blob.len.int32, nil)
|
|
|
|
proc get*(db: ChainDB, key: openarray[byte]): seq[byte] =
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeyReadError(key)
|
|
|
|
check reset(db.selectStmt)
|
|
check clearBindings(db.selectStmt)
|
|
check bindBlob(db.selectStmt, 1, key)
|
|
|
|
case step(db.selectStmt)
|
|
of SQLITE_ROW:
|
|
var
|
|
resStart = columnBlob(db.selectStmt, 0)
|
|
resLen = columnBytes(db.selectStmt, 0)
|
|
result = newSeq[byte](resLen)
|
|
copyMem(result.baseAddr, resStart, resLen)
|
|
traceGet key, result
|
|
of SQLITE_DONE:
|
|
discard
|
|
else:
|
|
raiseKeyReadError(key)
|
|
|
|
proc put*(db: ChainDB, key, value: openarray[byte]) =
|
|
tracePut key, value
|
|
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeyWriteError(key)
|
|
|
|
check reset(db.insertStmt)
|
|
check clearBindings(db.insertStmt)
|
|
check bindBlob(db.insertStmt, 1, key)
|
|
check bindBlob(db.insertStmt, 2, value)
|
|
|
|
if step(db.insertStmt) != SQLITE_DONE:
|
|
raiseKeyWriteError(key)
|
|
|
|
proc contains*(db: ChainDB, key: openarray[byte]): bool =
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeySearchError(key)
|
|
|
|
check reset(db.selectStmt)
|
|
check clearBindings(db.selectStmt)
|
|
check bindBlob(db.selectStmt, 1, key)
|
|
|
|
case step(db.selectStmt)
|
|
of SQLITE_ROW: result = true
|
|
of SQLITE_DONE: result = false
|
|
else: raiseKeySearchError(key)
|
|
|
|
proc del*(db: ChainDB, key: openarray[byte]) =
|
|
traceDel key
|
|
|
|
template check(op) =
|
|
let status = op
|
|
if status != SQLITE_OK: raiseKeyDeletionError(key)
|
|
|
|
check reset(db.deleteStmt)
|
|
check clearBindings(db.deleteStmt)
|
|
check bindBlob(db.deleteStmt, 1, key)
|
|
|
|
if step(db.deleteStmt) != SQLITE_DONE:
|
|
raiseKeyDeletionError(key)
|
|
|
|
proc close*(db: ChainDB) =
|
|
discard sqlite3.close(db.store)
|
|
reset(db[])
|