compile_error!.
The two backends
Both engines implement the
DuckBackend trait; commands are written against the trait and
dispatched through managed state, which is why your JS never changes when you switch.
The DuckBackend trait
Every command delegates to one trait method. The trait is the contract that keeps the two engines
interchangeable:
Row is serde_json::Map<String, Value> (a JSON object); ExecuteResult is { rowsAffected }.
Connection-string resolution
Both engines resolve the webview’s connection string the same way: strip a leadingduckdb:
prefix, then map an empty string or :memory: to an in-memory database, otherwise treat it as a
filesystem path.
How the engines differ
Dynamic SQL on the Diesel backend
The plugin forwards arbitrary statements plus JSON params, whose result shape is only known at runtime. Diesel’s compile-timeQueryableByName cannot express that, so the Diesel engine runs
dynamic-column SQL on the pooled connection’s underlying core Connection via inner_mut(),
reusing the same DuckValue ⇄ JSON materialization as the core engine. The two backends therefore
return byte-identical row shapes.
on_connect hook timing
Choosing
- Default to
backend-core: a lighter dependency tree and the inline migration engine. This is what the example app uses. - Use
backend-dieselwhen the app already usesbetter-duck-diesel(shared schema / DSL /embed_migrations!) and wants a connection pool.
See also
- Checkpointing — WAL-flush strategies, identical across backends.
- Migrations — the one place the two builders’ APIs differ.
- Async & pooling — the pooling model the diesel backend builds on.