An experimental duckdb bindings for Ocaml
0

Configure Feed

Select the types of activity you want to include in your feed.

5 1 0

Clone this repository

https://git.vm.fail/gdiazlo.tngl.sh/duckdb https://git.vm.fail/did:plc:7mx4ywmojd2prdw23djc37zn
ssh://git@knot1.tangled.sh:2222/gdiazlo.tngl.sh/duckdb ssh://git@knot1.tangled.sh:2222/did:plc:7mx4ywmojd2prdw23djc37zn

For self-hosted knots, clone URLs may differ based on your setup.


README.md

duckdb#

Idiomatic OCaml bindings to DuckDB, the in-process OLAP database — what sqlite3 is to SQLite.

FFI mechanism: ctypes#

These bindings talk to DuckDB's C API through ctypes (with ctypes-foreign for dynamic linking). There is no hand-written C stub layerctypes expresses the foreign function surface directly in OCaml.

The bindings are generated, not hand-written#

The low-level binding surface is generated from the DuckDB C API, not maintained by hand. This is the whole point of the project: when DuckDB ships a new release (e.g. the upcoming 2.x), we regenerate the bindings from its headers instead of hand-patching them.

The generator itself is a pure OCaml library, driven by a small CLI.

Project layout#

Path Role
lib/duckdb_c/ Low-level ctypes FFI surface. Generated, not hand-written.
lib/duckdb/ High-level, idiomatic OCaml API consumers program against.
lib/duckdb_bindgen/ Pure code generator: DuckDB C API → ctypes bindings source.
bin/duckdb_bindgen/ climate-based CLI that drives the generator.
lib/duckdb_shell/ Pure shell domain for oduck: output renderers, settings, dot-command + SQL-statement parsing.
lib/duckdb_cli/ climate-based CLI shell for oduck (argv, REPL) over duckdb_shell.
bin/duckdb_cli/ The oduck binary — a DuckDB SQL shell mirroring the duckdb CLI.
test/parity/ run.sh — diffs oduck output against the real duckdb CLI.
test/ alcotest test harness (qcheck available for property tests).
vendor/ Pinned, prebuilt DuckDB library + header (provisioned, not built).
scripts/ fetch-duckdb.sh — downloads/verifies/unpacks the prebuilt.

Vendored DuckDB library#

The bindings compile against duckdb.h and link against a prebuilt libduckdb shared library. We do not vendor DuckDB source. Instead a single pinned, checksum-verified prebuilt archive is downloaded into vendor/duckdb/ by scripts/fetch-duckdb.sh, and lib/duckdb_c/dune splices the resulting compile/link flags in via (:include ...).

The pinned version lives in exactly one place — vendor/duckdb.version — so a future DuckDB 2.x bump is a one-line change. Supported platforms are Linux and macOS; Windows is out of scope. See vendor/README.md for the full vendoring and opam packaging strategy.

This is currently a scaffold: the libraries contain placeholder modules so the tree builds and links. Real bindings are not implemented yet.

Build & test#

The prebuilt DuckDB library must be provisioned first (one-time, re-run on a version bump):

./scripts/fetch-duckdb.sh   # download + verify + unpack into vendor/duckdb/
dune build
dune runtest
dune exec duckdb-bindgen -- --help

oduck — a DuckDB SQL shell#

oduck is a command-line SQL shell built on these bindings, an OCaml twin of the official duckdb CLI. It is a thin climate shell (lib/duckdb_cli/) over a pure rendering/parsing domain (lib/duckdb_shell/) over the bindings — the same DDD layering the project uses elsewhere.

oduck -c "SELECT 42 AS answer"     # run SQL and exit
oduck mydb.db -c "SELECT * FROM t" # against a file-backed database
oduck mydb.db                      # interactive REPL (in-memory if omitted)
echo "SELECT 1;" | oduck           # read a script from stdin
oduck --csv -c "SELECT * FROM t"   # choose an output mode

Output modes (--csv --json --line --markdown --list --table --column --box, default box) reproduce the corresponding duckdb mode, and the interactive/piped REPL supports the common dot-commands (.mode, .headers, .separator, .nullvalue, .maxrows, .tables, .schema, .databases, .read, .output, .help, .quit).

Parity with the real CLI#

Correctness is defined as byte-for-byte agreement with the installed duckdb CLI, which is used as the oracle. test/parity/run.sh feeds the same input to both binaries across a corpus × output-mode matrix (and piped dot-command scripts) and diffs stdout/stderr and exit codes:

bash test/parity/run.sh   # skips gracefully if `duckdb` is not on PATH

All covered cases match exactly. Documented v1 gaps (reported by the harness's informational section, not silently dropped): the duckbox row-elision footer spacing past maxrows + 3, embedded control characters in markdown/table, terminal-width column elision, codepoint-based (not East-Asian) display width, and --csv-style flag spelling (vs duckdb's -csv). The interactive REPL is line-oriented; a mosaic full-screen front-end is a planned enhancement.