github.com/btcsuite/btcwallet/walletdb@v1.4.2/interface.go (about)

     1  // Copyright (c) 2014 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  // This interface was inspired heavily by the excellent boltdb project at
     6  // https://github.com/boltdb/bolt by Ben B. Johnson.
     7  
     8  package walletdb
     9  
    10  import (
    11  	"fmt"
    12  	"io"
    13  )
    14  
    15  // ReadTx represents a database transaction that can only be used for reads.  If
    16  // a database update must occur, use a ReadWriteTx.
    17  type ReadTx interface {
    18  	// ReadBucket opens the root bucket for read only access.  If the bucket
    19  	// described by the key does not exist, nil is returned.
    20  	ReadBucket(key []byte) ReadBucket
    21  
    22  	// ForEachBucket will iterate through all top level buckets.
    23  	ForEachBucket(func(key []byte) error) error
    24  
    25  	// Rollback closes the transaction, discarding changes (if any) if the
    26  	// database was modified by a write transaction.
    27  	Rollback() error
    28  }
    29  
    30  // ReadWriteTx represents a database transaction that can be used for both reads
    31  // and writes.  When only reads are necessary, consider using a ReadTx instead.
    32  type ReadWriteTx interface {
    33  	ReadTx
    34  
    35  	// ReadWriteBucket opens the root bucket for read/write access.  If the
    36  	// bucket described by the key does not exist, nil is returned.
    37  	ReadWriteBucket(key []byte) ReadWriteBucket
    38  
    39  	// CreateTopLevelBucket creates the top level bucket for a key if it
    40  	// does not exist.  The newly-created bucket it returned.
    41  	CreateTopLevelBucket(key []byte) (ReadWriteBucket, error)
    42  
    43  	// DeleteTopLevelBucket deletes the top level bucket for a key.  This
    44  	// errors if the bucket can not be found or the key keys a single value
    45  	// instead of a bucket.
    46  	DeleteTopLevelBucket(key []byte) error
    47  
    48  	// Commit commits all changes that have been on the transaction's root
    49  	// buckets and all of their sub-buckets to persistent storage.
    50  	Commit() error
    51  
    52  	// OnCommit takes a function closure that will be executed when the
    53  	// transaction successfully gets committed.
    54  	OnCommit(func())
    55  }
    56  
    57  // ReadBucket represents a bucket (a hierarchical structure within the database)
    58  // that is only allowed to perform read operations.
    59  type ReadBucket interface {
    60  	// NestedReadBucket retrieves a nested bucket with the given key.
    61  	// Returns nil if the bucket does not exist.
    62  	NestedReadBucket(key []byte) ReadBucket
    63  
    64  	// ForEach invokes the passed function with every key/value pair in
    65  	// the bucket.  This includes nested buckets, in which case the value
    66  	// is nil, but it does not include the key/value pairs within those
    67  	// nested buckets.
    68  	//
    69  	// NOTE: The values returned by this function are only valid during a
    70  	// transaction.  Attempting to access them after a transaction has ended
    71  	// results in undefined behavior.  This constraint prevents additional
    72  	// data copies and allows support for memory-mapped database
    73  	// implementations.
    74  	ForEach(func(k, v []byte) error) error
    75  
    76  	// Get returns the value for the given key.  Returns nil if the key does
    77  	// not exist in this bucket (or nested buckets).
    78  	//
    79  	// NOTE: The value returned by this function is only valid during a
    80  	// transaction.  Attempting to access it after a transaction has ended
    81  	// results in undefined behavior.  This constraint prevents additional
    82  	// data copies and allows support for memory-mapped database
    83  	// implementations.
    84  	Get(key []byte) []byte
    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
    99  	// key.  Returns ErrBucketExists if the bucket already exists,
   100  	// ErrBucketNameRequired if the key is empty, or ErrIncompatibleValue
   101  	// if the key value is otherwise invalid for the particular database
   102  	// implementation.  Other errors are possible depending on the
   103  	// implementation.
   104  	CreateBucket(key []byte) (ReadWriteBucket, error)
   105  
   106  	// CreateBucketIfNotExists creates and returns a new nested bucket with
   107  	// the given key if it does not already exist.  Returns
   108  	// ErrBucketNameRequired if the key is empty or ErrIncompatibleValue
   109  	// if the key value is otherwise invalid for the particular database
   110  	// backend.  Other errors are possible depending on the implementation.
   111  	CreateBucketIfNotExists(key []byte) (ReadWriteBucket, error)
   112  
   113  	// DeleteNestedBucket removes a nested bucket with the given key.
   114  	// Returns ErrTxNotWritable if attempted against a read-only transaction
   115  	// and ErrBucketNotFound if the specified bucket does not exist.
   116  	DeleteNestedBucket(key []byte) error
   117  
   118  	// Put saves the specified key/value pair to the bucket.  Keys that do
   119  	// not already exist are added and keys that already exist are
   120  	// overwritten.  Returns ErrTxNotWritable if attempted against a
   121  	// read-only transaction.
   122  	Put(key, value []byte) error
   123  
   124  	// Delete removes the specified key from the bucket.  Deleting a key
   125  	// that does not exist does not return an error.  Returns
   126  	// ErrTxNotWritable if attempted against a read-only transaction.
   127  	Delete(key []byte) error
   128  
   129  	// ReadWriteCursor returns a new cursor, allowing for iteration over the
   130  	// bucket's key/value pairs and nested buckets in forward or backward
   131  	// order.
   132  	ReadWriteCursor() ReadWriteCursor
   133  
   134  	// Tx returns the bucket's transaction.
   135  	Tx() ReadWriteTx
   136  
   137  	// NextSequence returns an autoincrementing integer for the bucket.
   138  	NextSequence() (uint64, error)
   139  
   140  	// SetSequence updates the sequence number for the bucket.
   141  	SetSequence(v uint64) error
   142  
   143  	// Sequence returns the current integer for the bucket without
   144  	// incrementing it.
   145  	Sequence() uint64
   146  }
   147  
   148  // ReadCursor represents a bucket cursor that can be positioned at the start or
   149  // end of the bucket's key/value pairs and iterate over pairs in the bucket.
   150  // This type is only allowed to perform database read operations.
   151  type ReadCursor interface {
   152  	// First positions the cursor at the first key/value pair and returns
   153  	// the pair.
   154  	First() (key, value []byte)
   155  
   156  	// Last positions the cursor at the last key/value pair and returns the
   157  	// pair.
   158  	Last() (key, value []byte)
   159  
   160  	// Next moves the cursor one key/value pair forward and returns the new
   161  	// pair.
   162  	Next() (key, value []byte)
   163  
   164  	// Prev moves the cursor one key/value pair backward and returns the new
   165  	// pair.
   166  	Prev() (key, value []byte)
   167  
   168  	// Seek positions the cursor at the passed seek key.  If the key does
   169  	// not exist, the cursor is moved to the next key after seek.  Returns
   170  	// the new pair.
   171  	Seek(seek []byte) (key, value []byte)
   172  }
   173  
   174  // ReadWriteCursor represents a bucket cursor that can be positioned at the
   175  // start or end of the bucket's key/value pairs and iterate over pairs in the
   176  // bucket.  This abstraction is allowed to perform both database read and write
   177  // operations.
   178  type ReadWriteCursor interface {
   179  	ReadCursor
   180  
   181  	// Delete removes the current key/value pair the cursor is at without
   182  	// invalidating the cursor.  Returns ErrIncompatibleValue if attempted
   183  	// when the cursor points to a nested bucket.
   184  	Delete() error
   185  }
   186  
   187  // BucketIsEmpty returns whether the bucket is empty, that is, whether there are
   188  // no key/value pairs or nested buckets.
   189  func BucketIsEmpty(bucket ReadBucket) bool {
   190  	k, v := bucket.ReadCursor().First()
   191  	return k == nil && v == nil
   192  }
   193  
   194  // DB represents an ACID database. All database access is performed through
   195  // read or read+write transactions.
   196  type DB interface {
   197  	// BeginReadTx opens a database read transaction.
   198  	BeginReadTx() (ReadTx, error)
   199  
   200  	// BeginReadWriteTx opens a database read+write transaction.
   201  	BeginReadWriteTx() (ReadWriteTx, error)
   202  
   203  	// Copy writes a copy of the database to the provided writer.  This
   204  	// call will start a read-only transaction to perform all operations.
   205  	Copy(w io.Writer) error
   206  
   207  	// Close cleanly shuts down the database and syncs all data.
   208  	Close() error
   209  
   210  	// PrintStats returns all collected stats pretty printed into a string.
   211  	PrintStats() string
   212  
   213  	// View opens a database read transaction and executes the function f
   214  	// with the transaction passed as a parameter. After f exits, the
   215  	// transaction is rolled back. If f errors, its error is returned, not a
   216  	// rollback error (if any occur). The passed reset function is called
   217  	// before the start of the transaction and can be used to reset
   218  	// intermediate state. As callers may expect retries of the f closure
   219  	// (depending on the database backend used), the reset function will be
   220  	// called before each retry respectively.
   221  	//
   222  	// NOTE: For new code, this method should be used directly instead of
   223  	// the package level View() function.
   224  	View(f func(tx ReadTx) error, reset func()) error
   225  
   226  	// Update opens a database read/write transaction and executes the
   227  	// function f with the transaction passed as a parameter. After f exits,
   228  	// if f did not error, the transaction is committed. Otherwise, if f did
   229  	// error, the transaction is rolled back. If the rollback fails, the
   230  	// original error returned by f is still returned. If the commit fails,
   231  	// the commit error is returned. As callers may expect retries of the f
   232  	// closure (depending on the database backend used), the reset function
   233  	// will be called before each retry respectively.
   234  	//
   235  	// NOTE: For new code, this method should be used directly instead of
   236  	// the package level Update() function.
   237  	Update(f func(tx ReadWriteTx) error, reset func()) error
   238  }
   239  
   240  // BatchDB is a special version of the main DB interface that allows the caller
   241  // to specify write transactions that should be combined toegether if multiple
   242  // goroutines are calling the Batch method.
   243  type BatchDB interface {
   244  	DB
   245  
   246  	// Batch is similar to the package-level Update method, but it will
   247  	// attempt to optimistically combine the invocation of several
   248  	// transaction functions into a single db write transaction.
   249  	Batch(func(tx ReadWriteTx) error) error
   250  }
   251  
   252  // View opens a database read transaction and executes the function f with the
   253  // transaction passed as a parameter.  After f exits, the transaction is rolled
   254  // back.  If f errors, its error is returned, not a rollback error (if any
   255  // occur).
   256  //
   257  // NOTE: For new code the database backend's View method should be used directly
   258  // as this package level function will be phased out in the future.
   259  func View(db DB, f func(tx ReadTx) error) error {
   260  	return db.View(f, func() {})
   261  }
   262  
   263  // Update opens a database read/write transaction and executes the function f
   264  // with the transaction passed as a parameter.  After f exits, if f did not
   265  // error, the transaction is committed.  Otherwise, if f did error, the
   266  // transaction is rolled back.  If the rollback fails, the original error
   267  // returned by f is still returned.  If the commit fails, the commit error is
   268  // returned.
   269  //
   270  // NOTE: For new code the database backend's Update method should be used
   271  // directly as this package level function will be phased out in the future.
   272  func Update(db DB, f func(tx ReadWriteTx) error) error {
   273  	return db.Update(f, func() {})
   274  }
   275  
   276  // Batch opens a database read/write transaction and executes the function f
   277  // with the transaction passed as a parameter.  After f exits, if f did not
   278  // error, the transaction is committed.  Otherwise, if f did error, the
   279  // transaction is rolled back.  If the rollback fails, the original error
   280  // returned by f is still returned.  If the commit fails, the commit error is
   281  // returned.
   282  //
   283  // Batch is only useful when there are multiple goroutines calling it.
   284  func Batch(db DB, f func(tx ReadWriteTx) error) error {
   285  	batchDB, ok := db.(BatchDB)
   286  	if !ok {
   287  		return fmt.Errorf("need batch")
   288  	}
   289  
   290  	return batchDB.Batch(f)
   291  }
   292  
   293  // Driver defines a structure for backend drivers to use when they registered
   294  // themselves as a backend which implements the Db interface.
   295  type Driver struct {
   296  	// DbType is the identifier used to uniquely identify a specific
   297  	// database driver.  There can be only one driver with the same name.
   298  	DbType string
   299  
   300  	// Create is the function that will be invoked with all user-specified
   301  	// arguments to create the database.  This function must return
   302  	// ErrDbExists if the database already exists.
   303  	Create func(args ...interface{}) (DB, error)
   304  
   305  	// Open is the function that will be invoked with all user-specified
   306  	// arguments to open the database.  This function must return
   307  	// ErrDbDoesNotExist if the database has not already been created.
   308  	Open func(args ...interface{}) (DB, error)
   309  }
   310  
   311  // driverList holds all of the registered database backends.
   312  var drivers = make(map[string]*Driver)
   313  
   314  // RegisterDriver adds a backend database driver to available interfaces.
   315  // ErrDbTypeRegistered will be retruned if the database type for the driver has
   316  // already been registered.
   317  func RegisterDriver(driver Driver) error {
   318  	if _, exists := drivers[driver.DbType]; exists {
   319  		return ErrDbTypeRegistered
   320  	}
   321  
   322  	drivers[driver.DbType] = &driver
   323  	return nil
   324  }
   325  
   326  // SupportedDrivers returns a slice of strings that represent the database
   327  // drivers that have been registered and are therefore supported.
   328  func SupportedDrivers() []string {
   329  	supportedDBs := make([]string, 0, len(drivers))
   330  	for _, drv := range drivers {
   331  		supportedDBs = append(supportedDBs, drv.DbType)
   332  	}
   333  	return supportedDBs
   334  }
   335  
   336  // Create intializes and opens a database for the specified type.  The arguments
   337  // are specific to the database type driver.  See the documentation for the
   338  // database driver for further details.
   339  //
   340  // ErrDbUnknownType will be returned if the the database type is not registered.
   341  func Create(dbType string, args ...interface{}) (DB, error) {
   342  	drv, exists := drivers[dbType]
   343  	if !exists {
   344  		return nil, ErrDbUnknownType
   345  	}
   346  
   347  	return drv.Create(args...)
   348  }
   349  
   350  // Open opens an existing database for the specified type.  The arguments are
   351  // specific to the database type driver.  See the documentation for the database
   352  // driver for further details.
   353  //
   354  // ErrDbUnknownType will be returned if the the database type is not registered.
   355  func Open(dbType string, args ...interface{}) (DB, error) {
   356  	drv, exists := drivers[dbType]
   357  	if !exists {
   358  		return nil, ErrDbUnknownType
   359  	}
   360  
   361  	return drv.Open(args...)
   362  }