128 Commits

Author SHA1 Message Date
13fedfa2f6 doc: update README and TODO 2025-12-22 19:48:36 +13:00
8b06ccef48 buntdb: add support, including editing+shrink 2025-12-22 18:43:48 +13:00
17d6b5172d ui: use compress icon for bolt-zip tool 2025-12-22 18:23:13 +13:00
708e8072ff sqlite: support ssh connections 2025-12-22 18:17:41 +13:00
c8125d2c84 sqliteclidriver: add ssh backend 2025-12-22 18:17:35 +13:00
a5138a51f3 sqliteclidriver: refactor cmd handling to support other implementations 2025-12-22 18:17:29 +13:00
02b5a8fd48 sqliteclidriver: add more comments, expose DriverName const 2025-12-22 18:17:06 +13:00
b2118c9196 config: add omitempty for many db types 2025-12-22 18:16:35 +13:00
5aa76a465c sshtunnel: add icons for connection modes, add omitempty 2025-12-22 18:16:11 +13:00
f3e729b023 assets: add compress, key, page_key icons 2025-12-22 18:15:55 +13:00
5c7a99d16b makefile: autogenerate embed.qrc from asset files 2025-12-22 18:15:49 +13:00
4d5ca19b47 doc/changelog: changelog for v2.0.0 2025-12-20 19:19:46 +13:00
a0e70636a1 doc/LICENSE: merge qbolt/yvbolt license headers 2025-12-20 19:19:46 +13:00
13e139e023 app: show version in main window titlebar 2025-12-20 19:19:46 +13:00
1f7d03e67a doc: update screenshots 2025-12-20 19:19:46 +13:00
0b1f662e99 doc/README: add other features from qbolt's readme 2025-12-20 19:19:46 +13:00
e5f36b0f66 doc/README: remove yvbolt/qbolt cross reference link 2025-12-20 19:19:46 +13:00
0eccb12744 doc/README: add direct links to changelog/downloads 2025-12-20 19:19:46 +13:00
f2d3240153 git: merge yvbolt/qbolt changelogs as separate file 2025-12-20 19:19:46 +13:00
9fb4302000 git: merge all yvbolt/qbolt names 2025-12-20 19:19:40 +13:00
e3f94f1eba git: synthetic merge of qbolt and yvbolt branches 2025-12-20 18:32:58 +13:00
4e13d8dffd doc/README: changelog for v1.1.0 2025-05-04 14:45:50 +12:00
20e5efa711 makefile: set version to 1.1.0 2025-05-04 14:45:42 +12:00
6378740051 bump bbolt to v1.4.0 2025-05-04 14:43:03 +12:00
b8ce7a667b makefile: use miqt-docker for windows build 2025-05-04 14:25:22 +12:00
281ca18d90 uic: update to latest miqt-uic 2025-05-04 14:23:14 +12:00
1725de6ace import/export: use background thread 2025-04-18 12:13:56 +12:00
0ad7c03db0 makefile: add workaround for nested menu generation 2025-04-17 21:53:04 +12:00
1cdd0d113b support drag+drop database files in 2025-04-17 21:48:52 +12:00
a0fae43690 makefile/uic: work around miqt issue with SetObjectName 2025-04-17 21:27:06 +12:00
04ac766125 windows build: update build command for latest miqt 2025-04-17 21:26:51 +12:00
9ac26467c0 add zip roundtrip conversion feature 2025-04-17 21:26:27 +12:00
cbfc038839 add f1/f5 keyboard shortcuts for help/refresh 2025-04-17 21:25:38 +12:00
7ae6462da0 qbolt: do more work in []byte space instead of strings 2025-04-17 21:25:02 +12:00
09a3e5b90f main: force highdpi attributes (helps on windows) 2025-04-16 18:37:39 +12:00
1cddd17017 make(linux): smaller output binary 2025-04-16 18:33:42 +12:00
8fdc3a0428 makefile: allow overriding $GO 2025-04-16 18:17:52 +12:00
18f10fc1b4 update to miqt 0.10, update to qt 6 2025-04-16 18:17:44 +12:00
18331ae007 doc/README: updates for v1.0.3 2024-10-05 17:44:27 +13:00
f08242e93c Merge branch 'qbolt-go' 2024-10-05 17:42:14 +13:00
23965230e2 make: add windows resources, improve output compression 2024-10-05 17:41:38 +13:00
e43b261752 build: upgrade makefile/dockerfile for miqt and win64 support 2024-10-05 16:37:23 +13:00
f9b4cb71a5 qbolt: show makefile-based version number in help dialog 2024-10-05 16:37:15 +13:00
96c05641bd mod: upgrade bbolt v1.3.5 -> v1.3.11 2024-10-05 16:36:41 +13:00
2c5d1946ec qbolt: fixes post-port 2024-10-05 15:58:56 +13:00
a6cbc5a9ed qbolt: initial port to go/miqt 2024-10-03 19:34:28 +13:00
6008ae44a2 doc: rename image directory for teafolio 2021-04-12 18:03:05 +12:00
99096b2360 doc/README: add v1.0.2 changelog 2021-04-12 18:01:46 +12:00
0e92459779 no-op merge with legacy-codesite branch 2021-04-12 17:52:00 +12:00
a13eaaf523 make: upx doesn't support current mxe mingw/qt linked binaries 2021-04-12 17:44:38 +12:00
dca8d277d3 make: option to build windows binary in docker 2021-04-12 17:44:17 +12:00
15c739c0b9 squash 2021-04-12 17:43:59 +12:00
fc2da972ef dummy-data: fix build for renamed bbolt package 2021-04-12 17:07:16 +12:00
e45eea7111 makefile: remove 'hg export' function 2021-04-12 17:04:40 +12:00
1e62d79c07 doc: delete TODO.txt (issues now being tracked in Gitea)
The new issue tracker is at https://git.ivysaur.me/code.ivysaur.me/qbolt/issues
2021-04-12 17:04:27 +12:00
c74c5ae5c0 doc: move README/TODO to top-level directory 2021-04-12 17:00:49 +12:00
b0092c4a6e vendor: switch bolt implementation to etcd-io/bbolt v1.3.5 2021-04-12 17:00:40 +12:00
5ce6368c4a qt: fix missing -lpthread on Debian Bullseye 2021-04-12 16:54:06 +12:00
3e7e54da4b go: add go.mod file for bolt dependency 2021-04-12 16:53:57 +12:00
9f80d687b2 hg2git: replace hgignore/hgtags files 2021-04-12 16:53:43 +12:00
96411e877f bump all versions to 1.0.2 2017-06-19 21:04:40 +12:00
ac7e078c02 Added tag release-1.0.1 for changeset 0528a0ab20b6 2017-06-19 21:04:23 +12:00
e13314f5dc 1.0.1 meta 2017-06-19 21:04:17 +12:00
b50c3e738a doc: update features list in readme 2017-06-19 21:02:42 +12:00
54ad6015b7 more binary correctness 2017-06-19 20:53:50 +12:00
40e84ac230 dummy-data: change to generate binary names 2017-06-19 20:45:26 +12:00
767eaa0a47 add fallback display for non-printable characters 2017-06-19 20:42:51 +12:00
bb594e768c doc: update readme 2017-06-19 20:42:42 +12:00
516bd99c4d doc: update TODO 2017-06-19 20:28:33 +12:00
571bfcf4b6 one more preservation for previous 2017-06-19 20:27:52 +12:00
26f7a11d80 preserve the binary content of keys and bucket names during edit operations 2017-06-19 20:27:05 +12:00
6fb8d1ba0c commit all archived files 2017-06-19 00:00:00 +00:00
21588021d3 doc: update TODO 2017-05-25 20:00:10 +12:00
7441e0c15b option to open database as read-only 2017-05-25 19:59:53 +12:00
142f3f6bf4 track screenshot of qbolt on windows 2017-05-25 19:54:35 +12:00
19ddb1c956 select parent when deleting bucket (maybe fixes a crash?) 2017-05-25 19:53:35 +12:00
97467eae4d add icon for win32 binary 2017-05-25 19:50:11 +12:00
17c37b6568 bump dist version to 1.0.1 2017-05-21 18:23:41 +12:00
57237ef2e8 Added tag release-1.0.0 for changeset 74cacbbe8f6c 2017-05-21 18:23:29 +12:00
164f5a071d doc: update README 2017-05-21 18:22:23 +12:00
ac46b16d0e hgignore: simplify 2017-05-21 18:19:47 +12:00
b9d114a2d8 remove extra .db file from dist archives 2017-05-21 18:18:45 +12:00
b368457247 add some screenshots 2017-05-21 18:18:14 +12:00
fb940d5f60 doc: TODO.txt 2017-05-21 18:18:10 +12:00
6d1e671e44 doc: README 2017-05-21 18:18:04 +12:00
b5149c4efa bolt: strict 10 second timeout for opening databases 2017-05-21 18:05:20 +12:00
f0f642b6b0 add many more icons 2017-05-21 18:05:11 +12:00
7c62abb352 working add items 2017-05-21 17:59:27 +12:00
4f1c48b55d working edit/delete items 2017-05-21 17:53:40 +12:00
37f9307db1 wip set/delete items 2017-05-21 17:44:07 +12:00
14e7ebb59c go: simplify out a wrapper function 2017-05-21 17:26:09 +12:00
14510545d4 fix right-hand default pane when a bucket is selected 2017-05-21 17:14:32 +12:00
906cff2e57 working bucket deletions 2017-05-21 17:14:20 +12:00
bf28495b1f add our application icon to system popups 2017-05-21 17:12:02 +12:00
98b78ab1e6 option to create new buckets / sub-buckets 2017-05-21 17:08:15 +12:00
4394c8a50a option to create new databases 2017-05-21 16:50:17 +12:00
9fb5bdad78 always open databases read/write 2017-05-21 16:47:28 +12:00
d2ec12798e popup editor to view record content 2017-05-21 16:44:44 +12:00
5270dd00bc display data keys, data item length 2017-05-21 16:15:49 +12:00
35f28fa5ed makefile: fix qbolt.a target 2017-05-21 15:59:00 +12:00
5ea969e10c pretty-print json display 2017-05-21 15:49:47 +12:00
d0becd0c3c clean up qt project, always refer to makefile-produced qbolt.a files 2017-05-21 15:00:53 +12:00
1c81444645 makefile: distribution targets 2017-05-21 14:51:22 +12:00
d7c3bfd1f5 unify makefile, make win32 builds 2017-05-21 14:33:46 +12:00
0f1cc014d7 working 'refresh buckets' action 2017-05-21 13:49:41 +12:00
6ac8c3e67b remove dead code 2017-05-21 13:49:36 +12:00
546681a0ef working cgo slices, working nested-bucket operations 2017-05-21 13:47:46 +12:00
679d1140cc recursive bucket scan - we're crashing when passing a slice to go code 2017-05-21 13:31:28 +12:00
60d71104e8 retrieve bucket properties 2017-05-21 12:39:55 +12:00
8d8374be24 initial meta commit 2017-05-21 00:00:00 +00:00
95a1bfeea5 retrieve database properties 2017-05-21 11:59:38 +12:00
8597f270f6 context menus, disconnection, re-refresh, selection behaviour, ^O shortcut 2017-05-21 11:47:16 +12:00
d32ab82d73 don't pass go strings to c - pass char*+len, callee must call free() 2017-05-20 23:02:58 +12:00
b6d1cafe54 cgo: minor cleanups, remove unused export 2017-05-20 18:15:39 +12:00
3911d7d66c convert cgo function pointer call to repeated iteration 2017-05-20 18:01:55 +12:00
be594cb1a5 call bucket enumeration - function pointers are misbehaving 2017-05-20 17:28:27 +12:00
8a93f163c4 c++ wrappers for bucket enumeration function 2017-05-20 15:47:18 +12:00
e82d0a5dd2 add top-level db items to ui 2017-05-20 15:47:05 +12:00
4b3fb783c4 wire up other menu items, add app logo icon 2017-05-20 15:20:30 +12:00
e8030a9579 cgo: change interface to not return already-collected error interfaces 2017-05-20 15:13:16 +12:00
a94a330345 add GetMagic() check, move c/go interop into separate classes 2017-05-20 14:57:51 +12:00
8ce03cbed6 go: build .a instead of .so 2017-05-20 14:57:19 +12:00
b76fad9512 hgignore: pro.user file 2017-05-18 20:11:19 +12:00
a9158c3a0c gms: fix missing initialisation 2017-05-16 19:47:50 +12:00
dc31787526 track sample.db file 2017-05-16 19:47:41 +12:00
ff7484079e track dummy-data generator 2017-05-16 19:47:26 +12:00
6e1f9ca1e1 hgignore 2017-05-16 19:47:17 +12:00
ac56f5e6c8 initial commit 2017-05-16 19:34:54 +12:00
37 changed files with 664 additions and 333 deletions

11
.gitignore vendored
View File

@@ -1,8 +1,5 @@
testdata/ testdata/
liblcl-*.zip qbolt
liblcl.so qbolt.exe
liblcl.dll qbolt.linux64.tar.xz
yvbolt qbolt.win64.zip
yvbolt.exe
yvbolt.linux64.tar.xz
yvbolt.win64.zip

190
CHANGELOG.md Normal file
View File

@@ -0,0 +1,190 @@
# Changelog
2025-12-20 v2.0.0
- Merge yvbolt and QBolt together
- App: Show application version in main window titlebar
2025-12-20 v0.11.0 (yvbolt)
- Badger: Change encryption configuration options (**BREAKING**)
- SSH Agent: Initial support, including lock/unlock
- Badger, SQLite: Fix "all files" spec when choosing an export file path
- Bolt: Support import/export as zip archive
- Support SSH agents when using SSH tunnels (e.g. for Redis or Mongo)
2025-12-17 v0.10.1 (yvbolt)
- Fix mixed Qt 5 / Qt 6 syntax
- Build release binaries with Go1.26
- [⬇️ Download here](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/tag/v0.10.1)
2025-12-16 v0.10.0 (yvbolt)
- MongoDB: Initial support, including SSH tunnel, managing collections, traversing BSON documents, and querying
- LotusDB: Initial support, including editing
- Bitcask: Initial support, including editing and backup
- RoseDB: Initial support, including editing
- Badger: Fix k/v column display, support editing
- LMDB: Support editing, add warning for destructive actions
- LevelDB: Support editing
- Pebble: Support editing
- Starskey: Fix k/v column display, support editing
- Redis: Bump driver version v9.16.0 -> v9.17.2
- Bolt: Add warning for destructive actions
- Connection manager: When saving, use the database's preferred name
- App: Add hex viewer for binary data if it is not valid UTF-8
- App: Use virtualized table renderer
- App: Use multiple plug-in typed column stores in table backend
- App: Fix graphical flicker when editing connections on Windows
- App: Set up icon and exe properties on Windows
- App: Embed version number in release builds
- App: Show all driver versions as a virtual data table
- App: Show unsaved-changes warning also when changing database
- App: Fix rich text formatting appearing when pasting into the query window
2025-12-02 v0.9.0 (yvbolt)
- LMDB: Initial support, including multi-database mode, data editing, and managing child databases
- Starskey: Initial support
- Freedesktop.org Secret Service: Initial support, including unlocking collections and creating child collections
- App: Use global toolbar style
- App: Add connection manager, encrypting credentials with AEAD AES256-GCM using OS keychain
- App: Offer to save valid quick-connection to the connection manager
- App: Add 'About Qt' menu option
- App: Fixed file extension filter for database files with no extension
- App: Fixed background colour for Properties area on different OSes
- App: Fixed libpng warnings about greyscale image data for embedded logo images
- App: Fixed issue with non-UTF8 child database names
- Debconf: Improve navigation speed by splitting applications into virtual tables
- SSH: Redesign options to pick only one of the available auth methods
- Fixed running release binary on Debian 12
- [⬇️ Download here](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/tag/v0.9.0)
2025-11-23 v0.8.0 (yvbolt)
- Port from [liblcl](https://github.com/ying32/liblcl) to [MIQT Qt 6](https://github.com/mappu/miqt)
- Badger: Upgrade v4.2.0 -> v4.8.0
- Pebble: Upgrade v1.0.0 -> v1.1.5
- SQLite: Upgrade v1.14.22 -> v1.14.32
- Redis: Upgrade v9.5.3 -> v9.16.0
- Bolt: Upgrade v1.4.0-alpha.1 -> v1.4.3
- Bolt: Fix child buckets appearing in data area
- Badger, Pebble, Debconf: Remove redundant "Data" navigation layer
- Badger: Support encrypted databases
- Badger: Support readonly databases
- Badger: Add context-menu actions for backup, restore, and compact
- Pebble: Support readonly databases
- LevelDB: Add LevelDB database integration
- Redis: Support SSH tunnel
- SQLite: Allow editing the primary key column
- App: New style connection dialog
- App: Updated keyboard shortcuts (Ctrl+O to open new connection, F5 to refresh, F9 to execute query)
- App: Add confirmation when refreshing the data table if there are uncommitted changes
- [⬇️ Download here](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/tag/v0.8.0)
2025-05-04 1.1.0 (qbolt)
- New feature to import/export database as zip archive
- Upgrade to Qt 6
- Add keyboard shortcuts for refresh
- Improve High DPI support
- Rebuild artefacts with miqt v0.10.0, etcd-io/bbolt v1.4.0, go 1.23, Qt 6.8 (win64)
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.1.0)
2024-10-05 1.0.3 (qbolt)
- Port from hybrid Go/C++ to now using [MIQT](https://github.com/mappu/miqt)
- Switch Windows build to win64
- Rebuild artefacts with miqt v0.5.0, etcd-io/bbolt v1.3.11, go 1.19 (deb12), go 1.23 (win64)
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.3)
2024-07-18 v0.7.0 (yvbolt)
- SQLite, Bolt: Initial support for editing data (insert, per-cell update, delete)
- SQLite: Add context menu actions for compact (vacuum), export, and drop table
- App: New grid widget
- App: Add refresh button
- App: Bigger window size, use icons for toolbars, better UI colours for Windows
- App: Prevent submitting blank queries to database
- Refactor database interface and error handling
- [⬇️ Download here](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/tag/v0.7.0)
2024-06-30 v0.6.0 (yvbolt)
- Debconf: Add as supported database
- SQLite: Support table names containing special characters
- SQLite: Improvements for experimental command-line driver
- Redis: Improve connection dialog window position
- App: Cosmetic fixes for frame borders, help dialog, and Windows fonts+colours
- Build: Change compression parameters for release builds
- Build: Compile CGO with -O2 for release builds
- [⬇️ Download here](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/tag/v0.6.0)
2024-06-29 v0.5.0 (yvbolt)
- Pebble: Add as supported database
- Bolt: Support opening as readonly
- Bolt: Support creating new databases
- Bolt: Support adding/removing recursive child buckets
- SQLite: Support custom CLI driver that parses `/usr/bin/sqlite3 -json` output (experimental)
- Redis: Improve query parser to support quoted strings
- App: Support refreshing elements in nav tree
- App: Help menu option to show driver versions
- App: Add image icons for refresh and close context menu actions
- Build: Add makefile for cross-compiling release binaries
- [⬇️ Download here](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/tag/v0.5.0)
2024-06-23 v0.4.0 (yvbolt)
- Redis: Add as supported database
- Badger: Allow creating in-memory databases
- App: Allow selecting partial query text to execute
- App: Allow closing database connections from context menu
- App: Allow scrolling large content on Properties pane
- App: Preload recursive navigation
- App: Automatically switch to selected database when new connection is created
- App: Add help website link
- App: Add database logo images
2024-06-25 v0.3.0 (yvbolt)
- Badger: Add BadgerDB v4 as supported database
- SQLite: Add support for CGo-free SQLite driver under cross-compilation
- Bolt: Update Bolt to v1.4.0-alpha.1
- App: Add support for running custom queries
- App: Add status bar showing currently selected DB
- App: Fix missing icons in nav when selecting items
- App: Fix extra quotemarks when browsing string content of database
2024-06-08 v0.2.0 (yvbolt)
- SQLite: Add SQLite support (now requires CGo)
- App: Add images for menu and navigation items
2024-06-03 v0.1.0 (yvbolt)
- Initial public release
2020-04-12 1.0.2 (qbolt)
- Rebuild artefacts with etcd-io/bbolt v1.3.5, go 1.15, Qt 5.15, and new GCC versions
- Switch from hg to Git
- Use Go modules
- Add support for building Windows binary in Docker
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.2)
2017-06-19 1.0.1 (qbolt)
- Feature: Option to open database as read-only
- Fix an issue with support for bucket names and keys not surviving UTF-8 roundtrips (now binary-clean)
- Fix an issue with crashing when deleting a bucket other than the selected one
- Fix a cosmetic issue with application icon on Windows
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.1)
2017-05-21 1.0.0 (qbolt)
- Initial public release
- The project consists of two parts; a C binding (CGo) for the embeddable Bolt database engine, and a graphical interface built in C++/Qt that links to it.
- [⬇ Download here](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases/tag/v1.0.0)

View File

@@ -1,7 +1,8 @@
ISC License ISC License
Copyright 2024 mappy Copyright 2025 mappy
Copyright 2024 The yvbolt Author(s) Copyright 2025 The QBolt Author(s)
Copyright 2025 The yvbolt Author(s)
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

View File

@@ -9,6 +9,7 @@ GO_WINRES ?= ~/go/bin/go-winres
.PHONY: generate .PHONY: generate
generate: generate:
/bin/bash -c '( echo "<RCC>" ; echo " <qresource prefix=\"/\">" ; for f in assets/* ; do echo " <file>$$f</file>" ; done ; echo " </qresource>" ; echo "</RCC>" ) > embed.qrc'
$(MIQT_UIC) -InFile mainwindow.ui -OutFile mainwindow.go -Qt6 $(MIQT_UIC) -InFile mainwindow.ui -OutFile mainwindow.go -Qt6
$(MIQT_UIC) -InFile connectionDialog.ui -OutFile connectionDialog.go -Qt6 $(MIQT_UIC) -InFile connectionDialog.ui -OutFile connectionDialog.go -Qt6
$(MIQT_UIC) -InFile connectionManagerDialog.ui -OutFile connectionManagerDialog.go -Qt6 $(MIQT_UIC) -InFile connectionManagerDialog.ui -OutFile connectionManagerDialog.go -Qt6
@@ -25,39 +26,39 @@ optimize-images:
optipng -quiet -o5 assets/*.png optipng -quiet -o5 assets/*.png
make generate make generate
yvbolt: $(SOURCES) qbolt: $(SOURCES)
# Target a debian-12 baseline build # Target a debian-12 baseline build
sed -i -re 's/".+"/"$(GIT_REV)"/g' version.go sed -i -re 's/".+"/"$(GIT_REV)"/g' version.go
$(MIQT_DOCKER) linux64-go1.26-qt6.4-dynamic -minify-build $(MIQT_DOCKER) linux64-go1.26-qt6.4-dynamic -minify-build
git checkout -- version.go git checkout -- version.go
chmod 755 yvbolt chmod 755 qbolt
upx --lzma yvbolt upx --lzma qbolt
yvbolt.exe: $(SOURCES) qbolt.exe: $(SOURCES)
sed -i -re 's/".+"/"$(GIT_REV)"/g' version.go sed -i -re 's/".+"/"$(GIT_REV)"/g' version.go
$(MIQT_DOCKER) win64-cross-go1.26-qt6.5-static -windows-build --tags=windowsqtstatic $(MIQT_DOCKER) win64-cross-go1.26-qt6.5-static -windows-build --tags=windowsqtstatic
git checkout -- version.go git checkout -- version.go
$(GO_WINRES) patch --in winres.json --no-backup --product-version git-tag --file-version git-tag yvbolt.exe $(GO_WINRES) patch --in winres.json --no-backup --product-version git-tag --file-version git-tag qbolt.exe
upx --lzma yvbolt.exe upx --lzma qbolt.exe
yvbolt.apk: $(SOURCES) qbolt.apk: $(SOURCES)
$(MIQT_DOCKER) android-armv8a-go1.23-qt6.6-dynamic -android-build $(MIQT_DOCKER) android-armv8a-go1.23-qt6.6-dynamic -android-build
yvbolt.linux64.tar.xz: yvbolt qbolt.linux64.tar.xz: qbolt
rm -f yvbolt.linux64.tar.xz rm -f qbolt.linux64.tar.xz
XZ_OPT='-T0 -9' tar caf yvbolt.linux64.tar.xz --owner=0 --group=0 yvbolt XZ_OPT='-T0 -9' tar caf qbolt.linux64.tar.xz --owner=0 --group=0 qbolt
yvbolt.win64.zip: yvbolt.exe qbolt.win64.zip: qbolt.exe
rm -f yvbolt.win64.zip rm -f qbolt.win64.zip
zip -9 yvbolt.win64.zip yvbolt.exe zip -9 qbolt.win64.zip qbolt.exe
.PHONY: dist .PHONY: dist
dist: yvbolt.linux64.tar.xz yvbolt.win64.zip dist: qbolt.linux64.tar.xz qbolt.win64.zip
.PHONY: clean .PHONY: clean
clean: clean:
git checkout -- version.go git checkout -- version.go
rm -f yvbolt.exe yvbolt yvbolt.linux64.tar.xz yvbolt.win64.zip rm -f qbolt.exe qbolt qbolt.linux64.tar.xz qbolt.win64.zip
##### #####
# Test databases in Docker # Test databases in Docker

178
README.md
View File

@@ -1,4 +1,4 @@
# yvbolt # QBolt
A graphical interface for multiple databases. A graphical interface for multiple databases.
@@ -8,22 +8,23 @@ A graphical interface for multiple databases.
- Connect to multiple databases at once - Connect to multiple databases at once
- Browse table/bucket content - Browse table/bucket content
- Use context menu to perform special table/bucket actions - Use context menu to perform special table/bucket actions
- Edit content, and add/delete rows for supported databases
- View database/bucket statistics and metadata
- Run custom SQL queries - Run custom SQL queries
- Select text to run partial query - Select text to run partial query
- Safe handling for non-UTF8 key and data fields - Safe handling for non-UTF8 key and data fields
- Hex viewer for binary data - Hex viewer for binary data
- Connection Manager saves connections with AEAD AES256-GCM using OS keychain - Connection Manager saves connections with AEAD AES256-GCM using OS keychain
See also [qbolt](https://code.ivysaur.me/qbolt) for more/different functionality.
## Supported databases ## Supported databases
There are currently 15 supported databases: There are currently 16 supported databases:
Database |Read |Editing |Query |Connection options |Context menu actions Database |Read |Editing |Query |Connection options |Context menu actions
-------------|------|---------|------|--------------------|-------- -------------|------|---------|------|--------------------|--------
Badger v4 |Yes |Yes |No |Encrypted, readonly, in-memory |Backup, restore, compact Badger v4 |Yes |Yes |No |Encrypted, readonly, in-memory |Backup, restore, compact
Bitcask |Yes |Yes |No |Readonly, autorecovery |Backup Bitcask |Yes |Yes |No |Readonly, autorecovery |Backup
BuntDB |Yes |Yes |No |In-memory |Shrink
Bolt |Yes |Yes |No |Readonly |Create/delete child buckets, import/export as zip Bolt |Yes |Yes |No |Readonly |Create/delete child buckets, import/export as zip
Debconf |Yes |No |No | | Debconf |Yes |No |No | |
Freedesktop.org Secret Service |Yes |No | No | |Unlock, create new collection Freedesktop.org Secret Service |Yes |No | No | |Unlock, create new collection
@@ -34,7 +35,7 @@ MongoDB |Yes |No |Yes |SSH tunnel |Create/delete child d
Pebble |Yes |Yes |No |Readonly, in-memory | Pebble |Yes |Yes |No |Readonly, in-memory |
Redis |Yes |No |Yes |SSH tunnel, RESP v3 | Redis |Yes |No |Yes |SSH tunnel, RESP v3 |
RoseDB |Yes |Yes |No | | RoseDB |Yes |Yes |No | |
SQLite |Yes |Yes |Yes |CLI driver, in-memory |Vacuum, export SQLite |Yes |Yes |Yes |**SSH tunnel**, CLI driver, in-memory |Vacuum, export
SSH Agent |Yes |No |No |Unix/TCP |Lock, unlock SSH Agent |Yes |No |No |Unix/TCP |Lock, unlock
Starskey |Yes |Yes |No |Compression | Starskey |Yes |Yes |No |Compression |
@@ -47,167 +48,10 @@ The code in this project is licensed under the ISC license (see `LICENSE` file f
- This project includes trademarked logo images for each supported database type. - This project includes trademarked logo images for each supported database type.
- The Windows binary is released under LGPL-3+ owing to the static copy of Qt. - The Windows binary is released under LGPL-3+ owing to the static copy of Qt.
## Download
Get the latest version from [the releases page »](https://git.ivysaur.me/code.ivysaur.me/qbolt/releases)
## Changelog ## Changelog
2025-12-20 v0.11.0 See [the full change history »](https://git.ivysaur.me/code.ivysaur.me/qbolt/src/branch/master/CHANGELOG.md)
- Badger: Change encryption configuration options (**BREAKING**)
- SSH Agent: Initial support, including lock/unlock
- Badger, SQLite: Fix "all files" spec when choosing an export file path
- Bolt: Support import/export as zip archive
- Support SSH agents when using SSH tunnels (e.g. for Redis or Mongo)
2025-12-17 v0.10.1
- Fix mixed Qt 5 / Qt 6 syntax
- Build release binaries with Go1.26
[⬇️ Download for Windows x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.10.1/yvbolt.win64.zip)
[⬇️ Download for Linux x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.10.1/yvbolt.linux64.tar.xz)
2025-12-16 v0.10.0
- MongoDB: Initial support, including SSH tunnel, managing collections, traversing BSON documents, and querying
- LotusDB: Initial support, including editing
- Bitcask: Initial support, including editing and backup
- RoseDB: Initial support, including editing
- Badger: Fix k/v column display, support editing
- LMDB: Support editing, add warning for destructive actions
- LevelDB: Support editing
- Pebble: Support editing
- Starskey: Fix k/v column display, support editing
- Redis: Bump driver version v9.16.0 -> v9.17.2
- Bolt: Add warning for destructive actions
- Connection manager: When saving, use the database's preferred name
- App: Add hex viewer for binary data if it is not valid UTF-8
- App: Use virtualized table renderer
- App: Use multiple plug-in typed column stores in table backend
- App: Fix graphical flicker when editing connections on Windows
- App: Set up icon and exe properties on Windows
- App: Embed version number in release builds
- App: Show all driver versions as a virtual data table
- App: Show unsaved-changes warning also when changing database
- App: Fix rich text formatting appearing when pasting into the query window
2025-12-02 v0.9.0
- LMDB: Initial support, including multi-database mode, data editing, and managing child databases
- Starskey: Initial support
- Freedesktop.org Secret Service: Initial support, including unlocking collections and creating child collections
- App: Use global toolbar style
- App: Add connection manager, encrypting credentials with AEAD AES256-GCM using OS keychain
- App: Offer to save valid quick-connection to the connection manager
- App: Add 'About Qt' menu option
- App: Fixed file extension filter for database files with no extension
- App: Fixed background colour for Properties area on different OSes
- App: Fixed libpng warnings about greyscale image data for embedded logo images
- App: Fixed issue with non-UTF8 child database names
- Debconf: Improve navigation speed by splitting applications into virtual tables
- SSH: Redesign options to pick only one of the available auth methods
- Fixed running release binary on Debian 12
[⬇️ Download for Windows x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.9.0/yvbolt.win64.zip)
[⬇️ Download for Linux x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.9.0/yvbolt.linux64.tar.xz)
2025-11-23 v0.8.0
- Port from [liblcl](https://github.com/ying32/liblcl) to [MIQT Qt 6](https://github.com/mappu/miqt)
- Badger: Upgrade v4.2.0 -> v4.8.0
- Pebble: Upgrade v1.0.0 -> v1.1.5
- SQLite: Upgrade v1.14.22 -> v1.14.32
- Redis: Upgrade v9.5.3 -> v9.16.0
- Bolt: Upgrade v1.4.0-alpha.1 -> v1.4.3
- Bolt: Fix child buckets appearing in data area
- Badger, Pebble, Debconf: Remove redundant "Data" navigation layer
- Badger: Support encrypted databases
- Badger: Support readonly databases
- Badger: Add context-menu actions for backup, restore, and compact
- Pebble: Support readonly databases
- LevelDB: Add LevelDB database integration
- Redis: Support SSH tunnel
- SQLite: Allow editing the primary key column
- App: New style connection dialog
- App: Updated keyboard shortcuts (Ctrl+O to open new connection, F5 to refresh, F9 to execute query)
- App: Add confirmation when refreshing the data table if there are uncommitted changes
[⬇️ Download for Windows x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.8.0/yvbolt.win64.zip)
[⬇️ Download for Linux x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.8.0/yvbolt.linux64.tar.xz)
2024-07-18 v0.7.0
- SQLite, Bolt: Initial support for editing data (insert, per-cell update, delete)
- SQLite: Add context menu actions for compact (vacuum), export, and drop table
- App: New grid widget
- App: Add refresh button
- App: Bigger window size, use icons for toolbars, better UI colours for Windows
- App: Prevent submitting blank queries to database
- Refactor database interface and error handling
[⬇️ Download for Windows x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.7.0/yvbolt.win64.zip)
[⬇️ Download for Linux x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.7.0/yvbolt.linux64.tar.xz)
2024-06-30 v0.6.0
- Debconf: Add as supported database
- SQLite: Support table names containing special characters
- SQLite: Improvements for experimental command-line driver
- Redis: Improve connection dialog window position
- App: Cosmetic fixes for frame borders, help dialog, and Windows fonts+colours
- Build: Change compression parameters for release builds
- Build: Compile CGO with -O2 for release builds
[⬇️ Download for Windows x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.6.0/yvbolt.win64.zip)
[⬇️ Download for Linux x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.6.0/yvbolt.linux64.tar.xz)
2024-06-29 v0.5.0
- Pebble: Add as supported database
- Bolt: Support opening as readonly
- Bolt: Support creating new databases
- Bolt: Support adding/removing recursive child buckets
- SQLite: Support custom CLI driver that parses `/usr/bin/sqlite3 -json` output (experimental)
- Redis: Improve query parser to support quoted strings
- App: Support refreshing elements in nav tree
- App: Help menu option to show driver versions
- App: Add image icons for refresh and close context menu actions
- Build: Add makefile for cross-compiling release binaries
[⬇️ Download for Windows x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.5.0/yvbolt.win64.zip)
[⬇️ Download for Linux x86_64](https://git.ivysaur.me/code.ivysaur.me/yvbolt/releases/download/v0.5.0/yvbolt.linux64.tar.xz)
2024-06-23 v0.4.0
- Redis: Add as supported database
- Badger: Allow creating in-memory databases
- App: Allow selecting partial query text to execute
- App: Allow closing database connections from context menu
- App: Allow scrolling large content on Properties pane
- App: Preload recursive navigation
- App: Automatically switch to selected database when new connection is created
- App: Add help website link
- App: Add database logo images
2024-06-25 v0.3.0
- Badger: Add BadgerDB v4 as supported database
- SQLite: Add support for CGo-free SQLite driver under cross-compilation
- Bolt: Update Bolt to v1.4.0-alpha.1
- App: Add support for running custom queries
- App: Add status bar showing currently selected DB
- App: Fix missing icons in nav when selecting items
- App: Fix extra quotemarks when browsing string content of database
2024-06-08 v0.2.0
- SQLite: Add SQLite support (now requires CGo)
- App: Add images for menu and navigation items
2024-06-03 v0.1.0
- Initial public release

8
TODO
View File

@@ -1,13 +1,13 @@
- BUG: Connecting to multiple DBs from connection manager: if one has an error, the error popup from the others is lost - BUG: Connecting to multiple DBs from connection manager: if one has an error, the error popup from the others is lost
- BUG: ExecQuery being called multiple times on error? - BUG: ExecQuery being called multiple times on error?
- Drag and drop database into UI (QBolt parity) - Drag and drop database into UI (QBolt parity)
- Merge with QBolt
- Portable mode (portable.txt or portable/ dir) - Portable mode (portable.txt or portable/ dir)
- Syntax highlighting in editor - Syntax highlighting in editor
- Autorefresh - Autorefresh
- Sshagent - Sshagent
- want to trigger an async refresh from inside the LDB after lock/unlock - want to trigger an async refresh from inside the LDB after lock/unlock
- support adding/removing keys (will need per-row actions) - support adding/removing keys (will need per-row actions)
- SSH: knownhost parser is stricter than openssh, does not support hostname if there is known a knownhost for the IP address
- Bolt: import/export should support passworded zips - Bolt: import/export should support passworded zips
- Table: BSON view can't see data - Table: BSON view can't see data
- Table: quick filter - Table: quick filter
@@ -67,7 +67,9 @@
- https://github.com/TerraTech/go-tokyocabinet needs pkg-config tokyocabinet - https://github.com/TerraTech/go-tokyocabinet needs pkg-config tokyocabinet
- https://github.com/estraier/tkrzw-go needs pkg-config tkrzw - https://github.com/estraier/tkrzw-go needs pkg-config tkrzw
- cdb (DJB's Constant Database) - cdb (DJB's Constant Database)
- Rust - RocksDB
- https://github.com/tecbot/gorocksdb Go bindings, need pkg-config rocksdb
- Rust
- Stoolap https://github.com/stoolap/stoolap - Stoolap https://github.com/stoolap/stoolap
- Rust, needs C binding layer https://github.com/mozilla/cbindgen - Rust, needs C binding layer https://github.com/mozilla/cbindgen
- JDBC Java databases - JDBC Java databases
@@ -75,8 +77,8 @@
- SQLite CLI driver: - SQLite CLI driver:
- Context support - Context support
- Write support - Write support
- Attach to SSH tunnel
- Configure binary path - Configure binary path
- Error handling: if an error occurs, listing db tables has problems/shows separators
- https://ricardoanderegg.com/posts/sqlite-remote-explorer-gui/ - https://ricardoanderegg.com/posts/sqlite-remote-explorer-gui/
- https://github.com/litements/litexplore - https://github.com/litements/litexplore
- Badger: - Badger:

BIN
assets/compress.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

BIN
assets/key.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

BIN
assets/page_key.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

BIN
assets/vendor_buntdb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

View File

@@ -22,6 +22,7 @@ type ConnectionConfig struct {
Badger *badgerConnection `ylabel:"BadgerDB v4" yicon:":/assets/vendor_dgraph.png" json:",omitempty"` Badger *badgerConnection `ylabel:"BadgerDB v4" yicon:":/assets/vendor_dgraph.png" json:",omitempty"`
Bitcask *bitcaskDBConnection `yicon:":/assets/vendor_riak.png" json:",omitempty"` Bitcask *bitcaskDBConnection `yicon:":/assets/vendor_riak.png" json:",omitempty"`
Bolt *boltConfig `yicon:":/assets/vendor_github.png" json:",omitempty"` Bolt *boltConfig `yicon:":/assets/vendor_github.png" json:",omitempty"`
BuntDB *buntDBConnection `ylabel:"BuntDB" yicon:":/assets/vendor_buntdb.png" json:",omitempty"`
Debconf *debconfConnection `yicon:":/assets/vendor_debian.png" json:",omitempty"` Debconf *debconfConnection `yicon:":/assets/vendor_debian.png" json:",omitempty"`
SecretService *secretServiceConnection `ylabel:"Freedesktop.org Secret Service" yicon:":/assets/vendor_freedesktop.png" json:",omitempty"` SecretService *secretServiceConnection `ylabel:"Freedesktop.org Secret Service" yicon:":/assets/vendor_freedesktop.png" json:",omitempty"`
LevelDB *leveldbConnection `ylabel:"LevelDB" yicon:":/assets/vendor_leveldb.png" json:",omitempty"` LevelDB *leveldbConnection `ylabel:"LevelDB" yicon:":/assets/vendor_leveldb.png" json:",omitempty"`

View File

@@ -159,7 +159,7 @@ func (f *App) saveConnectionManagerContents(sc *SavedConfig) error {
} }
// Force update the saved version // Force update the saved version
sc.UserAgent = APPNAME + `/` + appVersion // e.g. yvbolt/v0.0.0-devel sc.UserAgent = APPNAME + `/` + appVersion // e.g. QBolt/v0.0.0-devel
// Marshal // Marshal

View File

@@ -143,8 +143,8 @@ type badgerConnection struct {
Directory autoconfig.ExistingDirectory Directory autoconfig.ExistingDirectory
Readonly bool Readonly bool
Encryption *encryptionKey Encryption *encryptionKey
} } `json:",omitempty"`
Memory *struct{} Memory *struct{} `json:",omitempty"`
} }
type encryptionKey struct { type encryptionKey struct {

118
db_bunt.go Normal file
View File

@@ -0,0 +1,118 @@
package main
import (
"context"
"fmt"
"path/filepath"
"strings"
"github.com/mappu/autoconfig"
qt "github.com/mappu/miqt/qt6"
"github.com/tidwall/buntdb"
)
type buntLdb struct {
db *buntdb.DB
}
func (ld *buntLdb) DriverName() string {
return "BuntDB"
}
func (ld *buntLdb) Properties(bucketPath []string) (string, error) {
idxInfo, err := ld.db.Indexes()
if err != nil {
return "", err
}
return fmt.Sprintf("Indexes (%d):\n%s", len(idxInfo), "-"+strings.Join(idxInfo, "\n- ")+"\n"), nil
}
func (ld *buntLdb) RenderForNav(f *tableState, bucketPath []string) error {
// Load data
// Interestingly, BuntDB internally uses string data, not []byte data
// That would stop us from using kvstore_ApplyChanges that only works with
// []byte data, so, fake []byte casts ourselves
f.SetupColumns([]columnType{columnType_popupData, columnType_popupData}, []string{"Key", "Value"})
err := ld.db.View(func(tx *buntdb.Tx) error {
return tx.Ascend("", func(k, v string) bool {
f.AddRow_PK_Data([]byte(k), []byte(k), []byte(v))
return true
})
})
if err != nil {
return err
}
// Valid
f.Ready()
return nil
}
func (n *buntLdb) ApplyChanges(f *tableState, bucketPath []string) error {
return n.db.Update(func(tx *buntdb.Tx) error {
return kvstore_ApplyChanges(
f,
func(k, v []byte) error {
_, _, err := tx.Set(string(k), string(v), nil)
return err
},
func(k []byte) error {
_, err := tx.Delete(string(k))
return err
},
)
})
}
func (ld *buntLdb) doShrink(sender *qt.QTreeWidgetItem, bucketPath []string) error {
return ld.db.Shrink()
}
func (ld *buntLdb) NavChildren(bucketPath []string) ([]string, error) {
return []string{}, nil // No children
}
func (ld *buntLdb) NavContext(bucketPath []string) ([]contextAction, error) {
return []contextAction{
{"Shrink", ld.doShrink},
}, nil
}
func (ld *buntLdb) Close() {
_ = ld.db.Close()
}
var _ loadedDatabase = &buntLdb{} // interface assertion
var _ editableLoadedDatabase = &buntLdb{} // interface assertion
//
type buntDBConnection struct {
Type autoconfig.OneOf
Disk *struct {
File autoconfig.ExistingFile
} `json:",omitempty"`
Memory *struct{} `json:",omitempty"`
}
func (ldc *buntDBConnection) Connect(ctx context.Context) (loadedDatabase, string, error) {
var path string
if ldc.Disk != nil {
path = string(ldc.Disk.File)
} else {
path = `:memory:` // Special string known by driver
}
db, err := buntdb.Open(path)
if err != nil {
return nil, "", err
}
return &buntLdb{db: db}, filepath.Base(path), nil
}

View File

@@ -7,7 +7,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"yvbolt/debconf" "qbolt/debconf"
"github.com/mappu/autoconfig" "github.com/mappu/autoconfig"
) )

View File

@@ -238,10 +238,10 @@ var _ editableLoadedDatabase = &lmdbDatabase{} // interface assertion
type lmdbConnection struct { type lmdbConnection struct {
Storage struct { Storage struct {
Type autoconfig.OneOf Type autoconfig.OneOf
Directory *autoconfig.ExistingDirectory Directory *autoconfig.ExistingDirectory `json:",omitempty"`
File *struct { File *struct {
Path autoconfig.ExistingFile `yfilter:"LMDB database (*.mdb);;All files (*)"` Path autoconfig.ExistingFile `yfilter:"LMDB database (*.mdb);;All files (*)"`
} } `json:",omitempty"`
} }
MultiDB *struct { MultiDB *struct {
Slots int Slots int

View File

@@ -287,7 +287,7 @@ type mongoConnection struct {
Conn struct { Conn struct {
Mode autoconfig.OneOf Mode autoconfig.OneOf
Connection_String *string Connection_String *string `json:",omitempty"`
} }
SSH_Tunnel *SSHTunnel SSH_Tunnel *SSHTunnel

View File

@@ -91,8 +91,8 @@ type pebbleConnection struct {
Disk *struct { Disk *struct {
Directory autoconfig.ExistingDirectory Directory autoconfig.ExistingDirectory
Readonly bool Readonly bool
} } `json:",omitempty"`
Memory *struct{} Memory *struct{} `json:",omitempty"`
} }
func (pdc *pebbleConnection) Connect(ctx context.Context) (loadedDatabase, string, error) { func (pdc *pebbleConnection) Connect(ctx context.Context) (loadedDatabase, string, error) {

View File

@@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"yvbolt/lexer" "qbolt/lexer"
"github.com/mappu/autoconfig" "github.com/mappu/autoconfig"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"

View File

@@ -8,7 +8,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"yvbolt/sqliteclidriver" "qbolt/sqliteclidriver"
"github.com/mappu/autoconfig" "github.com/mappu/autoconfig"
qt "github.com/mappu/miqt/qt6" qt "github.com/mappu/miqt/qt6"
@@ -406,8 +406,12 @@ type sqliteConnection struct {
Disk *struct { Disk *struct {
Database autoconfig.ExistingFile `yfilter:"SQLite database (*.db *.db3 *.sqlite *.sqlite3);;All files (*)"` Database autoconfig.ExistingFile `yfilter:"SQLite database (*.db *.db3 *.sqlite *.sqlite3);;All files (*)"`
CliDriver bool `ylabel:"Use experimental CLI driver"` CliDriver bool `ylabel:"Use experimental CLI driver"`
} } `json:",omitempty"`
Memory *struct{} Memory *struct{} `json:",omitempty"`
SSH *struct {
SSHServer *SSHTunnel
Database string
} `json:",omitempty"`
} }
func (sc *sqliteConnection) Connect(ctx context.Context) (loadedDatabase, string, error) { func (sc *sqliteConnection) Connect(ctx context.Context) (loadedDatabase, string, error) {
@@ -424,12 +428,29 @@ func (sc *sqliteConnection) Connect(ctx context.Context) (loadedDatabase, string
return &sqliteLoadedDatabase{db: db}, filepath.Base(string(sc.Disk.Database)), nil return &sqliteLoadedDatabase{db: db}, filepath.Base(string(sc.Disk.Database)), nil
} else { // memory } else if sc.Memory != nil { // memory
db, err := sql.Open("sqlite3", ":memory:") db, err := sql.Open("sqlite3", ":memory:")
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
return &sqliteLoadedDatabase{db: db}, ":memory:", nil return &sqliteLoadedDatabase{db: db}, ":memory:", nil
} else if sc.SSH != nil {
if sc.SSH.SSHServer == nil {
return nil, "", errors.New("Invalid configuration")
}
cl, err := sc.SSH.SSHServer.Open(ctx)
if err != nil {
return nil, "", err
}
db := sqliteclidriver.OpenSSH(cl, sc.SSH.Database)
return &sqliteLoadedDatabase{db: db}, "SSH[" + filepath.Base(string(sc.SSH.Database)) + "]", nil
} else {
return nil, "", errors.New("Invalid configuration")
} }
} }

View File

@@ -117,8 +117,8 @@ var _ loadedDatabase = &sshAgentLdb{} // interface assertion
type sshAgentConn struct { type sshAgentConn struct {
Type autoconfig.OneOf Type autoconfig.OneOf
Unix *autoconfig.ExistingFile Unix *autoconfig.ExistingFile `json:",omitempty"`
TCP *autoconfig.AddressPort TCP *autoconfig.AddressPort `json:",omitempty"`
} }
func (c *sshAgentConn) Reset() { func (c *sshAgentConn) Reset() {

BIN
doc/screenshot-000.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,21 +1,23 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>assets/vendor_qt.png</file>
<file>assets/connect.png</file>
<file>assets/disconnect.png</file>
<file>assets/database_key.png</file>
<file>assets/help.png</file>
<file>assets/add.png</file> <file>assets/add.png</file>
<file>assets/arrow_refresh.png</file> <file>assets/arrow_refresh.png</file>
<file>assets/chart_bar.png</file> <file>assets/chart_bar.png</file>
<file>assets/compress.png</file>
<file>assets/connect.png</file>
<file>assets/database.png</file> <file>assets/database.png</file>
<file>assets/database_add.png</file> <file>assets/database_add.png</file>
<file>assets/database_delete.png</file> <file>assets/database_delete.png</file>
<file>assets/database_key.png</file>
<file>assets/database_lightning.png</file> <file>assets/database_lightning.png</file>
<file>assets/database_save.png</file> <file>assets/database_save.png</file>
<file>assets/delete.png</file> <file>assets/delete.png</file>
<file>assets/disconnect.png</file>
<file>assets/help.png</file>
<file>assets/key.png</file>
<file>assets/lightning.png</file> <file>assets/lightning.png</file>
<file>assets/lightning_go.png</file> <file>assets/lightning_go.png</file>
<file>assets/page_key.png</file>
<file>assets/pencil.png</file> <file>assets/pencil.png</file>
<file>assets/pencil_add.png</file> <file>assets/pencil_add.png</file>
<file>assets/pencil_delete.png</file> <file>assets/pencil_delete.png</file>
@@ -25,6 +27,7 @@
<file>assets/table_add.png</file> <file>assets/table_add.png</file>
<file>assets/table_delete.png</file> <file>assets/table_delete.png</file>
<file>assets/table_save.png</file> <file>assets/table_save.png</file>
<file>assets/vendor_buntdb.png</file>
<file>assets/vendor_cockroach.png</file> <file>assets/vendor_cockroach.png</file>
<file>assets/vendor_debian.png</file> <file>assets/vendor_debian.png</file>
<file>assets/vendor_dgraph.png</file> <file>assets/vendor_dgraph.png</file>
@@ -35,6 +38,7 @@
<file>assets/vendor_lotus.png</file> <file>assets/vendor_lotus.png</file>
<file>assets/vendor_mongodb.png</file> <file>assets/vendor_mongodb.png</file>
<file>assets/vendor_mysql.png</file> <file>assets/vendor_mysql.png</file>
<file>assets/vendor_qt.png</file>
<file>assets/vendor_redis.png</file> <file>assets/vendor_redis.png</file>
<file>assets/vendor_riak.png</file> <file>assets/vendor_riak.png</file>
<file>assets/vendor_rosedb.png</file> <file>assets/vendor_rosedb.png</file>

BIN
embed.rcc

Binary file not shown.

10
go.mod
View File

@@ -1,4 +1,4 @@
module yvbolt module qbolt
go 1.24.0 go 1.24.0
@@ -71,6 +71,14 @@ require (
github.com/rosedblabs/wal v1.3.6 // indirect github.com/rosedblabs/wal v1.3.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tidwall/btree v1.7.0 // indirect
github.com/tidwall/buntdb v1.3.2 // indirect
github.com/tidwall/gjson v1.14.3 // indirect
github.com/tidwall/grect v0.1.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/rtred v0.1.2 // indirect
github.com/tidwall/tinyqueue v0.1.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.2.0 // indirect github.com/xdg-go/scram v1.2.0 // indirect

17
go.sum
View File

@@ -165,6 +165,23 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
github.com/tidwall/buntdb v1.3.2 h1:qd+IpdEGs0pZci37G4jF51+fSKlkuUTMXuHhXL1AkKg=
github.com/tidwall/buntdb v1.3.2/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=

View File

@@ -13,8 +13,8 @@ import (
) )
const ( const (
APPNAME = "yvbolt" APPNAME = "QBolt"
HOMEPAGE_URL = "https://code.ivysaur.me/yvbolt" HOMEPAGE_URL = "https://code.ivysaur.me/qbolt"
) )
type App struct { type App struct {
@@ -37,6 +37,7 @@ func newApp() *App {
a.dbs = make(map[int]loadedDatabase, 0) a.dbs = make(map[int]loadedDatabase, 0)
a.ui = NewMainWindowUi() a.ui = NewMainWindowUi()
a.ui.MainWindow.SetWindowTitle(APPNAME + " " + appVersion)
// Using stylesheet works better than picking a palette colour across all // Using stylesheet works better than picking a palette colour across all
// different QStyles // different QStyles

View File

@@ -143,6 +143,9 @@ func NewMainWindowUi() *MainWindowUi {
actionCreate_Bolt_database_from_zip__objectName := qt.NewQAnyStringView3("actionCreate_Bolt_database_from_zip") actionCreate_Bolt_database_from_zip__objectName := qt.NewQAnyStringView3("actionCreate_Bolt_database_from_zip")
ui.actionCreate_Bolt_database_from_zip.SetObjectName(*actionCreate_Bolt_database_from_zip__objectName) ui.actionCreate_Bolt_database_from_zip.SetObjectName(*actionCreate_Bolt_database_from_zip__objectName)
actionCreate_Bolt_database_from_zip__objectName.Delete() // setter copied value actionCreate_Bolt_database_from_zip__objectName.Delete() // setter copied value
icon10 := qt.NewQIcon()
icon10.AddFile4(":/assets/compress.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
ui.actionCreate_Bolt_database_from_zip.SetIcon(icon10)
ui.centralwidget = qt.NewQWidget(ui.MainWindow.QWidget) ui.centralwidget = qt.NewQWidget(ui.MainWindow.QWidget)
centralwidget__objectName := qt.NewQAnyStringView3("centralwidget") centralwidget__objectName := qt.NewQAnyStringView3("centralwidget")
ui.centralwidget.SetObjectName(*centralwidget__objectName) ui.centralwidget.SetObjectName(*centralwidget__objectName)
@@ -198,9 +201,9 @@ func NewMainWindowUi() *MainWindowUi {
ui.propertiesBox.SetReadOnly(true) ui.propertiesBox.SetReadOnly(true)
ui.gridLayout_2.AddWidget2(ui.propertiesBox.QWidget, 0, 0) ui.gridLayout_2.AddWidget2(ui.propertiesBox.QWidget, 0, 0)
icon10 := qt.NewQIcon() icon11 := qt.NewQIcon()
icon10.AddFile4(":/assets/chart_bar.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off) icon11.AddFile4(":/assets/chart_bar.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
ui.tabWidget.AddTab2(ui.tabProperties, icon10, "") ui.tabWidget.AddTab2(ui.tabProperties, icon11, "")
ui.tabData = qt.NewQWidget(ui.tabWidget.QWidget) ui.tabData = qt.NewQWidget(ui.tabWidget.QWidget)
tabData__objectName := qt.NewQAnyStringView3("tabData") tabData__objectName := qt.NewQAnyStringView3("tabData")
ui.tabData.SetObjectName(*tabData__objectName) ui.tabData.SetObjectName(*tabData__objectName)
@@ -219,9 +222,9 @@ func NewMainWindowUi() *MainWindowUi {
ui.contentBox.SetHorizontalScrollMode(qt.QAbstractItemView__ScrollPerPixel) ui.contentBox.SetHorizontalScrollMode(qt.QAbstractItemView__ScrollPerPixel)
ui.verticalLayout.AddWidget(ui.contentBox.QWidget) ui.verticalLayout.AddWidget(ui.contentBox.QWidget)
icon11 := qt.NewQIcon() icon12 := qt.NewQIcon()
icon11.AddFile4(":/assets/table.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off) icon12.AddFile4(":/assets/table.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
ui.tabWidget.AddTab2(ui.tabData, icon11, "") ui.tabWidget.AddTab2(ui.tabData, icon12, "")
ui.tabQuery = qt.NewQWidget(ui.tabWidget.QWidget) ui.tabQuery = qt.NewQWidget(ui.tabWidget.QWidget)
tabQuery__objectName := qt.NewQAnyStringView3("tabQuery") tabQuery__objectName := qt.NewQAnyStringView3("tabQuery")
ui.tabQuery.SetObjectName(*tabQuery__objectName) ui.tabQuery.SetObjectName(*tabQuery__objectName)
@@ -253,9 +256,9 @@ func NewMainWindowUi() *MainWindowUi {
ui.splitter_2.AddWidget(ui.queryResult.QWidget) ui.splitter_2.AddWidget(ui.queryResult.QWidget)
ui.verticalLayout_2.AddWidget(ui.splitter_2.QWidget) ui.verticalLayout_2.AddWidget(ui.splitter_2.QWidget)
icon12 := qt.NewQIcon() icon13 := qt.NewQIcon()
icon12.AddFile4(":/assets/lightning.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off) icon13.AddFile4(":/assets/lightning.png", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)
ui.tabWidget.AddTab2(ui.tabQuery, icon12, "") ui.tabWidget.AddTab2(ui.tabQuery, icon13, "")
ui.splitter.AddWidget(ui.tabWidget.QWidget) ui.splitter.AddWidget(ui.tabWidget.QWidget)
ui.gridLayout.AddWidget2(ui.splitter.QWidget, 0, 0) ui.gridLayout.AddWidget2(ui.splitter.QWidget, 0, 0)
@@ -338,12 +341,13 @@ func NewMainWindowUi() *MainWindowUi {
// Retranslate reapplies all text translations. // Retranslate reapplies all text translations.
func (ui *MainWindowUi) Retranslate() { func (ui *MainWindowUi) Retranslate() {
ui.MainWindow.SetWindowTitle(qt.QCoreApplication_Tr("yvbolt")) ui.MainWindow.SetWindowTitle(qt.QCoreApplication_Tr("QBolt"))
ui.actionE_xit.SetText(qt.QMainWindow_Tr("E&xit")) ui.actionE_xit.SetText(qt.QMainWindow_Tr("E&xit"))
ui.mnuExecute.SetText(qt.QMainWindow_Tr("E&xecute")) ui.mnuExecute.SetText(qt.QMainWindow_Tr("E&xecute"))
ui.mnuExecute.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("F9"))) ui.mnuExecute.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("F9")))
ui.mnuDriverVersions.SetText(qt.QMainWindow_Tr("Driver versions...")) ui.mnuDriverVersions.SetText(qt.QMainWindow_Tr("Driver versions..."))
ui.mnuHelpAbout.SetText(qt.QMainWindow_Tr("&About yvbolt")) ui.mnuHelpAbout.SetText(qt.QMainWindow_Tr("&About QBolt"))
ui.mnuHelpAbout.SetToolTip(qt.QMainWindow_Tr("About QBolt"))
ui.mnuHelpAbout.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("F1"))) ui.mnuHelpAbout.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("F1")))
ui.actionConnect.SetText(qt.QMainWindow_Tr("Connect...")) ui.actionConnect.SetText(qt.QMainWindow_Tr("Connect..."))
ui.actionConnect.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("Ctrl+O"))) ui.actionConnect.SetShortcut(qt.NewQKeySequence2(qt.QMainWindow_Tr("Ctrl+O")))

View File

@@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>yvbolt</string> <string>QBolt</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset resource="embed.qrc"> <iconset resource="embed.qrc">
@@ -252,7 +252,10 @@
<normaloff>:/assets/help.png</normaloff>:/assets/help.png</iconset> <normaloff>:/assets/help.png</normaloff>:/assets/help.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;About yvbolt</string> <string>&amp;About QBolt</string>
</property>
<property name="toolTip">
<string>About QBolt</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string>F1</string> <string>F1</string>
@@ -355,6 +358,10 @@
</property> </property>
</action> </action>
<action name="actionCreate_Bolt_database_from_zip"> <action name="actionCreate_Bolt_database_from_zip">
<property name="icon">
<iconset resource="embed.qrc">
<normaloff>:/assets/compress.png</normaloff>:/assets/compress.png</iconset>
</property>
<property name="text"> <property name="text">
<string>Create Bolt database from zip</string> <string>Create Bolt database from zip</string>
</property> </property>

94
sqliteclidriver/event.go Normal file
View File

@@ -0,0 +1,94 @@
package sqliteclidriver
import (
"io"
"sync"
)
const (
evtypeStdout int = iota
evtypeStderr
evtypeExit
)
type processEvent struct {
evtype int
data []byte
err error
}
func (pe processEvent) Error() string {
if pe.err != nil {
return pe.err.Error()
}
if pe.evtype == evtypeStderr {
return string(pe.data)
}
return "<no error>"
}
func (pe processEvent) Unwrap() error {
return pe.err
}
//
func handleEvents(pw io.WriteCloser, pr, pe io.Reader, waiter func() error) (<-chan processEvent, io.WriteCloser, error) {
chEvents := make(chan processEvent, 0)
var wg sync.WaitGroup
go func() {
defer wg.Done()
processEventWorker(pr, evtypeStdout, chEvents)
}()
go func() {
defer wg.Done()
processEventWorker(pe, evtypeStderr, chEvents)
}()
wg.Add(2)
go func() {
// Only call cmd.Wait() after pipes are closed
wg.Wait()
err := waiter()
chEvents <- processEvent{
evtype: evtypeExit,
err: err,
}
close(chEvents)
}()
return chEvents, pw, nil
}
func processEventWorker(p io.Reader, evtype int, dest chan<- processEvent) {
for {
buf := make([]byte, 1024)
n, err := p.Read(buf)
if n > 0 {
dest <- processEvent{
evtype: evtype,
data: buf[0:n],
}
}
if err != nil {
dest <- processEvent{
evtype: evtype,
err: err,
}
// Assume all errors are permanent
// Ordering can produce either io.EOF, ErrClosedPipe, or PathError{"file already closed"}
return
}
}
}

View File

@@ -3,40 +3,15 @@ package sqliteclidriver
import ( import (
"io" "io"
"os/exec" "os/exec"
"sync"
) )
const ( func LocalBinEvents(databasePath string) (<-chan processEvent, io.WriteCloser, error) {
evtypeStdout int = iota // TODO find a way to strictly separate parameters and paths (`--` is not supported here)
evtypeStderr cmd := exec.Command(`/usr/bin/sqlite3`, `-noheader`, `-json`, databasePath)
evtypeExit return execEvents(cmd)
)
type processEvent struct {
evtype int
data []byte
err error
} }
func (pe processEvent) Error() string { func execEvents(cmd *exec.Cmd) (<-chan processEvent, io.WriteCloser, error) {
if pe.err != nil {
return pe.err.Error()
}
if pe.evtype == evtypeStderr {
return string(pe.data)
}
return "<no error>"
}
func (pe processEvent) Unwrap() error {
return pe.err
}
//
func ExecEvents(cmd *exec.Cmd) (<-chan processEvent, io.WriteCloser, error) {
pw, err := cmd.StdinPipe() pw, err := cmd.StdinPipe()
if err != nil { if err != nil {
@@ -58,58 +33,5 @@ func ExecEvents(cmd *exec.Cmd) (<-chan processEvent, io.WriteCloser, error) {
return nil, nil, err return nil, nil, err
} }
chEvents := make(chan processEvent, 0) return handleEvents(pw, pr, pe, cmd.Wait)
var wg sync.WaitGroup
go func() {
defer wg.Done()
processEventWorker(pr, evtypeStdout, chEvents)
}()
go func() {
defer wg.Done()
processEventWorker(pe, evtypeStderr, chEvents)
}()
wg.Add(2)
go func() {
// Only call cmd.Wait() after pipes are closed
wg.Wait()
err = cmd.Wait()
chEvents <- processEvent{
evtype: evtypeExit,
err: err,
}
close(chEvents)
}()
return chEvents, pw, nil
}
func processEventWorker(p io.Reader, evtype int, dest chan<- processEvent) {
for {
buf := make([]byte, 1024)
n, err := p.Read(buf)
if n > 0 {
dest <- processEvent{
evtype: evtype,
data: buf[0:n],
}
}
if err != nil {
dest <- processEvent{
evtype: evtype,
err: err,
}
// Assume all errors are permanent
// Ordering can produce either io.EOF, ErrClosedPipe, or PathError{"file already closed"}
return
}
}
} }

View File

@@ -13,7 +13,7 @@ import (
func TestEventCmd(t *testing.T) { func TestEventCmd(t *testing.T) {
cmd := exec.Command("/bin/bash", "-c", `echo "hello world"`) cmd := exec.Command("/bin/bash", "-c", `echo "hello world"`)
ch, _, err := ExecEvents(cmd) ch, _, err := execEvents(cmd)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -36,7 +36,7 @@ func TestEventCmd(t *testing.T) {
func TestEventCmdStdin(t *testing.T) { func TestEventCmdStdin(t *testing.T) {
cmd := exec.Command("/usr/bin/tr", "a-z", "A-Z") cmd := exec.Command("/usr/bin/tr", "a-z", "A-Z")
ch, pw, err := ExecEvents(cmd) ch, pw, err := execEvents(cmd)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -0,0 +1,94 @@
package sqliteclidriver
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"io"
"strconv"
"golang.org/x/crypto/ssh"
)
func SSHEvents(cl *ssh.Client, databasePath string) (<-chan processEvent, io.WriteCloser, error) {
// Client must have already been Dial()'ed
ses, err := cl.NewSession()
if err != nil {
return nil, nil, fmt.Errorf("NewSession: %w", err)
}
pw, err := ses.StdinPipe()
if err != nil {
return nil, nil, fmt.Errorf("StdinPipe: %w", err)
}
pr, err := ses.StdoutPipe()
if err != nil {
return nil, nil, fmt.Errorf("StdoutPipe: %w", err)
}
pe, err := ses.StderrPipe()
if err != nil {
return nil, nil, fmt.Errorf("StderrPipe: %w", err)
}
// FIXME use accurate Bash escaping
// TODO find a way to strictly separate parameters and paths (`--` is not supported here)
ses.Start(`/usr/bin/sqlite3 -noheader -json "` + strconv.Quote(databasePath) + `"`)
return handleEvents(pw, pr, pe, ses.Wait)
}
//
type sshDriver struct {
cl *ssh.Client
}
func (d *sshDriver) Open(connectionString string) (driver.Conn, error) {
// TODO support custom binpath from our connection string
chEvents, pw, err := SSHEvents(d.cl, connectionString)
if err != nil {
return nil, err
}
return &SCConn{
listen: chEvents,
w: pw,
}, nil
}
var _ driver.Driver = &sshDriver{} // interface assertion
//
type sshConnector struct {
connectionString string
driver *sshDriver
}
func (c *sshConnector) Connect(context.Context) (driver.Conn, error) {
// FIXME support context
return c.driver.Open(c.connectionString)
}
func (c *sshConnector) Driver() driver.Driver {
return c.driver
}
var _ driver.Connector = &sshConnector{} // interface assertion
//
// OpenSSH opens an sql.DB connection over SSH using the supplied ssh.Client
// object.
func OpenSSH(cl *ssh.Client, databasePath string) *sql.DB {
return sql.OpenDB(&sshConnector{
connectionString: databasePath,
driver: &sshDriver{
cl: cl,
},
})
}

View File

@@ -21,25 +21,25 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os/exec"
"yvbolt/lexer" "qbolt/lexer"
"github.com/google/uuid" "github.com/google/uuid"
) )
const DriverName = `sqliteclidriver`
var ErrNotSupported = errors.New("Not supported") var ErrNotSupported = errors.New("Not supported")
// //
// SCDriver is a thing that can open connections to the database for a given
// connection string. It has no particular state.
type SCDriver struct{} type SCDriver struct{}
func (d *SCDriver) Open(connectionString string) (driver.Conn, error) { func (d *SCDriver) Open(connectionString string) (driver.Conn, error) {
// TODO support custom binpath from our connection string // TODO support custom binpath from our connection string
chEvents, pw, err := LocalBinEvents(connectionString)
cmd := exec.Command(`/usr/bin/sqlite3`, `-noheader`, `-json`, connectionString) // n.b. doesn't support `--`
chEvents, pw, err := ExecEvents(cmd)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -53,17 +53,21 @@ func (d *SCDriver) Open(connectionString string) (driver.Conn, error) {
var _ driver.Driver = &SCDriver{} // interface assertion var _ driver.Driver = &SCDriver{} // interface assertion
func init() { func init() {
sql.Register("sqliteclidriver", &SCDriver{}) sql.Register(DriverName, &SCDriver{})
} }
// //
// SCConnector is a container holding a driver and connection string together.
// It is the result of parsing a sql.Open(driverName, connectionString) call
// and has not parsed anything.
type SCConnector struct { type SCConnector struct {
connectionString string connectionString string
driver *SCDriver driver *SCDriver
} }
func (c *SCConnector) Connect(context.Context) (driver.Conn, error) { func (c *SCConnector) Connect(context.Context) (driver.Conn, error) {
// FIXME support context
return c.driver.Open(c.connectionString) return c.driver.Open(c.connectionString)
} }
@@ -75,6 +79,7 @@ var _ driver.Connector = &SCConnector{} // interface assertion
// //
// SCConn represents a single, active, open, connection to the database.
type SCConn struct { type SCConn struct {
listen <-chan processEvent listen <-chan processEvent
w io.WriteCloser w io.WriteCloser
@@ -136,7 +141,7 @@ func (s *SCStmt) buildQuery(args []driver.Value) ([]byte, error) {
// Embed query params // Embed query params
// WARNING: Not secure against injection? That's not a security boundary // WARNING: Not secure against injection? That's not a security boundary
// for the purposes of yvbolt, but maybe for other package users // for the purposes of qbolt, but maybe for other package users
var querybuilder bytes.Buffer var querybuilder bytes.Buffer

View File

@@ -21,24 +21,24 @@ type SSHTunnel struct {
Type autoconfig.OneOf Type autoconfig.OneOf
Password *struct { Password *struct {
Password autoconfig.Password Password autoconfig.Password
} } `yicon:":/assets/key.png" json:",omitempty"`
PrivateKeyExternal *struct { PrivateKeyExternal *struct {
PrivateKeyFile autoconfig.ExistingFile `ylabel:"Private Key"` PrivateKeyFile autoconfig.ExistingFile `ylabel:"Private Key"`
PrivateKeyFilePassword autoconfig.Password `ylabel:"Password (optional)"` PrivateKeyFilePassword autoconfig.Password `ylabel:"Password (optional)"`
} `ylabel:"Private key (External)"` } `ylabel:"Private key (External)" yicon:":/assets/page_key.png" json:",omitempty"`
PrivateKeyInternal *struct { PrivateKeyInternal *struct {
PrivateKeyContent autoconfig.MultiLineString `ylabel:"Private Key"` PrivateKeyContent autoconfig.MultiLineString `ylabel:"Private Key"`
PrivateKeyContentPassword autoconfig.Password `ylabel:"Password (optional)"` PrivateKeyContentPassword autoconfig.Password `ylabel:"Password (optional)"`
} `ylabel:"Private key (Internal)"` } `ylabel:"Private key (Internal)" yicon:":/assets/page_key.png" json:",omitempty"`
SSHAgent *sshAgentConn `ylabel:"SSH Agent"` SSHAgent *sshAgentConn `ylabel:"SSH Agent" yicon:":/assets/vendor_ssh.png" json:",omitempty"`
} }
H2 autoconfig.Header `ylabel:"Server host key:" json:",omitempty"` H2 autoconfig.Header `ylabel:"Server host key:" json:",omitempty"`
HostVerification struct { HostVerification struct {
Type autoconfig.OneOf Type autoconfig.OneOf
InsecureSkipVerify *struct{} InsecureSkipVerify *struct{} `json:",omitempty"`
ExternalKnownHostsFile *struct { ExternalKnownHostsFile *struct {
Path autoconfig.ExistingFile Path autoconfig.ExistingFile
} } `json:",omitempty"`
} }
} }

View File

@@ -15,11 +15,11 @@
"0409": { "0409": {
"Comments": "", "Comments": "",
"CompanyName": "code.ivysaur.me", "CompanyName": "code.ivysaur.me",
"FileDescription": "yvbolt Database Editor", "FileDescription": "QBolt Database Editor",
"FileVersion": "0.0.0.0", "FileVersion": "0.0.0.0",
"LegalCopyright": "code.ivysaur.me", "LegalCopyright": "code.ivysaur.me",
"LegalTrademarks": "", "LegalTrademarks": "",
"ProductName": "yvbolt", "ProductName": "QBolt",
"ProductVersion": "0.0.0" "ProductVersion": "0.0.0"
} }
} }