1 package bdb
2 3 import (
4 "io"
5 "os"
6 7 bolt "go.etcd.io/bbolt"
8 9 "github.com/p9c/p9/pkg/walletdb"
10 )
11 12 // convertErr converts some bolt errors to the equivalent walletdb error.
13 func convertErr(e1 error) (e error) {
14 switch e1 {
15 // Database open/create errors.
16 case bolt.ErrDatabaseNotOpen:
17 return walletdb.ErrDbNotOpen
18 case bolt.ErrInvalid:
19 return walletdb.ErrInvalid
20 // Transaction errors.
21 case bolt.ErrTxNotWritable:
22 return walletdb.ErrTxNotWritable
23 case bolt.ErrTxClosed:
24 return walletdb.ErrTxClosed
25 // value/bucket errors.
26 case bolt.ErrBucketNotFound:
27 return walletdb.ErrBucketNotFound
28 case bolt.ErrBucketExists:
29 return walletdb.ErrBucketExists
30 case bolt.ErrBucketNameRequired:
31 return walletdb.ErrBucketNameRequired
32 case bolt.ErrKeyRequired:
33 return walletdb.ErrKeyRequired
34 case bolt.ErrKeyTooLarge:
35 return walletdb.ErrKeyTooLarge
36 case bolt.ErrValueTooLarge:
37 return walletdb.ErrValueTooLarge
38 case bolt.ErrIncompatibleValue:
39 return walletdb.ErrIncompatibleValue
40 }
41 // Return the original error if none of the above applies.
42 return e1
43 }
44 45 // transaction represents a database transaction. It can either by read-only or
46 // read-write and implements the walletdb Tx interfaces. The transaction
47 // provides a root bucket against which all read and writes occur.
48 type transaction struct {
49 boltTx *bolt.Tx
50 }
51 52 func (tx *transaction) ReadBucket(key []byte) walletdb.ReadBucket {
53 return tx.ReadWriteBucket(key)
54 }
55 func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
56 boltBucket := tx.boltTx.Bucket(key)
57 if boltBucket == nil {
58 return nil
59 }
60 return (*bucket)(boltBucket)
61 }
62 func (tx *transaction) CreateTopLevelBucket(key []byte) (rwb walletdb.ReadWriteBucket, e error) {
63 var boltBucket *bolt.Bucket
64 if boltBucket, e = tx.boltTx.CreateBucket(key); D.Chk(convertErr(e)) {
65 return
66 }
67 return (*bucket)(boltBucket), nil
68 }
69 func (tx *transaction) DeleteTopLevelBucket(key []byte) (e error) {
70 if e = tx.boltTx.DeleteBucket(key); E.Chk(e) {
71 return convertErr(e)
72 }
73 return
74 }
75 76 // Commit commits all changes that have been made through the root bucket and all of its sub-buckets to persistent
77 // storage.
78 //
79 // This function is part of the walletdb.Tx interface implementation.
80 func (tx *transaction) Commit() (e error) {
81 return convertErr(tx.boltTx.Commit())
82 }
83 84 // Rollback undoes all changes that have been made to the root bucket and all of its sub-buckets.
85 //
86 // This function is part of the walletdb.Tx interface implementation.
87 func (tx *transaction) Rollback() (e error) {
88 return convertErr(tx.boltTx.Rollback())
89 }
90 91 // bucket is an internal type used to represent a collection of key/value pairs and implements the walletdb Bucket
92 // interfaces.
93 type bucket bolt.Bucket
94 95 // Enforce bucket implements the walletdb Bucket interfaces.
96 var _ walletdb.ReadWriteBucket = (*bucket)(nil)
97 98 // NestedReadWriteBucket retrieves a nested bucket with the given key. Returns nil if the bucket does not exist.
99 //
100 // This function is part of the walletdb.ReadWriteBucket interface implementation.
101 func (b *bucket) NestedReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
102 boltBucket := (*bolt.Bucket)(b).Bucket(key)
103 // Don't return a non-nil interface to a nil pointer.
104 if boltBucket == nil {
105 return nil
106 }
107 return (*bucket)(boltBucket)
108 }
109 func (b *bucket) NestedReadBucket(key []byte) walletdb.ReadBucket {
110 return b.NestedReadWriteBucket(key)
111 }
112 113 // CreateBucket creates and returns a new nested bucket with the given key.
114 //
115 // Returns ErrBucketExists if the bucket already exists, ErrBucketNameRequired if the key is empty, or
116 // ErrIncompatibleValue if the key value is otherwise invalid.
117 //
118 // This function is part of the walletdb.Bucket interface implementation.
119 func (b *bucket) CreateBucket(key []byte) (rwb walletdb.ReadWriteBucket, e error) {
120 var boltBucket *bolt.Bucket
121 if boltBucket, e = (*bolt.Bucket)(b).CreateBucket(key); D.Chk(convertErr(e)) {
122 return
123 }
124 return (*bucket)(boltBucket), e
125 }
126 127 // CreateBucketIfNotExists creates and returns a new nested bucket with the given key if it does not already exist.
128 //
129 // Returns ErrBucketNameRequired if the key is empty or ErrIncompatibleValue if the key value is otherwise invalid.
130 //
131 // This function is part of the walletdb.Bucket interface implementation.
132 func (b *bucket) CreateBucketIfNotExists(key []byte) (rwb walletdb.ReadWriteBucket, e error) {
133 var boltBucket *bolt.Bucket
134 if boltBucket, e = (*bolt.Bucket)(b).CreateBucketIfNotExists(key); D.Chk(convertErr(e)) {
135 } else {
136 rwb = (*bucket)(boltBucket)
137 }
138 return
139 }
140 141 // DeleteNestedBucket removes a nested bucket with the given key.
142 //
143 // Returns ErrTxNotWritable if attempted against a read-only transaction and ErrBucketNotFound if the specified bucket
144 // does not exist.
145 //
146 // This function is part of the walletdb.Bucket interface implementation.
147 func (b *bucket) DeleteNestedBucket(key []byte) (e error) {
148 return convertErr((*bolt.Bucket)(b).DeleteBucket(key))
149 }
150 151 // ForEach invokes the passed function with every key/value pair in the bucket.
152 //
153 // This includes nested buckets, in which case the value is nil, but it does not include the key/value pairs within
154 // those nested buckets.
155 //
156 // NOTE: The values returned by this function are only valid during a transaction. Attempting to access them after a
157 // transaction has ended will likely result in an access violation.
158 //
159 // This function is part of the walletdb.Bucket interface implementation.
160 func (b *bucket) ForEach(fn func(k, v []byte) error) (e error) {
161 return convertErr((*bolt.Bucket)(b).ForEach(fn))
162 }
163 164 // Put saves the specified key/value pair to the bucket.
165 //
166 // Keys that do not already exist are added and keys that already exist are overwritten.
167 //
168 // Returns ErrTxNotWritable if attempted against a read-only transaction.
169 //
170 // This function is part of the walletdb.Bucket interface implementation.
171 func (b *bucket) Put(key, value []byte) (e error) {
172 return convertErr((*bolt.Bucket)(b).Put(key, value))
173 }
174 175 // Get returns the value for the given key.
176 //
177 // Returns nil if the key does not exist in this bucket (or nested buckets).
178 //
179 // NOTE: The value returned by this function is only valid during a transaction. Attempting to access it after a
180 // transaction has ended will likely result in an access violation.
181 //
182 // This function is part of the walletdb.Bucket interface implementation.
183 func (b *bucket) Get(key []byte) []byte {
184 return (*bolt.Bucket)(b).Get(key)
185 }
186 187 // Delete removes the specified key from the bucket.
188 //
189 // Deleting a key that does not exist does not return an error.
190 //
191 // Returns ErrTxNotWritable if attempted against a read-only transaction.
192 //
193 // This function is part of the walletdb.Bucket interface implementation.
194 func (b *bucket) Delete(key []byte) (e error) {
195 return convertErr((*bolt.Bucket)(b).Delete(key))
196 }
197 func (b *bucket) ReadCursor() walletdb.ReadCursor {
198 return b.ReadWriteCursor()
199 }
200 201 // ReadWriteCursor returns a new cursor, allowing for iteration over the bucket's key/value pairs and nested buckets in
202 // forward or backward order.
203 //
204 // This function is part of the walletdb.Bucket interface implementation.
205 func (b *bucket) ReadWriteCursor() walletdb.ReadWriteCursor {
206 return (*cursor)((*bolt.Bucket)(b).Cursor())
207 }
208 209 // cursor represents a cursor over key/value pairs and nested buckets of a bucket.
210 //
211 // Note that open cursors are not tracked on bucket changes and any modifications to the bucket, with the exception of
212 // cursor.Delete, invalidate the cursor. After invalidation, the cursor must be repositioned, or the keys and values
213 // returned may be unpredictable.
214 type cursor bolt.Cursor
215 216 // Delete removes the current key/value pair the cursor is at without invalidating the cursor.
217 //
218 // Returns ErrTxNotWritable if attempted on a read-only transaction, or ErrIncompatibleValue if attempted when the
219 // cursor points to a nested bucket.
220 //
221 // This function is part of the walletdb.Cursor interface implementation.
222 func (c *cursor) Delete() (e error) {
223 return convertErr((*bolt.Cursor)(c).Delete())
224 }
225 226 // First positions the cursor at the first key/value pair and returns the pair.
227 //
228 // This function is part of the walletdb.Cursor interface implementation.
229 func (c *cursor) First() (key, value []byte) {
230 return (*bolt.Cursor)(c).First()
231 }
232 233 // Last positions the cursor at the last key/value pair and returns the pair.
234 //
235 // This function is part of the walletdb.Cursor interface implementation.
236 func (c *cursor) Last() (key, value []byte) {
237 return (*bolt.Cursor)(c).Last()
238 }
239 240 // Next moves the cursor one key/value pair forward and returns the new pair.
241 //
242 // This function is part of the walletdb.Cursor interface implementation.
243 func (c *cursor) Next() (key, value []byte) {
244 return (*bolt.Cursor)(c).Next()
245 }
246 247 // Prev moves the cursor one key/value pair backward and returns the new pair.
248 //
249 // This function is part of the walletdb.Cursor interface implementation.
250 func (c *cursor) Prev() (key, value []byte) {
251 return (*bolt.Cursor)(c).Prev()
252 }
253 254 // Seek positions the cursor at the passed seek key.
255 //
256 // If the key does not exist, the cursor is moved to the next key after seek.
257 //
258 // Returns the new pair.
259 //
260 // This function is part of the walletdb.Cursor interface implementation.
261 func (c *cursor) Seek(seek []byte) (key, value []byte) {
262 return (*bolt.Cursor)(c).Seek(seek)
263 }
264 265 // db represents a collection of namespaces which are persisted and implements the walletdb.Db interface.
266 //
267 // All database access is performed through transactions which are obtained through the specific Namespace.
268 type db bolt.DB
269 270 // Enforce db implements the walletdb.Db interface.
271 var _ walletdb.DB = (*db)(nil)
272 273 func (db *db) beginTx(writable bool) (t *transaction, e error) {
274 var boltTx *bolt.Tx
275 if boltTx, e = (*bolt.DB)(db).Begin(writable); E.Chk(e) {
276 return nil, convertErr(e)
277 }
278 return &transaction{boltTx: boltTx}, nil
279 }
280 func (db *db) BeginReadTx() (walletdb.ReadTx, error) {
281 return db.beginTx(false)
282 }
283 func (db *db) BeginReadWriteTx() (walletdb.ReadWriteTx, error) {
284 return db.beginTx(true)
285 }
286 287 // Copy writes a copy of the database to the provided writer.
288 //
289 // This call will start a read-only transaction to perform all operations.
290 //
291 // This function is part of the walletdb.Db interface implementation.
292 func (db *db) Copy(w io.Writer) (e error) {
293 return convertErr(
294 (*bolt.DB)(db).View(
295 func(tx *bolt.Tx) (e error) {
296 return tx.Copy(w)
297 },
298 ),
299 )
300 }
301 302 // Close cleanly shuts down the database and syncs all data.
303 //
304 // This function is part of the walletdb.Db interface implementation.
305 func (db *db) Close() (e error) {
306 return convertErr((*bolt.DB)(db).Close())
307 }
308 309 // filesExists reports whether the named file or directory exists.
310 func fileExists(name string) bool {
311 var e error
312 if _, e = os.Stat(name); E.Chk(e) {
313 if os.IsNotExist(e) {
314 return false
315 }
316 }
317 return true
318 }
319 320 // openDB opens the database at the provided path.
321 //
322 // walletdb.ErrDbDoesNotExist is returned if the database doesn't exist and the create flag is not set.
323 func openDB(dbPath string, create bool) (d walletdb.DB, e error) {
324 if !create && !fileExists(dbPath) {
325 return nil, walletdb.ErrDbDoesNotExist
326 }
327 var boltDB *bolt.DB
328 if boltDB, e = bolt.Open(dbPath, 0600, nil); E.Chk(e) {
329 }
330 return (*db)(boltDB), convertErr(e)
331 }
332