diff --git a/bolt.go b/bolt.go index 59d93b6..4a28d9f 100644 --- a/bolt.go +++ b/bolt.go @@ -94,6 +94,10 @@ func (ld *boltLoadedDatabase) NavChildren(ndata *navData) ([]string, error) { return boltChildBucketNames(ld.db, ndata.bucketPath) } +func (ld *boltLoadedDatabase) ExecQuery(query string, resultArea *vcl.TListView) { + vcl.ShowMessage("Bolt doesn't support querying") +} + var _ loadedDatabase = &boltLoadedDatabase{} // interface assertion // diff --git a/loadedDatabase.go b/loadedDatabase.go index 2a647ba..49d5027 100644 --- a/loadedDatabase.go +++ b/loadedDatabase.go @@ -10,6 +10,7 @@ type loadedDatabase interface { DriverName() string RootElement() *vcl.TTreeNode RenderForNav(f *TMainForm, ndata *navData) + ExecQuery(query string, resultArea *vcl.TListView) NavChildren(ndata *navData) ([]string, error) Keepalive(ndata *navData) } diff --git a/main.go b/main.go index 7414b90..fa8c4a2 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( const ( MY_SPACING = 6 + MY_HEIGHT = 90 MY_WIDTH = 180 ) @@ -25,6 +26,8 @@ type TMainForm struct { Tabs *vcl.TPageControl propertiesBox *vcl.TMemo contentBox *vcl.TListView + queryInput *vcl.TMemo + queryResult *vcl.TListView dbs []loadedDatabase } @@ -132,6 +135,43 @@ func (f *TMainForm) OnFormCreate(sender vcl.IObject) { f.contentBox.SetAutoWidthLastColumn(true) f.contentBox.SetReadOnly(true) f.contentBox.Columns().Clear() + + queryTab := vcl.NewTabSheet(f.Tabs) + queryTab.SetParent(f.Tabs) + queryTab.SetCaption("Query") + queryTab.SetImageIndex(imgLightning) + + queryExecBtn := vcl.NewButton(queryTab) + queryExecBtn.SetParent(queryTab) + queryExecBtn.SetCaption("Execute") + queryExecBtn.SetAlign(types.AlTop) + queryExecBtn.SetOnClick(f.OnQueryExecute) + + f.queryInput = vcl.NewMemo(queryTab) + f.queryInput.SetParent(queryTab) + f.queryInput.SetHeight(MY_HEIGHT) + f.queryInput.SetAlign(types.AlTop) + f.queryInput.SetTop(1) + f.queryInput.Font().SetName("monospace") + f.queryInput.BorderSpacing().SetLeft(MY_SPACING) + f.queryInput.BorderSpacing().SetTop(MY_SPACING) + f.queryInput.BorderSpacing().SetRight(MY_SPACING) + + vsplit := vcl.NewSplitter(queryTab) + vsplit.SetParent(queryTab) + vsplit.SetAlign(types.AlTop) + vsplit.SetTop(2) + + f.queryResult = vcl.NewListView(queryTab) + f.queryResult.SetParent(queryTab) + f.queryResult.SetAlign(types.AlClient) // fill remaining space + f.queryResult.SetViewStyle(types.VsReport) // "Report style" i.e. has columns + f.queryResult.SetAutoWidthLastColumn(true) + f.queryResult.SetReadOnly(true) + f.queryResult.Columns().Clear() + f.queryResult.BorderSpacing().SetLeft(MY_SPACING) + f.queryResult.BorderSpacing().SetRight(MY_SPACING) + f.queryResult.BorderSpacing().SetBottom(MY_SPACING) } func (f *TMainForm) OnMnuFileOpenClick(sender vcl.IObject) { @@ -162,6 +202,18 @@ func (f *TMainForm) OnMnuFileExitClick(sender vcl.IObject) { f.Close() } +func (f *TMainForm) OnQueryExecute(sender vcl.IObject) { + // Execute + node := f.Buckets.Selected() + if node == nil { + vcl.ShowMessage("No database selected") + return + } + + ndata := (*navData)(node.Data()) + ndata.ld.ExecQuery(f.queryInput.Text(), f.queryResult) +} + func (f *TMainForm) OnNavChange(sender vcl.IObject, node *vcl.TTreeNode) { if node.Data() == nil { diff --git a/sqlite.go b/sqlite.go index cbaf656..a3d1f47 100644 --- a/sqlite.go +++ b/sqlite.go @@ -173,6 +173,29 @@ func populateRows(rr *sql.Rows, dest *vcl.TListView) { } } +func (ld *sqliteLoadedDatabase) ExecQuery(query string, resultArea *vcl.TListView) { + rr, err := ld.db.Query(query) + if err != nil { + vcl.ShowMessage(err.Error()) + return + } + + defer rr.Close() + + resultArea.SetEnabled(false) + resultArea.Clear() + + columns, err := rr.Columns() + if err != nil { + vcl.ShowMessage(err.Error()) + return + } + + populateColumns(columns, resultArea) + + populateRows(rr, resultArea) + + resultArea.SetEnabled(true) } func (ld *sqliteLoadedDatabase) NavChildren(ndata *navData) ([]string, error) {