278 lines
11 KiB
Go
278 lines
11 KiB
Go
/*
|
|
* Copyright (c) 2014 The btcsuite developers
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
// This interface was inspired heavily by the excellent boltdb project at
|
|
// https://github.com/boltdb/bolt by Ben B. Johnson.
|
|
|
|
package walletdb
|
|
|
|
import "io"
|
|
|
|
// Bucket represents a collection of key/value pairs.
|
|
type Bucket interface {
|
|
// Bucket retrieves a nested bucket with the given key. Returns nil if
|
|
// the bucket does not exist.
|
|
Bucket(key []byte) Bucket
|
|
|
|
// CreateBucket creates and returns a new nested bucket with the given
|
|
// key. Returns ErrBucketExists if the bucket already exists,
|
|
// ErrBucketNameRequired if the key is empty, or ErrIncompatibleValue
|
|
// if the key value is otherwise invalid for the particular database
|
|
// implementation. Other errors are possible depending on the
|
|
// implementation.
|
|
CreateBucket(key []byte) (Bucket, error)
|
|
|
|
// CreateBucketIfNotExists creates and returns a new nested bucket with
|
|
// the given key if it does not already exist. Returns
|
|
// ErrBucketNameRequired if the key is empty or ErrIncompatibleValue
|
|
// if the key value is otherwise invalid for the particular database
|
|
// backend. Other errors are possible depending on the implementation.
|
|
CreateBucketIfNotExists(key []byte) (Bucket, error)
|
|
|
|
// DeleteBucket removes a nested bucket with the given key. Returns
|
|
// ErrTxNotWritable if attempted against a read-only transaction and
|
|
// ErrBucketNotFound if the specified bucket does not exist.
|
|
DeleteBucket(key []byte) error
|
|
|
|
// ForEach invokes the passed function with every key/value pair in
|
|
// the bucket. This includes nested buckets, in which case the value
|
|
// is nil, but it does not include the key/value pairs within those
|
|
// nested buckets.
|
|
//
|
|
// NOTE: The values returned by this function are only valid during a
|
|
// transaction. Attempting to access them after a transaction has ended
|
|
// results in undefined behavior. This constraint prevents additional
|
|
// data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
ForEach(func(k, v []byte) error) error
|
|
|
|
// Writable returns whether or not the bucket is writable.
|
|
Writable() bool
|
|
|
|
// Put saves the specified key/value pair to the bucket. Keys that do
|
|
// not already exist are added and keys that already exist are
|
|
// overwritten. Returns ErrTxNotWritable if attempted against a
|
|
// read-only transaction.
|
|
Put(key, value []byte) error
|
|
|
|
// Get returns the value for the given key. Returns nil if the key does
|
|
// not exist in this bucket (or nested buckets).
|
|
//
|
|
// NOTE: The value returned by this function is only valid during a
|
|
// transaction. Attempting to access it after a transaction has ended
|
|
// results in undefined behavior. This constraint prevents additional
|
|
// data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
Get(key []byte) []byte
|
|
|
|
// Delete removes the specified key from the bucket. Deleting a key
|
|
// that does not exist does not return an error. Returns
|
|
// ErrTxNotWritable if attempted against a read-only transaction.
|
|
Delete(key []byte) error
|
|
|
|
// Cursor returns a new cursor, allowing for iteration over the bucket's
|
|
// key/value pairs and nested buckets in forward or backward order.
|
|
Cursor() Cursor
|
|
}
|
|
|
|
// Cursor represents a cursor over key/value pairs and nested buckets of a
|
|
// bucket.
|
|
//
|
|
// Note that open cursors are not tracked on bucket changes and any
|
|
// modifications to the bucket, with the exception of Cursor.Delete, invalidate
|
|
// the cursor. After invalidation, the cursor must be repositioned, or the keys
|
|
// and values returned may be unpredictable.
|
|
type Cursor interface {
|
|
// Bucket returns the bucket the cursor was created for.
|
|
Bucket() Bucket
|
|
|
|
// Delete removes the current key/value pair the cursor is at without
|
|
// invalidating the cursor. Returns ErrTxNotWritable if attempted on a
|
|
// read-only transaction, or ErrIncompatibleValue if attempted when the
|
|
// cursor points to a nested bucket.
|
|
Delete() error
|
|
|
|
// First positions the cursor at the first key/value pair and returns
|
|
// the pair.
|
|
First() (key, value []byte)
|
|
|
|
// Last positions the cursor at the last key/value pair and returns the
|
|
// pair.
|
|
Last() (key, value []byte)
|
|
|
|
// Next moves the cursor one key/value pair forward and returns the new
|
|
// pair.
|
|
Next() (key, value []byte)
|
|
|
|
// Prev moves the cursor one key/value pair backward and returns the new
|
|
// pair.
|
|
Prev() (key, value []byte)
|
|
|
|
// Seek positions the cursor at the passed seek key. If the key does
|
|
// not exist, the cursor is moved to the next key after seek. Returns
|
|
// the new pair.
|
|
Seek(seek []byte) (key, value []byte)
|
|
}
|
|
|
|
// Tx represents a database transaction. It can either by read-only or
|
|
// read-write. The transaction provides a root bucket against which all read
|
|
// and writes occur.
|
|
//
|
|
// As would be expected with a transaction, no changes will be saved to the
|
|
// database until it has been committed. The transaction will only provide a
|
|
// view of the database at the time it was created. Transactions should not be
|
|
// long running operations.
|
|
type Tx interface {
|
|
// RootBucket returns the top-most bucket for the namespace the
|
|
// transaction was created from.
|
|
RootBucket() Bucket
|
|
|
|
// Commit commits all changes that have been made through the root
|
|
// bucket and all of its sub-buckets to persistent storage.
|
|
Commit() error
|
|
|
|
// Rollback undoes all changes that have been made to the root bucket
|
|
// and all of its sub-buckets.
|
|
Rollback() error
|
|
}
|
|
|
|
// Namespace represents a database namespace that is inteded to support the
|
|
// concept of a single entity that controls the opening, creating, and closing
|
|
// of a database while providing other entities their own namespace to work in.
|
|
type Namespace interface {
|
|
// Begin starts a transaction which is either read-only or read-write
|
|
// depending on the specified flag. Multiple read-only transactions
|
|
// can be started simultaneously while only a single read-write
|
|
// transaction can be started at a time. The call will block when
|
|
// starting a read-write transaction when one is already open.
|
|
//
|
|
// NOTE: The transaction must be closed by calling Rollback or Commit on
|
|
// it when it is no longer needed. Failure to do so can result in
|
|
// unclaimed memory depending on the specific database implementation.
|
|
Begin(writable bool) (Tx, error)
|
|
|
|
// View invokes the passed function in the context of a managed
|
|
// read-only transaction. Any errors returned from the user-supplied
|
|
// function are returned from this function.
|
|
//
|
|
// Calling Rollback on the transaction passed to the user-supplied
|
|
// function will result in a panic.
|
|
View(fn func(Tx) error) error
|
|
|
|
// Update invokes the passed function in the context of a managed
|
|
// read-write transaction. Any errors returned from the user-supplied
|
|
// function will cause the transaction to be rolled back and are
|
|
// returned from this function. Otherwise, the transaction is commited
|
|
// when the user-supplied function returns a nil error.
|
|
//
|
|
// Calling Rollback on the transaction passed to the user-supplied
|
|
// function will result in a panic.
|
|
Update(fn func(Tx) error) error
|
|
}
|
|
|
|
// DB represents a collection of namespaces which are persisted. All database
|
|
// access is performed through transactions which are obtained through the
|
|
// specific Namespace.
|
|
type DB interface {
|
|
// Namespace returns a Namespace interface for the provided key. See
|
|
// the Namespace interface documentation for more details. Attempting
|
|
// to access a Namespace on a database that is not open yet or has been
|
|
// closed will result in ErrDbNotOpen. Namespaces are created in the
|
|
// database on first access.
|
|
Namespace(key []byte) (Namespace, error)
|
|
|
|
// DeleteNamespace deletes the namespace for the passed key.
|
|
// ErrBucketNotFound will be returned if the namespace does not exist.
|
|
DeleteNamespace(key []byte) error
|
|
|
|
// Copy writes a copy of the database to the provided writer. This
|
|
// call will start a read-only transaction to perform all operations.
|
|
Copy(w io.Writer) error
|
|
|
|
// Close cleanly shuts down the database and syncs all data.
|
|
Close() error
|
|
}
|
|
|
|
// Driver defines a structure for backend drivers to use when they registered
|
|
// themselves as a backend which implements the Db interface.
|
|
type Driver struct {
|
|
// DbType is the identifier used to uniquely identify a specific
|
|
// database driver. There can be only one driver with the same name.
|
|
DbType string
|
|
|
|
// Create is the function that will be invoked with all user-specified
|
|
// arguments to create the database. This function must return
|
|
// ErrDbExists if the database already exists.
|
|
Create func(args ...interface{}) (DB, error)
|
|
|
|
// Open is the function that will be invoked with all user-specified
|
|
// arguments to open the database. This function must return
|
|
// ErrDbDoesNotExist if the database has not already been created.
|
|
Open func(args ...interface{}) (DB, error)
|
|
}
|
|
|
|
// driverList holds all of the registered database backends.
|
|
var drivers = make(map[string]*Driver)
|
|
|
|
// RegisterDriver adds a backend database driver to available interfaces.
|
|
// ErrDbTypeRegistered will be retruned if the database type for the driver has
|
|
// already been registered.
|
|
func RegisterDriver(driver Driver) error {
|
|
if _, exists := drivers[driver.DbType]; exists {
|
|
return ErrDbTypeRegistered
|
|
}
|
|
|
|
drivers[driver.DbType] = &driver
|
|
return nil
|
|
}
|
|
|
|
// SupportedDrivers returns a slice of strings that represent the database
|
|
// drivers that have been registered and are therefore supported.
|
|
func SupportedDrivers() []string {
|
|
supportedDBs := make([]string, 0, len(drivers))
|
|
for _, drv := range drivers {
|
|
supportedDBs = append(supportedDBs, drv.DbType)
|
|
}
|
|
return supportedDBs
|
|
}
|
|
|
|
// Create intializes and opens a database for the specified type. The arguments
|
|
// are specific to the database type driver. See the documentation for the
|
|
// database driver for further details.
|
|
//
|
|
// ErrDbUnknownType will be returned if the the database type is not registered.
|
|
func Create(dbType string, args ...interface{}) (DB, error) {
|
|
drv, exists := drivers[dbType]
|
|
if !exists {
|
|
return nil, ErrDbUnknownType
|
|
}
|
|
|
|
return drv.Create(args...)
|
|
}
|
|
|
|
// Open opens an existing database for the specified type. The arguments are
|
|
// specific to the database type driver. See the documentation for the database
|
|
// driver for further details.
|
|
//
|
|
// ErrDbUnknownType will be returned if the the database type is not registered.
|
|
func Open(dbType string, args ...interface{}) (DB, error) {
|
|
drv, exists := drivers[dbType]
|
|
if !exists {
|
|
return nil, ErrDbUnknownType
|
|
}
|
|
|
|
return drv.Open(args...)
|
|
}
|