Interactive DuckDB shell implemented in Solod (So) and linked against libduckdb.
Experimental research code. SoloDuckDB is an AI assisted proof-of-concept to explore embedding DuckDB with Solod. It is not a supported project, not audited for security or correctness, and not suitable for production workloads. Prefer the official DuckDB CLI for anything that matters.
This research repository is self-contained and vendors the Solod compiler/stdlib as a git submodule (solod → solod-dev/solod). Builds always sync to the latest main on that repo (make runs scripts/update_solod.sh before translate; CI does the same).
CLI behavior is loosely aligned with the official DuckDB CLI (LTS):
% soloduck -c "SELECT version()"
┌-------------┐
│ "version"() │
│ varchar │
├-------------┤
│ v1.5.2 │
└-------------┘
One-line installer (Linux/macOS, amd64/arm64)
curl -fsSL https://lmangani.github.io/soloduck/install | shInstalls soloduck to ~/.soloduck/cli/latest/soloduck. Override latest with SOLODUCK_VERSION=<tag>.
git clone --recurse-submodules https://github.com/lmangani/soloduck.git
cd soloduck
make
./soloduck -versionIf you cloned without submodules:
git submodule update --init --recursivemake fetches the latest Solod main into solod/ before so translate. To record that pointer in git: git add solod && git commit.
You need libduckdb on the machine (install)
make DUCK_PREFIX=/usr/localmake runs Solod so translate into gen/, then compiles and links soloduck next to the Makefile. Use make clean to remove generated C.
Show flags:
./soloduck -helpOne-shot SQL (same idea as official duckdb -c):
./soloduck -batch -c "SELECT version();"
./soloduck :memory: 'SELECT 42 AS n'- Interactive REPL (no
-cand no SQL argument) -batch: read stdin as a script with no prompts (pipes and files).- Output modes:
-csv,-json, or.mode …. Defaultduckboxtries to mirror the official CLI layout
This compares the official DuckDB CLI against Solod + DuckDB’s C API in soloduck
Reference benchmark (python3 scripts/benchmark_init.py --runs 25 --warmup 2 on Apple Silicon M4;
Wall time per fresh process (startup + query + exit).
Timed runs per binary (after warmup): 25
Query: 'SELECT 1;'
duckdb mean 15.11 ms σ 0.64 ms min 14.19 ms max 16.55 ms
soloduck mean 14.54 ms σ 0.62 ms min 13.45 ms max 15.85 ms
ratio (soloduck / duckdb mean wall time): 0.96x
Query: 'SELECT version();'
duckdb mean 15.10 ms σ 0.68 ms min 13.92 ms max 16.74 ms
soloduck mean 14.26 ms σ 0.57 ms min 13.44 ms max 15.17 ms
ratio (soloduck / duckdb mean wall time): 0.94x
Solod vs Go (database/sql + duckdb-go, CGO)
This compares Go runtime + database/sql + duckdb-go against Solod + DuckDB’s C API in soloduck
Reference benchmark (python3 scripts/benchmark_init_go.py --runs 25 --warmup 2 on Apple Silicon M4;
Wall time per fresh process (startup + query + exit).
Timed runs per binary (after warmup): 25
Compare: soloduck (Solod + libduckdb C API) vs benchmark_go_once (Go + database/sql + duckdb-go / CGO).
Query: 'SELECT 1;'
duckdb-go mean 17.01 ms σ 0.75 ms min 15.91 ms max 18.62 ms
soloduck mean 14.55 ms σ 0.48 ms min 13.77 ms max 15.40 ms
ratio (soloduck / duckdb-go mean wall time): 0.86x
Query: 'SELECT version();'
duckdb-go mean 17.18 ms σ 1.28 ms min 15.71 ms max 21.64 ms
soloduck mean 14.83 ms σ 0.78 ms min 13.79 ms max 16.74 ms
ratio (soloduck / duckdb-go mean wall time): 0.86x
| Path | Purpose |
|---|---|
solod/ |
Submodule: upstream Solod (so translate, so/* stdlib). Always built at latest main (see scripts/update_solod.sh). |
duckdb/ |
Solod package wrapping DuckDB’s C API (duckdb.h). |
main.go |
CLI entrypoint. |
gen/ |
Generated C from make translate (removed by make clean). |
scripts/benchmark_init.py |
Startup/latency vs official duckdb CLI. |
scripts/benchmark_init_go.py |
Startup/latency vs Go database/sql + duckdb-go (CGO). |
scripts/update_solod.sh |
Fetch latest Solod main into the solod/ submodule (run by make and CI). |
docs/ |
GitHub Pages site + curl installer (deployed to gh-pages branch). |
go.mod uses replace solod.dev => ./solod so imports resolve to the submodule checkout.