dbcli
Automatically convert SQL query results to JSON without struct mapping.
Features
- Schema-free — no struct definitions or
#[derive]needed; works directly on raw query rows - Multi-database — supports MySQL, PostgreSQL, and SQLite via feature flags (all enabled by default), plus ODBC for Access (.mdb/.accdb) and Excel (.xls/.xlsx)
- CLI tool — interactive REPL mode with SQL history, colored table output, and JSON export
- Vector support — pgvector (
vector,halfvec,sparsevec), MySQL HeatWave / MySQL 9.0+, sqlite-vec - Customizable — override type conversion logic per-column with the
ToJsonCustomizertrait - Performant — function pointer array built once per query; zero overhead per row during iteration
- ODBC support — query Microsoft Access (.mdb/.accdb), Excel (.xls/.xlsx), and any ODBC-compatible data source; auto-detects driver from file extension
Installation
By default, all features are enabled (CLI + all databases):
[]
# Default: all features enabled (CLI + all databases)
= "0.1"
# Or select specific databases (as library, without CLI)
= { = "0.1", = false, = ["postgres"] }
Each feature automatically pulls in the corresponding sqlx driver and auxiliary crates:
| Feature | Extra dependencies |
|---|---|
cli |
clap, tokio, comfy-table, rustyline, crossterm |
mysql |
sqlx/mysql, chrono, rust_decimal, base64, encoding_rs |
postgres |
sqlx/postgres, chrono, rust_decimal, base64, encoding_rs |
sqlite |
sqlx/sqlite, base64 |
odbc |
odbc-api, base64, encoding_rs, tokio (requires system unixODBC library) |
Note: The
odbcfeature is not included in the default feature set because it requires the system-level unixODBC library. Enable it explicitly when needed.
Quick Start
PostgreSQL
use PgPool;
use to_json;
async
MySQL
use MySqlPool;
use to_json;
async
SQLite
use SqlitePool;
use to_json;
async
ODBC (Access / Excel)
use to_json;
use ;
ODBC is synchronous; when used in an async context,
execute_raw_sqlwraps calls intokio::task::spawn_blockingautomatically.
Example output:
CLI Tool
When built with the cli feature (included by default), dbcli provides a standalone
command-line tool for interactive SQL querying.
Build & Install
# Build with all databases (default)
# Or select specific databases
# Build with ODBC support
# Build with ODBC + PostgreSQL
Usage
# Single query
# Interactive REPL mode (omit --sql)
# Use environment variable
# JSON output (pipe-friendly)
# Limit rows in table mode (default: 1000)
# Test connection only
# Query Access database via ODBC
# Query Excel spreadsheet via ODBC
# Direct ODBC connection string
REPL Mode
When no --sql argument is provided, dbcli enters interactive REPL mode:
- Multi-line SQL input (terminate with
;) - Arrow-key history navigation (persisted to
~/.db-json_history) - Type
exit,quit, or\qto leave - Ctrl+C clears the current multi-line buffer; Ctrl+D exits immediately
Parameters
| Parameter | Short | Description |
|---|---|---|
--url |
-u |
Database connection URL (or set DATABASE_URL env) |
--sql |
-s |
SQL query to execute (omit for REPL mode) |
--connect |
--conn |
Test connection only |
--json |
-j (implicit) |
Force JSON output |
--table |
Force table output | |
--limit |
-l |
Max rows in table mode (default: 1000, 0 = unlimited) |
--max-col-width |
--mcw |
Max column width (default: 0 = auto) |
--help |
-h |
Show help |
--version |
-V |
Show version |
Output Modes
- Terminal (TTY): Colored table with UTF-8 borders, auto-adapted to terminal width
- Pipe / redirect: JSON array output for programmatic use
- Override with
--jsonor--tableflags
Custom Type Parsing
Implement the ToJsonCustomizer trait to override the default conversion for any column type.
Register it once at application startup via set_to_json_customizer.
use ;
;
How it works:
determine_parsing_methodscallscustomize_mysql/customize_pg/customize_sqliteonce per column when the first row is processed.- The returned function pointer (or the built-in default) is cached in a
Vec. - All subsequent rows call the cached pointer directly — no trait dispatch, no branch per row.
Implement only the database methods you need; the other two default to built-in behavior.
Type Mapping Reference
All type_name values passed to ToJsonCustomizer are UPPERCASE strings.
MySQL
| DB Type | type_name |
Default output |
|---|---|---|
| VARCHAR / CHAR / TEXT | "VARCHAR" / "CHAR" / "TEXT" |
String |
| INT / BIGINT / SMALLINT (and unsigned variants) | "INT" / "BIGINT" / "SMALLINT" |
i64 |
| FLOAT / DOUBLE / REAL | "FLOAT" / "DOUBLE" / "REAL" |
f64 (NaN/Inf → String) |
| DATETIME | "DATETIME" |
String "%Y-%m-%d %H:%M:%S" |
| DATE | "DATE" |
String "%Y-%m-%d" |
| TIME | "TIME" |
String "%H:%M:%S" (via NaiveTime) |
| TIMESTAMP | "TIMESTAMP" |
String "%Y-%m-%d %H:%M:%S %Z" (DateTime → local) |
| DECIMAL / NUMERIC | "DECIMAL" / "NUMERIC" |
String (precision preserved) |
| BLOB / TINYBLOB | "BLOB" / "TINYBLOB" |
String (text) or Base64 (binary) |
| MEDIUMBLOB / LONGBLOB | "MEDIUMBLOB" / "LONGBLOB" |
Base64 String |
| BINARY | "BINARY" |
UUID String (16-byte) or Base64 String |
| VARBINARY | "VARBINARY" |
Auto encoding detection (UTF-8/GBK/BIG5) → String |
| JSON | "JSON" |
serde_json::Value |
| BOOLEAN / BOOL | "BOOLEAN" / "BOOL" |
bool |
| ENUM | "ENUM" |
String |
| SET | "SET" |
JSON Array |
| VECTOR (HeatWave / MySQL 9.0+) | "VECTOR" |
JSON Array (f64 values) |
PostgreSQL
Raw PG type names are normalized to uppercase by detect_pg_type.
| Raw PG Type | type_name |
Default output |
|---|---|---|
| text / varchar / char / bpchar / citext | "TEXT" |
String |
| int2 / int4 / int8 / smallint / bigint | "INT" |
i64 |
| float4 / float8 / real | "FLOAT" |
f64 (via parse_float_value, NaN/Inf → String) |
| numeric / decimal | "NUMERIC" |
String (precision preserved) |
| bool / boolean | "BOOL" |
bool |
| date | "DATE" |
String "%Y-%m-%d" |
| timestamp without time zone | "TIMESTAMP" |
String "%Y-%m-%d %H:%M:%S" (NaiveDateTime) |
| timestamp with time zone | "TIMESTAMPTZ" |
String (RFC 3339) |
| time / timetz | "TIME" |
String "%H:%M:%S" (via NaiveTime) |
| jsonb / json | "JSON" |
serde_json::Value |
| bytea | "BYTEA" |
String (text) or Base64 (binary) |
| uuid | "UUID" |
String |
| array types | "ARRAY" |
JSON Array |
| interval / money / inet / cidr / macaddr / xml | "TEXT" |
String |
| geometry / geography | "GEOMETRY" |
String |
| hstore | "HSTORE" |
String |
| enum types | "ENUM" |
String (label) |
| range types | "RANGE" |
JSON Object {lower, upper, lower_inc, upper_inc} |
| composite types | "COMPOSITE" |
String (text representation) |
| domain types | "DOMAIN" |
Resolved to underlying base type |
| vector / halfvec (pgvector) | "VECTOR" |
JSON Array (f64 values) |
| sparsevec (pgvector) | "SPARSEVEC" |
JSON Object {dimensions, indices, values} |
| bit / varbit | "BIT" |
String (binary, e.g. "10101010") |
SQLite
| SQLite Type | type_name |
Default output |
|---|---|---|
| TEXT / DATETIME / DATE / TIME | "TEXT" |
String (auto-parses JSON if starts with { or [) |
| INTEGER / BOOLEAN | "INTEGER" |
i64 |
| REAL | "REAL" |
f64 |
| BLOB | "BLOB" |
Base64 String |
| NUMERIC | "NUMERIC" |
Auto-infer: i64 / f64 / String |
| NULL | "NULL" |
Dynamic inference |
| VECTOR (sqlite-vec) | "VECTOR" |
JSON Array (f64 values) |
ODBC
Type mapping is based on the odbc_api::DataType enum. Column names from sources without
headers (e.g., Excel without HDR) are auto-generated as col_1, col_2, etc. Duplicate
names are disambiguated with _2, _3 suffixes.
| ODBC DataType | type_name |
Default output |
|---|---|---|
| Integer / SmallInt / TinyInt / BigInt | "INTEGER" |
i64 |
| Float / Double / Real | "FLOAT" |
f64 (NaN/Inf → String) |
| Decimal / Numeric | "DECIMAL" |
String (precision preserved) |
| Bit | "BIT" |
bool (Access: -1/1 = true, 0 = false) |
| Char / Varchar / LongVarchar / WChar / WVarchar / WLongVarchar | "TEXT" |
String (auto encoding: UTF-8 / GBK / BIG5) |
| Date | "DATE" |
String |
| Time | "TIME" |
String |
| Timestamp | "TIMESTAMP" |
String |
| Binary / Varbinary / LongVarbinary | "BINARY" |
Base64 String |
| Other | "TEXT" |
String |
API Reference
Core functions
| Symbol | Description |
|---|---|
to_json::mysql::to_json(rows) |
Convert Vec<MySqlRow> → (Vec<Value>, Vec<ColumnBaseInfo>) |
to_json::postgres::to_json(rows) |
Convert Vec<PgRow> → (Vec<Value>, Vec<ColumnBaseInfo>) |
to_json::sqlite::to_json(rows) |
Convert Vec<SqliteRow> → (Vec<Value>, Vec<ColumnBaseInfo>) |
to_json::odbc::to_json(cursor) |
Convert ODBC Cursor → (Vec<Value>, Vec<ColumnBaseInfo>) |
execute::odbc::execute_raw_sql(conn_str, sql) |
Execute SQL via ODBC, returns Vec<SqlResult> |
to_json::set_to_json_customizer(b) |
Register a global customizer (call once at startup) |
to_json::f64_to_json_safe(f) |
Convert f64 to JSON, mapping NaN/Infinity to strings |
to_json::blob_is_text(data) |
Heuristic: detect whether binary data is human-readable text |
Types
| Type | Description |
|---|---|
to_json::ToJsonCustomizer |
Trait for custom per-column type conversion |
column_info::ColumnBaseInfo |
Column metadata: name, type, index |
Full documentation is available on docs.rs/dbcli.
License
MIT