decred.org/dcrwallet/v3@v3.1.0/wallet/walletdb/interface.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 // This interface was inspired heavily by the excellent boltdb project at 7 // https://github.com/boltdb/bolt by Ben B. Johnson. 8 9 package walletdb 10 11 import ( 12 "context" 13 "io" 14 "runtime/trace" 15 16 "decred.org/dcrwallet/v3/errors" 17 ) 18 19 // ReadTx represents a database transaction that can only be used for reads. If 20 // a database update must occur, use a ReadWriteTx. 21 type ReadTx interface { 22 // ReadBucket opens the root bucket for read only access. If the bucket 23 // described by the key does not exist, nil is returned. 24 ReadBucket(key []byte) ReadBucket 25 26 // Rollback closes the transaction, discarding changes (if any) if the 27 // database was modified by a write transaction. 28 Rollback() error 29 } 30 31 // ReadWriteTx represents a database transaction that can be used for both reads 32 // and writes. When only reads are necessary, consider using a ReadTx instead. 33 type ReadWriteTx interface { 34 ReadTx 35 36 // ReadWriteBucket opens the root bucket for read/write access. If the 37 // bucket described by the key does not exist, nil is returned. 38 ReadWriteBucket(key []byte) ReadWriteBucket 39 40 // CreateTopLevelBucket creates the top level bucket for a key if it 41 // does not exist. The newly-created bucket is returned. 42 CreateTopLevelBucket(key []byte) (ReadWriteBucket, error) 43 44 // DeleteTopLevelBucket deletes the top level bucket for a key. This 45 // errors if the bucket can not be found or the key keys a single value 46 // instead of a bucket. 47 DeleteTopLevelBucket(key []byte) error 48 49 // Commit commits all changes that have been on the transaction's root 50 // buckets and all of their sub-buckets to persistent storage. 51 Commit() error 52 } 53 54 // ReadBucket represents a bucket (a hierarchical structure within the database) 55 // that is only allowed to perform read operations. 56 type ReadBucket interface { 57 // NestedReadBucket retrieves a nested bucket with the given key. 58 // Returns nil if the bucket does not exist. 59 NestedReadBucket(key []byte) ReadBucket 60 61 // ForEach invokes the passed function with every key/value pair in 62 // the bucket. This includes nested buckets, in which case the value 63 // is nil, but it does not include the key/value pairs within those 64 // nested buckets. 65 // 66 // NOTE: The values returned by this function are only valid during a 67 // transaction. Attempting to access them after a transaction has ended 68 // results in undefined behavior. This constraint prevents additional 69 // data copies and allows support for memory-mapped database 70 // implementations. 71 ForEach(func(k, v []byte) error) error 72 73 // Get returns the value for the given key. Returns nil if the key does 74 // not exist in this bucket (or nested buckets). 75 // 76 // NOTE: The value returned by this function is only valid during a 77 // transaction. Attempting to access it after a transaction has ended 78 // results in undefined behavior. This constraint prevents additional 79 // data copies and allows support for memory-mapped database 80 // implementations. 81 Get(key []byte) []byte 82 83 // KeyN returns the number of keys and value pairs inside a bucket. 84 KeyN() int 85 86 ReadCursor() ReadCursor 87 } 88 89 // ReadWriteBucket represents a bucket (a hierarchical structure within the 90 // database) that is allowed to perform both read and write operations. 91 type ReadWriteBucket interface { 92 ReadBucket 93 94 // NestedReadWriteBucket retrieves a nested bucket with the given key. 95 // Returns nil if the bucket does not exist. 96 NestedReadWriteBucket(key []byte) ReadWriteBucket 97 98 // CreateBucket creates and returns a new nested bucket with the given key. 99 // Errors with code Exist if the bucket already exists and Invalid if the 100 // key is empty or otherwise invalid for the driver. 101 CreateBucket(key []byte) (ReadWriteBucket, error) 102 103 // CreateBucketIfNotExists creates and returns a new nested bucket with the 104 // given key if it does not already exist. Errors with code Invalid if the 105 // key is empty or the key/value is not valid for the driver. 106 CreateBucketIfNotExists(key []byte) (ReadWriteBucket, error) 107 108 // DeleteNestedBucket removes a nested bucket with the given key. Errors 109 // with code Invalid if attempted against a read-only transaction and 110 // NotExist if the specified bucket does not exist. 111 DeleteNestedBucket(key []byte) error 112 113 // Put saves the specified key/value pair to the bucket. Keys that do not 114 // already exist are added and keys that already exist are overwritten. 115 // Errors with code Invalid if attempted against a read-only transaction. 116 Put(key, value []byte) error 117 118 // Delete removes the specified key from the bucket. Deleting a key that 119 // does not exist does not return an error. Errors with code Invalid if 120 // attempted against a read-only transaction. 121 Delete(key []byte) error 122 123 // Cursor returns a new cursor, allowing for iteration over the bucket's 124 // key/value pairs and nested buckets in forward or backward order. 125 // Only one cursor can be opened at a time and should be closed before 126 // committing or rolling back the transaction. 127 ReadWriteCursor() ReadWriteCursor 128 } 129 130 // ReadCursor represents a bucket cursor that can be positioned at the start or 131 // end of the bucket's key/value pairs and iterate over pairs in the bucket. 132 // This type is only allowed to perform database read operations. 133 type ReadCursor interface { 134 // First positions the cursor at the first key/value pair and returns 135 // the pair. 136 First() (key, value []byte) 137 138 // Last positions the cursor at the last key/value pair and returns the 139 // pair. 140 Last() (key, value []byte) 141 142 // Next moves the cursor one key/value pair forward and returns the new 143 // pair. 144 Next() (key, value []byte) 145 146 // Prev moves the cursor one key/value pair backward and returns the new 147 // pair. 148 Prev() (key, value []byte) 149 150 // Seek positions the cursor at the passed seek key. If the key does 151 // not exist, the cursor is moved to the next key after seek. Returns 152 // the new pair. 153 Seek(seek []byte) (key, value []byte) 154 155 // Close closes the cursor. Cursors must be closed before opening a new 156 // cursor and before finishing a transaction. 157 Close() 158 } 159 160 // ReadWriteCursor represents a bucket cursor that can be positioned at the 161 // start or end of the bucket's key/value pairs and iterate over pairs in the 162 // bucket. This abstraction is allowed to perform both database read and write 163 // operations. 164 type ReadWriteCursor interface { 165 ReadCursor 166 167 // Delete removes the current key/value pair the cursor is at without 168 // invalidating the cursor. Errors with code Invalid if attempted when the 169 // cursor points to a nested bucket. 170 Delete() error 171 } 172 173 // BucketIsEmpty returns whether the bucket is empty, that is, whether there are 174 // no key/value pairs or nested buckets. 175 func BucketIsEmpty(bucket ReadBucket) bool { 176 cursor := bucket.ReadCursor() 177 k, v := cursor.First() 178 cursor.Close() 179 return k == nil && v == nil 180 } 181 182 // DB represents an ACID database. All database access is performed through 183 // read or read+write transactions. 184 type DB interface { 185 // BeginReadTx opens a database read transaction. 186 BeginReadTx() (ReadTx, error) 187 188 // BeginReadWriteTx opens a database read+write transaction. 189 BeginReadWriteTx() (ReadWriteTx, error) 190 191 // Copy writes a copy of the database to the provided writer. This 192 // call will start a read-only transaction to perform all operations. 193 Copy(w io.Writer) error 194 195 // Close cleanly shuts down the database and syncs all data. 196 Close() error 197 } 198 199 // View opens a database read transaction and executes the function f with the 200 // transaction passed as a parameter. After f exits or panics, the transaction 201 // is rolled back. If f errors, its error is returned, not a rollback error (if 202 // any occurred). 203 func View(ctx context.Context, db DB, f func(tx ReadTx) error) error { 204 defer trace.StartRegion(ctx, "db.View").End() 205 206 tx, err := db.BeginReadTx() 207 if err != nil { 208 return err 209 } 210 211 defer trace.StartRegion(ctx, "db.ReadTx").End() 212 213 // Rollback the transaction after f returns or panics. Do not recover from 214 // any panic to keep the original stack trace intact. 215 defer func() { 216 rollbackErr := tx.Rollback() 217 if err != nil { 218 err = rollbackErr 219 } 220 }() 221 222 return f(tx) 223 } 224 225 // Update opens a database read/write transaction and executes the function f 226 // with the transaction passed as a parameter. After f exits, if f did not 227 // error, the transaction is committed. Otherwise, if f did error or panic, the 228 // transaction is rolled back. If a rollback fails, the original error returned 229 // by f is still returned. If the commit fails, the commit error is returned. 230 func Update(ctx context.Context, db DB, f func(tx ReadWriteTx) error) (err error) { 231 defer trace.StartRegion(ctx, "db.Update").End() 232 233 tx, err := db.BeginReadWriteTx() 234 if err != nil { 235 return err 236 } 237 238 defer trace.StartRegion(ctx, "db.ReadWriteTx").End() 239 240 // Commit or rollback the transaction after f returns or panics. Do not 241 // recover from the panic to keep the original stack trace intact. 242 panicked := true 243 defer func() { 244 if panicked || err != nil { 245 tx.Rollback() 246 return 247 } 248 249 err = tx.Commit() 250 }() 251 252 err = f(tx) 253 panicked = false 254 return err 255 } 256 257 // Driver defines a structure for backend drivers to use when they registered 258 // themselves as a backend which implements the Db interface. 259 type Driver struct { 260 // DbType is the identifier used to uniquely identify a specific 261 // database driver. There can be only one driver with the same name. 262 DbType string 263 264 // Create is the function that will be invoked with all user-specified 265 // arguments to create the database. 266 Create func(args ...interface{}) (DB, error) 267 268 // Open is the function that will be invoked with all user-specified 269 // arguments to open the database. 270 Open func(args ...interface{}) (DB, error) 271 } 272 273 // driverList holds all of the registered database backends. 274 var drivers = make(map[string]*Driver) 275 276 // RegisterDriver adds a backend database driver to available interfaces. 277 // Errors if the will be returned if the database type for the driver has 278 // already been registered. 279 func RegisterDriver(driver Driver) error { 280 const op errors.Op = "walletdb.RegisterDriver" 281 if _, exists := drivers[driver.DbType]; exists { 282 return errors.E(op, errors.Exist, errors.Errorf("driver %q is already registered", driver.DbType)) 283 } 284 285 drivers[driver.DbType] = &driver 286 return nil 287 } 288 289 // SupportedDrivers returns a slice of strings that represent the database 290 // drivers that have been registered and are therefore supported. 291 func SupportedDrivers() []string { 292 supportedDBs := make([]string, 0, len(drivers)) 293 for _, drv := range drivers { 294 supportedDBs = append(supportedDBs, drv.DbType) 295 } 296 return supportedDBs 297 } 298 299 // Create intializes and opens a database for the specified type. The arguments 300 // are specific to the database type driver. See the documentation for the 301 // database driver for further details. 302 func Create(dbType string, args ...interface{}) (DB, error) { 303 const op errors.Op = "walletdb.Create" 304 drv, exists := drivers[dbType] 305 if !exists { 306 return nil, errors.E(op, errors.Invalid, errors.Errorf("driver %q is not registered", dbType)) 307 } 308 309 return drv.Create(args...) 310 } 311 312 // Open opens an existing database for the specified type. The arguments are 313 // specific to the database type driver. See the documentation for the database 314 // driver for further details. 315 func Open(dbType string, args ...interface{}) (DB, error) { 316 const op errors.Op = "walletdb.Open" 317 drv, exists := drivers[dbType] 318 if !exists { 319 return nil, errors.E(op, errors.Invalid, errors.Errorf("driver %q is not registered", dbType)) 320 } 321 322 return drv.Open(args...) 323 }