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