yvbolt/db_badger.go

152 lines
3.4 KiB
Go

package main
import (
"fmt"
"path/filepath"
"unsafe"
"github.com/dgraph-io/badger/v4"
"github.com/ying32/govcl/vcl"
)
type badgerLoadedDatabase struct {
displayName string
db *badger.DB
nav *vcl.TTreeNode
arena []*navData // keepalive
}
func (ld *badgerLoadedDatabase) DisplayName() string {
return ld.displayName
}
func (ld *badgerLoadedDatabase) DriverName() string {
return "Badger v4"
}
func (ld *badgerLoadedDatabase) RootElement() *vcl.TTreeNode {
return ld.nav
}
func (ld *badgerLoadedDatabase) Keepalive(ndata *navData) {
ld.arena = append(ld.arena, ndata)
}
func (ld *badgerLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) error {
// Load properties
content := fmt.Sprintf("Table statistics: %#v", ld.db.Tables())
f.propertiesBox.SetText(content)
// Load data
// Badger always uses Key + Value as the columns
colKey := f.contentBox.Columns().Add()
colKey.Title().SetCaption("Key")
colVal := f.contentBox.Columns().Add()
colVal.Title().SetCaption("Value")
err := ld.db.View(func(txn *badger.Txn) error {
// Create iterator
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 64
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
k := item.Key()
err := item.Value(func(v []byte) error {
rpos := f.contentBox.RowCount()
f.contentBox.SetRowCount(rpos + 1)
f.contentBox.SetCells(0, rpos, formatUtf8(k))
f.contentBox.SetCells(1, rpos, formatUtf8(v))
return nil
})
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
// Valid
vcl_stringgrid_columnwidths(f.contentBox)
f.contentBox.SetEnabled(true)
return nil
}
func (ld *badgerLoadedDatabase) NavChildren(ndata *navData) ([]string, error) {
// In the Badger implementation, there is only one child: "Data"
if len(ndata.bucketPath) == 0 {
return []string{"Data"}, nil
} else {
// No children deeper than that
return []string{}, nil
}
}
func (ld *badgerLoadedDatabase) NavContext(ndata *navData) ([]contextAction, error) {
return nil, nil // No special actions are supported
}
func (ld *badgerLoadedDatabase) Close() {
_ = ld.db.Close()
ld.arena = nil
}
var _ loadedDatabase = &badgerLoadedDatabase{} // interface assertion
//
func (f *TMainForm) badgerAddDatabaseFromMemory() {
f.badgerAddDatabaseFrom(badger.DefaultOptions("").WithInMemory(true))
}
func (f *TMainForm) badgerAddDatabaseFromDirectory(path string) {
f.badgerAddDatabaseFrom(badger.DefaultOptions(path))
}
func (f *TMainForm) badgerAddDatabaseFrom(opts badger.Options) {
// TODO load in background thread to stop blocking the UI
db, err := badger.Open(opts)
if err != nil {
vcl.ShowMessage(fmt.Sprintf("Failed to load database: %s", err.Error()))
return
}
ld := &badgerLoadedDatabase{
db: db,
}
if opts.Dir == "" {
ld.displayName = ":memory:" // SQLite-style naming
} else {
ld.displayName = filepath.Base(opts.Dir)
}
ld.nav = f.Buckets.Items().Add(nil, ld.displayName)
ld.nav.SetHasChildren(true) // dynamically populate in OnNavExpanding
ld.nav.SetImageIndex(imgDatabase)
ld.nav.SetSelectedIndex(imgDatabase)
navData := &navData{
ld: ld,
childrenLoaded: false, // will be loaded dynamically
bucketPath: []string{}, // empty = root
}
ld.nav.SetData(unsafe.Pointer(navData))
f.dbs = append(f.dbs, ld)
f.Buckets.SetSelected(ld.nav) // Select new element
ld.Keepalive(navData)
}