ipld-eth-server/vendor/github.com/ipfs/go-ds-leveldb/datastore.go
Elizabeth Engelman 36533f7c3f Update vendor directory and make necessary code changes
Fixes for new geth version
2019-09-25 16:32:27 -05:00

240 lines
5.3 KiB
Go

package leveldb
import (
"os"
"path/filepath"
ds "github.com/ipfs/go-datastore"
dsq "github.com/ipfs/go-datastore/query"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/syndtr/goleveldb/leveldb/util"
)
type Datastore struct {
*accessor
DB *leveldb.DB
path string
}
var _ ds.Datastore = (*Datastore)(nil)
var _ ds.TxnDatastore = (*Datastore)(nil)
// Options is an alias of syndtr/goleveldb/opt.Options which might be extended
// in the future.
type Options opt.Options
// NewDatastore returns a new datastore backed by leveldb
//
// for path == "", an in memory bachend will be chosen
func NewDatastore(path string, opts *Options) (*Datastore, error) {
var nopts opt.Options
if opts != nil {
nopts = opt.Options(*opts)
}
var err error
var db *leveldb.DB
if path == "" {
db, err = leveldb.Open(storage.NewMemStorage(), &nopts)
} else {
db, err = leveldb.OpenFile(path, &nopts)
if errors.IsCorrupted(err) && !nopts.GetReadOnly() {
db, err = leveldb.RecoverFile(path, &nopts)
}
}
if err != nil {
return nil, err
}
return &Datastore{
accessor: &accessor{ldb: db},
DB: db,
path: path,
}, nil
}
// An extraction of the common interface between LevelDB Transactions and the DB itself.
//
// It allows to plug in either inside the `accessor`.
type levelDbOps interface {
Put(key, value []byte, wo *opt.WriteOptions) error
Get(key []byte, ro *opt.ReadOptions) (value []byte, err error)
Has(key []byte, ro *opt.ReadOptions) (ret bool, err error)
Delete(key []byte, wo *opt.WriteOptions) error
NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator
}
// Datastore operations using either the DB or a transaction as the backend.
type accessor struct {
ldb levelDbOps
}
func (a *accessor) Put(key ds.Key, value []byte) (err error) {
return a.ldb.Put(key.Bytes(), value, nil)
}
func (a *accessor) Get(key ds.Key) (value []byte, err error) {
val, err := a.ldb.Get(key.Bytes(), nil)
if err != nil {
if err == leveldb.ErrNotFound {
return nil, ds.ErrNotFound
}
return nil, err
}
return val, nil
}
func (a *accessor) Has(key ds.Key) (exists bool, err error) {
return a.ldb.Has(key.Bytes(), nil)
}
func (d *accessor) GetSize(key ds.Key) (size int, err error) {
return ds.GetBackedSize(d, key)
}
func (a *accessor) Delete(key ds.Key) (err error) {
// leveldb Delete will not return an error if the key doesn't
// exist (see https://github.com/syndtr/goleveldb/issues/109),
// so check that the key exists first and if not return an
// error
exists, err := a.ldb.Has(key.Bytes(), nil)
if !exists {
return ds.ErrNotFound
} else if err != nil {
return err
}
return a.ldb.Delete(key.Bytes(), nil)
}
func (a *accessor) Query(q dsq.Query) (dsq.Results, error) {
var rnge *util.Range
// make a copy of the query for the fallback naive query implementation.
// don't modify the original so res.Query() returns the correct results.
qNaive := q
if q.Prefix != "" {
rnge = util.BytesPrefix([]byte(q.Prefix))
qNaive.Prefix = ""
}
i := a.ldb.NewIterator(rnge, nil)
next := i.Next
if len(q.Orders) > 0 {
switch q.Orders[0].(type) {
case dsq.OrderByKey, *dsq.OrderByKey:
qNaive.Orders = nil
case dsq.OrderByKeyDescending, *dsq.OrderByKeyDescending:
next = func() bool {
next = i.Prev
return i.Last()
}
qNaive.Orders = nil
default:
}
}
r := dsq.ResultsFromIterator(q, dsq.Iterator{
Next: func() (dsq.Result, bool) {
if !next() {
return dsq.Result{}, false
}
k := string(i.Key())
e := dsq.Entry{Key: k}
if !q.KeysOnly {
buf := make([]byte, len(i.Value()))
copy(buf, i.Value())
e.Value = buf
}
return dsq.Result{Entry: e}, true
},
Close: func() error {
i.Release()
return nil
},
})
return dsq.NaiveQueryApply(qNaive, r), nil
}
// DiskUsage returns the current disk size used by this levelDB.
// For in-mem datastores, it will return 0.
func (d *Datastore) DiskUsage() (uint64, error) {
if d.path == "" { // in-mem
return 0, nil
}
var du uint64
err := filepath.Walk(d.path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
du += uint64(info.Size())
return nil
})
if err != nil {
return 0, err
}
return du, nil
}
// LevelDB needs to be closed.
func (d *Datastore) Close() (err error) {
return d.DB.Close()
}
type leveldbBatch struct {
b *leveldb.Batch
db *leveldb.DB
}
func (d *Datastore) Batch() (ds.Batch, error) {
return &leveldbBatch{
b: new(leveldb.Batch),
db: d.DB,
}, nil
}
func (b *leveldbBatch) Put(key ds.Key, value []byte) error {
b.b.Put(key.Bytes(), value)
return nil
}
func (b *leveldbBatch) Commit() error {
return b.db.Write(b.b, nil)
}
func (b *leveldbBatch) Delete(key ds.Key) error {
b.b.Delete(key.Bytes())
return nil
}
// A leveldb transaction embedding the accessor backed by the transaction.
type transaction struct {
*accessor
tx *leveldb.Transaction
}
func (t *transaction) Commit() error {
return t.tx.Commit()
}
func (t *transaction) Discard() {
t.tx.Discard()
}
func (d *Datastore) NewTransaction(readOnly bool) (ds.Txn, error) {
tx, err := d.DB.OpenTransaction()
if err != nil {
return nil, err
}
accessor := &accessor{tx}
return &transaction{accessor, tx}, nil
}