bolt: support editing
This commit is contained in:
parent
8af27f8834
commit
f78eec1872
@ -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 {
|
||||
|
52
db_bolt.go
52
db_bolt.go
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
20
main.go
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user