-
Notifications
You must be signed in to change notification settings - Fork 115
/
sqlite_backend.nim
131 lines (103 loc) · 3.51 KB
/
sqlite_backend.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import
os, sqlite3, ranges, ranges/ptr_arith, eth_trie/[db_tracing, defs],
../storage_types
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[])