1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
package database
import (
"fmt"
"errors"
)
var (
// ErrNotFound is the type returned on DB implementations if an item does not
// exist.
ErrNotFound = errors.New("not found")
// ErrOpNotSupported is the type returned on DB implementations if an operation
// is not supported.
ErrOpNotSupported = errors.New("operation not supported")
)
// IsErrNotFound returns true if the cause of the given error is ErrNotFound.
func IsErrNotFound(err error) bool {
return err == ErrNotFound || cause(err) == ErrNotFound
}
// IsErrOpNotSupported returns true if the cause of the given error is ErrOpNotSupported.
func IsErrOpNotSupported(err error) bool {
return err == ErrOpNotSupported || cause(err) == ErrNotFound
}
// cause (from github.com/pkg/errors) returns the underlying cause of the
// error, if possible. An error value has a cause if it implements the
// following interface:
//
// type causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func cause(err error) error {
type causer interface {
Cause() error
}
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return err
}
// Options are configuration options for the database.
type Options struct {
Database string
ValueDir string
BadgerFileLoadingMode string
}
// Option is the modifier type over Options.
type Option func(o *Options) error
// WithValueDir is a modifier that sets the ValueDir attribute of Options.
func WithValueDir(path string) Option {
return func(o *Options) error {
o.ValueDir = path
return nil
}
}
// WithDatabase is a modifier that sets the Database attribute of Options.
func WithDatabase(db string) Option {
return func(o *Options) error {
o.Database = db
return nil
}
}
// WithBadgerFileLoadingMode is a modifier that sets the ValueLogLoadingMode
// of Badger db.
func WithBadgerFileLoadingMode(mode string) Option {
return func(o *Options) error {
o.BadgerFileLoadingMode = mode
return nil
}
}
// DB is a interface to be implemented by the databases.
type DB interface {
// Open opens the database available with the given options.
Open(dataSourceName string, opt ...Option) error
// Close closes the current database.
Close() error
// Get returns the value stored in the given table/bucket and key.
Get(bucket, key []byte) (ret []byte, err error)
// Set sets the given value in the given table/bucket and key.
Set(bucket, key, value []byte) error
// CmpAndSwap swaps the value at the given bucket and key if the current
// value is equivalent to the oldValue input. Returns 'true' if the
// swap was successful and 'false' otherwise.
CmpAndSwap(bucket, key, oldValue, newValue []byte) ([]byte, bool, error)
// Del deletes the data in the given table/bucket and key.
Del(bucket, key []byte) error
// List returns a list of all the entries in a given table/bucket.
List(bucket []byte) ([]*Entry, error)
// Update performs a transaction with multiple read-write commands.
Update(tx *Tx) error
// CreateTable creates a table or a bucket in the database.
CreateTable(bucket []byte) error
// DeleteTable deletes a table or a bucket in the database.
DeleteTable(bucket []byte) error
}
// Badger FileLoadingMode constants.
const (
BadgerMemoryMap = "mmap"
BadgerFileIO = "fileio"
)
// TxCmd is the type used to represent database command and operations.
type TxCmd int
const (
// CreateTable on a TxEntry will represent the creation of a table or
// bucket on the database.
CreateTable TxCmd = iota
// DeleteTable on a TxEntry will represent the deletion of a table or
// bucket on the database.
DeleteTable
// Get on a TxEntry will represent a command to retrieve data from the
// database.
Get
// Set on a TxEntry will represent a command to write data on the
// database.
Set
// Delete on a TxEntry represent a command to delete data on the database.
Delete
// CmpAndSwap on a TxEntry will represent a compare and swap operation on
// the database. It will compare the value read and change it if it's
// different. The TxEntry will contain the value read.
CmpAndSwap
// CmpOrRollback on a TxEntry will represent a read transaction that will
// compare the values will the ones passed, and if they don't match the
// transaction will fail
CmpOrRollback
)
// String implements the fmt.Stringer interface on TxCmd.
func (o TxCmd) String() string {
switch o {
case CreateTable:
return "create-table"
case DeleteTable:
return "delete-table"
case Get:
return "read"
case Set:
return "write"
case Delete:
return "delete"
case CmpAndSwap:
return "compare-and-swap"
case CmpOrRollback:
return "compare-and-rollback"
default:
return fmt.Sprintf("unknown(%d)", o)
}
}
// Tx represents a transaction and it's list of multiple TxEntry. Each TxEntry
// represents a read or write operation on the database.
type Tx struct {
Operations []*TxEntry
}
// CreateTable adds a new create query to the transaction.
func (tx *Tx) CreateTable(bucket []byte) {
tx.Operations = append(tx.Operations, &TxEntry{
Bucket: bucket,
Cmd: CreateTable,
})
}
// DeleteTable adds a new create query to the transaction.
func (tx *Tx) DeleteTable(bucket []byte) {
tx.Operations = append(tx.Operations, &TxEntry{
Bucket: bucket,
Cmd: DeleteTable,
})
}
// Get adds a new read query to the transaction.
func (tx *Tx) Get(bucket, key []byte) {
tx.Operations = append(tx.Operations, &TxEntry{
Bucket: bucket,
Key: key,
Cmd: Get,
})
}
// Set adds a new write query to the transaction.
func (tx *Tx) Set(bucket, key, value []byte) {
tx.Operations = append(tx.Operations, &TxEntry{
Bucket: bucket,
Key: key,
Value: value,
Cmd: Set,
})
}
// Del adds a new delete query to the transaction.
func (tx *Tx) Del(bucket, key []byte) {
tx.Operations = append(tx.Operations, &TxEntry{
Bucket: bucket,
Key: key,
Cmd: Delete,
})
}
// Cas adds a new compare-and-swap query to the transaction.
func (tx *Tx) Cas(bucket, key, value []byte) {
tx.Operations = append(tx.Operations, &TxEntry{
Bucket: bucket,
Key: key,
Value: value,
Cmd: CmpAndSwap,
})
}
// Cmp adds a new compare-or-rollback query to the transaction.
func (tx *Tx) Cmp(bucket, key, value []byte) {
tx.Operations = append(tx.Operations, &TxEntry{
Bucket: bucket,
Key: key,
Value: value,
Cmd: CmpOrRollback,
})
}
// TxEntry is the base elements for the transactions, a TxEntry is a read or
// write operation on the database.
type TxEntry struct {
Bucket []byte
Key []byte
Value []byte
CmpValue []byte
// Where the result of Get or CmpAndSwap txns is stored.
Result []byte
Cmd TxCmd
Swapped bool
}
// Entry is the return value for list commands.
type Entry struct {
Bucket []byte
Key []byte
Value []byte
}
|