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  }