The Problem
Developers and DBAs often need a single terminal client that works across dozens of SQL and NoSQL engines. Maintaining a separate native CLI for each system adds friction, especially when scripts must run on multiple back‑ends.
What This Does
usql provides a PostgreSQL‑style interactive shell that drives any of the supported drivers. The source is organized as a collection of self‑contained driver packages (drivers/ ≈ 104 files) plus a thin command dispatcher in handler/handler.go and the entry point main.go. Driver‑specific metadata lives under drivers/metadata/, while helper utilities (environment handling, command‑line completion, meta‑commands) are in env/, metacmd/, and stmt/. The repository ships a Dockerfile (drivers/testdata/docker/Dockerfile) for containerised builds and a full CI pipeline (.github/workflows/*.yml).
How It Is Wired
- Program start –
maininmain.go(line 23) creates ahandlerand callsRun. This reaches 78 functions and is the only top‑level caller. - Command handling –
handler/handler.godefinesExecute,Run,Bind, anddoExecChart.Run(line 145) is the busiest entry point, reaching 134 functions and invoking the core driver logic. - Driver selection –
drivers/drivers.goregisters each driver ininit(e.g.,adodb,clickhouse). The globalNextfunction (called from 68 places) drives the REPL loop, pulling commands fromhandler. - Database I/O – The shortest external‑effect path is:
main → Run → Execute → Rollback→tx.Rollback(DB)Run → Execute → Columns → Query→r.db.Query(DB)main → run → writeReadme→os.ReadFile(filesystem) - High‑impact functions –
Next,Close,Vars,Run,New,Scan,Print, andWrapErrare called from 15‑68 locations each, so changes ripple widely. - File responsibilities –
drivers/metadata/metadata.go– central catalog API (65 functions, 55 types).handler/handler.go– orchestrates command execution, I/O, and timing (62 functions).drivers/drivers.go– driver registry and capability queries (37 functions).env/env.go– environment variable handling and external command launching (20 functions).
No circular import cycles were detected; the internal call graph contains 995 self‑calls, confirming a flat package hierarchy.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/usql
cd usql
# Build the binary (requires Go 1.22+)
go build -o usql ./cmd/usql # main.go is the entry point
# Run the CLI against a database
./usql postgres://user:pass@localhost/dbname
The repository also supplies a Docker build (docker build -f drivers/testdata/docker/Dockerfile .) and a helper script build.sh for reproducible container images.
Configuration files for individual drivers live under contrib/ (e.g., contrib/mysql/usql-config). Environment variables are read via env/vars.go (Vars, Getenv). No additional runtime flags are required beyond standard DSN syntax.
Real‑World Use
A CI pipeline can invoke usql to run migration scripts against multiple back‑ends without changing the command line. For example:
./usql mysql://root@localhost/db -c "\i migrations/2024_add_user.sql"
./usql postgres://admin@dbserver/db -c "\i migrations/2024_add_user.sql"
The same script works for Oracle, SQLite, or any driver present in drivers/.
Code Health & Issues
- High – GitHub Actions are pinned to tags only (
shimataro/ssh-key-action@v2, etc.). Tags can move, potentially exposing secrets. Fix: Replace each tag with a 40‑character commit SHA and keep the version comment.
- Medium – Workflows lack explicit
permissionsforGITHUB_TOKEN. Without restriction the token inherits full repo rights. Fix: Addpermissions: { contents: read }at the top of each workflow, expanding only where needed.
- Medium – No dependency‑vulnerability scan is gated in CI. Fix: Add
dependency-review-actiononpull_requestor anosv-scannerstep.
- Low – Jobs omit
timeout-minutes, so they may run up to the platform default (6 h). Fix: Set a reasonabletimeout-minutesper job.
Additional static findings include duplicated 6‑line shell blocks across 39 files, nine oversized source files (e.g., drivers/completer/completer.go ≈ 1.6 k lines), deep nesting (max depth 6) in several driver implementations, and lingering TODO/FIXME markers (12 total). Refactoring the large files into smaller, responsibility‑focused packages would reduce cognitive load and blast radius.
The Bottom Line
usql delivers a functional, Go‑based multi‑driver SQL shell with a clear entry point and well‑isolated driver packages, making it a practical replacement for per‑engine CLIs. The codebase is healthy overall but would benefit from refactoring large files, deduping helper scripts, and tightening CI security settings before adopting in a high‑risk production environment. Engineers comfortable with Go and SQL driver development can extend or harden the tool without major architectural changes.