feat: add read-only SQLite inspection
This commit is contained in:
+38
-17
@@ -1,14 +1,15 @@
|
||||
mod app;
|
||||
mod db;
|
||||
|
||||
use std::{error::Error, io::IsTerminal, process::ExitCode};
|
||||
use std::{error::Error, io::IsTerminal, path::Path, process::ExitCode};
|
||||
|
||||
type Result<T, E = Box<dyn Error>> = std::result::Result<T, E>;
|
||||
|
||||
const HELP: &str = r#"dbdash — read-only PostgreSQL inspection
|
||||
const HELP: &str = r#"dbdash — read-only PostgreSQL and SQLite inspection
|
||||
|
||||
Usage:
|
||||
dbdash [POSTGRES_CONNECTION_STRING]
|
||||
dbdash --sqlite PATH
|
||||
dbdash --help
|
||||
|
||||
With no argument, the connection is read from DATABASE_URL.
|
||||
@@ -16,6 +17,10 @@ Prefer the environment variable to keep passwords out of process arguments:
|
||||
export DATABASE_URL='postgresql://user@localhost:5432/database'
|
||||
cargo run
|
||||
|
||||
Open an existing SQLite database file (read-only; missing files are not created):
|
||||
cargo run -- --sqlite ./database.sqlite
|
||||
SQLite paths can be relative or absolute; quote paths containing spaces.
|
||||
|
||||
PostgreSQL URLs and libpq-style key=value strings are accepted.
|
||||
Use sslmode=require for mandatory TLS; certificates and hostnames are verified.
|
||||
Use sslmode=disable only for trusted local connections.
|
||||
@@ -31,9 +36,11 @@ Navigation:
|
||||
r Refresh relations and the active relation
|
||||
q / Ctrl-C Quit
|
||||
|
||||
Reads are enforced read-only by PostgreSQL and have a 5-second query timeout.
|
||||
The interface waits for reads to complete. NULL cells are dimmed.
|
||||
Pages use primary-key order, or physical order for keyless tables.
|
||||
PostgreSQL reads are enforced read-only and have a 5-second query timeout.
|
||||
SQLite files are opened read-only, with a 5-second lock wait but no query timeout.
|
||||
The interface waits for reads to complete. NULL cells are dimmed; SQLite BLOBs
|
||||
are shown as X'hex'. Only SQLite's main database is inspected.
|
||||
Pages use primary-key order, or physical row order for keyless tables when available.
|
||||
Keyless views have no guaranteed order; concurrent changes can shift pages.
|
||||
"#;
|
||||
|
||||
@@ -49,24 +56,38 @@ fn main() -> ExitCode {
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let mut args = std::env::args_os().skip(1);
|
||||
let connection = match args.next() {
|
||||
Some(argument) if argument == "--help" || argument == "-h" => {
|
||||
print!("{HELP}");
|
||||
return Ok(());
|
||||
}
|
||||
Some(argument) => argument
|
||||
.into_string()
|
||||
.map_err(|_| "connection string must be UTF-8")?,
|
||||
None => std::env::var("DATABASE_URL")
|
||||
.map_err(|_| "set DATABASE_URL or pass a connection string; see dbdash --help")?,
|
||||
let argument = args.next();
|
||||
if argument
|
||||
.as_ref()
|
||||
.is_some_and(|arg| arg == "--help" || arg == "-h")
|
||||
{
|
||||
print!("{HELP}");
|
||||
return Ok(());
|
||||
}
|
||||
let sqlite = argument.as_ref().is_some_and(|arg| arg == "--sqlite");
|
||||
let connection = if sqlite {
|
||||
args.next()
|
||||
.ok_or("--sqlite requires a database file path; see dbdash --help")?
|
||||
} else {
|
||||
argument.or_else(|| std::env::var_os("DATABASE_URL"))
|
||||
.ok_or("set DATABASE_URL, pass a PostgreSQL connection string, or use --sqlite PATH; see dbdash --help")?
|
||||
};
|
||||
if args.next().is_some() {
|
||||
return Err("expected one connection string; see dbdash --help".into());
|
||||
return Err("unexpected extra arguments; see dbdash --help".into());
|
||||
}
|
||||
if !std::io::stdin().is_terminal() || !std::io::stdout().is_terminal() {
|
||||
return Err("an interactive terminal is required; see dbdash --help".into());
|
||||
}
|
||||
let mut app = app::App::new(db::Database::connect(&connection)?)?;
|
||||
let database = if sqlite {
|
||||
db::Database::open_sqlite(Path::new(&connection))?
|
||||
} else {
|
||||
db::Database::connect(
|
||||
connection
|
||||
.to_str()
|
||||
.ok_or("connection string must be UTF-8")?,
|
||||
)?
|
||||
};
|
||||
let mut app = app::App::new(database)?;
|
||||
let result = (|| {
|
||||
let mut terminal = ratatui::try_init()?;
|
||||
app.run(&mut terminal)
|
||||
|
||||
Reference in New Issue
Block a user