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  }