db.go raw

   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