bolt: support editing

This commit is contained in:
mappu 2024-07-06 11:45:41 +12:00
parent 8af27f8834
commit f78eec1872
9 changed files with 103 additions and 1 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"errors"
"fmt"
"path/filepath"
"unsafe"
@ -83,6 +84,10 @@ func (ld *badgerLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
f.contentBox.SetEnabled(true)
}
func (n *badgerLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
return errors.New("Editing is not supported")
}
func (ld *badgerLoadedDatabase) NavChildren(ndata *navData) ([]string, error) {
// In the Badger implementation, there is only one child: "Data"
if len(ndata.bucketPath) == 0 {

View File

@ -1,6 +1,7 @@
package main
import (
"errors"
"fmt"
"path/filepath"
"sort"
@ -80,6 +81,57 @@ func (ld *boltLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
f.contentBox.SetEnabled(true)
}
func (n *boltLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
if n.db.IsReadOnly() {
return errors.New("Database was opened read-only")
}
// We have rendered row IDs, need to convert back to a bolt primary key
// TODO stash the real key inside f.contentBox.Objects()
// FIXME breaks if you try and edit the primary key(!)
primaryKeyForRendered := func(rowid int32) []byte {
return []byte(f.contentBox.Cells(0, rowid))
}
return n.db.Update(func(tx *bbolt.Tx) error {
// Get current bucket handle
b := boltTargetBucket(tx, ndata.bucketPath)
// Edit
for rowid, _ /*editcells*/ := range f.updateRows {
k := primaryKeyForRendered(rowid)
v := f.contentBox.Cells(1, rowid) // There's only one value cell
err := b.Put(k, []byte(v))
if err != nil {
return fmt.Errorf("Updating cell %q: %w", formatUtf8(k), err)
}
}
// Delete by key (affects rowids after re-render)
for rowid, _ := range f.deleteRows {
k := primaryKeyForRendered(rowid)
err := b.Delete(k)
if err != nil {
return fmt.Errorf("Deleting cell %q: %w", formatUtf8(k), err)
}
}
// Insert all new entries
for rowid, _ := range f.insertRows {
k := primaryKeyForRendered(rowid)
v := f.contentBox.Cells(1, rowid) // There's only one value cell
err := b.Put(k, []byte(v))
if err != nil {
return fmt.Errorf("Inserting cell %q: %w", formatUtf8(k), err)
}
}
// Done
return nil
})
}
func (ld *boltLoadedDatabase) NavChildren(ndata *navData) ([]string, error) {
// In the bolt implementation, the nav is a recursive tree of child buckets
return boltChildBucketNames(ld.db, ndata.bucketPath)

View File

@ -1,6 +1,7 @@
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
@ -69,6 +70,10 @@ func (ld *debconfLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
f.contentBox.SetEnabled(true)
}
func (n *debconfLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
return errors.New("Editing is not supported")
}
func (ld *debconfLoadedDatabase) NavChildren(ndata *navData) ([]string, error) {
// In the debconf implementation, there is only one child: "Data"
if len(ndata.bucketPath) == 0 {

View File

@ -1,6 +1,8 @@
package main
import (
"errors"
"github.com/ying32/govcl/vcl"
)
@ -22,6 +24,10 @@ func (n *noLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
f.propertiesBox.SetText("Open a database to get started...")
}
func (n *noLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
return errors.New("Editing is not supported")
}
func (n *noLoadedDatabase) ExecQuery(query string, resultArea *vcl.TStringGrid) {
}

View File

@ -2,6 +2,7 @@ package main
import (
"context"
"errors"
"fmt"
"path/filepath"
"unsafe"
@ -74,6 +75,10 @@ func (ld *pebbleLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
f.contentBox.SetEnabled(true)
}
func (n *pebbleLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
return errors.New("Editing is not supported")
}
func (ld *pebbleLoadedDatabase) NavChildren(ndata *navData) ([]string, error) {
// In the pebble implementation, there is only one child: "Data"
if len(ndata.bucketPath) == 0 {

View File

@ -2,6 +2,7 @@ package main
import (
"context"
"errors"
"fmt"
"strconv"
"unsafe"
@ -137,6 +138,10 @@ func (ld *redisLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
}
}
func (n *redisLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
return errors.New("Editing is not supported")
}
func (ld *redisLoadedDatabase) NavChildren(ndata *navData) ([]string, error) {
// ctx := context.Background()

View File

@ -2,6 +2,7 @@ package main
import (
"database/sql"
"errors"
"fmt"
"path/filepath"
"unsafe"
@ -96,6 +97,10 @@ func (ld *sqliteLoadedDatabase) RenderForNav(f *TMainForm, ndata *navData) {
}
func (n *sqliteLoadedDatabase) ApplyChanges(f *TMainForm, ndata *navData) error {
return errors.New("Editing is not supported")
}
func (ld *sqliteLoadedDatabase) sqliteGetColumnNamesForTable(tableName string) ([]string, error) {
colr, err := ld.db.Query(`SELECT name FROM pragma_table_info( ? )`, tableName)
if err != nil {

View File

@ -19,6 +19,7 @@ type loadedDatabase interface {
DriverName() string
RootElement() *vcl.TTreeNode
RenderForNav(f *TMainForm, ndata *navData)
ApplyChanges(f *TMainForm, ndata *navData) error
ExecQuery(query string, resultArea *vcl.TStringGrid)
NavChildren(ndata *navData) ([]string, error)
NavContext(ndata *navData) ([]contextAction, error)

20
main.go
View File

@ -647,7 +647,25 @@ func (f *TMainForm) OnDataCommitClick(sender vcl.IObject) {
return // Not an active data view
}
// TODO
node := f.Buckets.Selected()
if node == nil {
vcl.ShowMessage("No database selected")
return
}
scrollPos := f.contentBox.TopRow()
ndata := (*navData)(node.Data())
err := ndata.ld.ApplyChanges(f, ndata)
if err != nil {
vcl.ShowMessage(err.Error())
}
// Refresh content
f.OnNavChange(f.Buckets, node) // Refresh RHS pane/data content
// Preserve scroll position
f.contentBox.SetTopRow(scrollPos)
}
func (f *TMainForm) OnNavContextClose(sender vcl.IObject) {