lbcwallet/walletdb/interface.go
2015-04-20 18:20:19 -04:00

279 lines
11 KiB
Go

/*
* Copyright (c) 2014 Conformal Systems LLC <info@conformal.com>
*
* 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...)
}