36533f7c3f
Fixes for new geth version
240 lines
5.3 KiB
Go
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
|
|
}
|