Skip to main content
Everything needed to get a DuckDbConnection open and talking to DuckDB — adding the crate, the ways to establish a connection, reaching the core driver underneath, running raw DDL, and the error type you catch. Verified against crates/better-duck-diesel/src/connection.rs and the crate’s tests.

Adding the crate

Pin the exact pre-release version. Cargo’s default version match excludes pre-releases, so a bare "0.1" will not resolve. See Installation.
Diesel’s own derives (Queryable, Insertable, QueryableByName, …) come from diesel itself — this crate only provides the backend. Opt into date/time with features = ["chrono"], r2d2 for pooling, and udf/derive/spatial/json as needed — see the feature-flags table.
chrono is not on by default here (it is in core). Enable it before using DATE / TIME / TIMESTAMP / TIMESTAMPTZ / INTERVAL columns.

Establishing a connection

DuckDbConnection::establish is Diesel’s Connection::establish:
":memory:" opens a fresh in-memory database; any other string (after stripping an optional duckdb:// prefix) is treated as a file path.
Each :memory: connection is its own independent, empty database. If you need several handles to observe the same in-memory data (e.g. a pool), share one core Database — see from_core below and SharedDuckDbConnectionManager.

Wrapping an existing core connection

To share one core Database across several Diesel connections, open it through core and wrap each connection with from_core:

Reaching the core connection

.inner() / .inner_mut() expose the underlying better_duck_core::connection::Connection so you can register UDFs, load extensions, or call core-only APIs:
This is the bridge between the two crates: anything the ORM does not model — the appender, columnar chunks, Arrow/Polars, async — you reach through inner_mut(). See better-duck-core.
UDF registration and LOADing extensions live on a single DuckDB connection, not on a shared Database. In a pool, do them in the on_connect hook so every pooled connection is set up.

Running raw / DDL statements

DuckDbConnection implements SimpleConnection, so DDL and multi-statement batches go through batch_execute (which maps to core’s execute_batch). It runs one or more ;-separated statements in a single call — ideal for schema setup:
For a single raw query mapped with Diesel’s QueryableByName, use diesel::sql_query(...). This is also the path for the DuckDB-specific Duck* types, which bind and load via QueryableByName rather than the typed builder — see Type mapping.

Error type

Backend errors surface as Diesel’s diesel::result::Error; the underlying DuckDB error is wrapped by better_duck_diesel::result::DuckDbError (carrying core’s EngineError message). Catch them as QueryResult<_> / diesel::result::Error like any other Diesel backend.

See also

Schema & Migrations

Declare tables with table! and run diesel migration.

Transactions

.transaction, test transactions, and the savepoint caveat.

Pooling

r2d2 pools and on_connect per-connection setup.

Core connections

The driver inner_mut() hands you.