feature = "udf", you can call your own Rust-authored DuckDB functions from the typed Diesel DSL —
no raw SQL. The pattern: register a #[duckdb_*] function on the underlying core connection, declare a
matching define_sql_function!, then call it as a typed function. Verified in the crate’s
tests/udf_dsl.rs.
The workflow
1
Author and register the UDF
Write a
#[duckdb_scalar] (or aggregate / table) function with
better-duck-core’s UDF macros, then register it on the core connection reached via
conn.inner_mut().2
Declare it to Diesel
Mirror the SQL name and DuckDB argument/result types with Diesel’s
define_sql_function!.3
Call it typed
Use the generated function in
.select / .filter like any built-in DSL function.When the UDF uses
name = "…", the generated registration type is named after the Rust function
(add_one_impl here), so you call add_one_impl::register(conn.inner_mut()). The define_sql_function!
name and signature must match the registered SQL name and DuckDB types.Aggregate and table UDFs
The same pattern covers aggregate and table functions. Aggregates authored with#[duckdb_aggregate]
are declared with define_sql_function! carrying the #[aggregate] attribute, then used in a grouped
.select like the built-in aggregates. See the
UDF macros page for authoring #[duckdb_scalar] / #[duckdb_aggregate] /
#[duckdb_table_function].
Per-connection registration
Binding custom-typed arguments
If a UDF takes a DuckDB-specific type (UBIGINT, HUGEINT, UUID, …), wrap literal arguments in the
matching values::* newtype and
.into_sql::<SqlType>() — bare foreign values can’t be used as expressions at a Duck* SQL type.
See also
UDF macros
Authoring
#[duckdb_scalar] and friends.Pooling
Register per connection with
on_connect.Aggregates
Built-in aggregates and custom ones.
Installation & setup
inner_mut() and the core connection.