duckfn
Write DuckDB extensions in plain Rust.
duckfn is a runtime framework for building DuckDB extensions on top of
DuckDB's C Extension API. Together with duckfn-macro,
a single attribute turns an ordinary Rust function into a DuckDB scalar, aggregate or
table function, a SQL macro, a replacement scan, a type cast, or a nested type — no C/C++ glue
code, and no local DuckDB build required.
- Repository: https://github.com/shijianjs/duckfn
- Built on
quack-rs·libduckdb-sys - No
unsafeto write: nounsafe fn, no raw pointers in your function bodies - No DuckDB build required, no C/C++ code
- Attribute-driven registration through
inventory - Panic-safe: Rust panics become DuckDB errors instead of unwinding across the FFI boundary
- Works with DuckDB's official multi-platform extension CI
Status: early / experimental. APIs may change before
1.0.
Installation
[]
= "0.0.3"
# duckfn itself is built on these two crates; add them explicitly when you use
# their types or builders directly.
= "0.16.0"
# `loadable-extension` dispatches through DuckDB's API table instead of linking
# libduckdb, which is what keeps a local DuckDB build unnecessary.
# (headers only — no linked library)
= { = ">=1.4.4, <2", = ["loadable-extension"] }
If you prefer the macros without the runtime, depend on
duckfn-macro directly; otherwise the macros are
re-exported by duckfn and no extra dependency is needed.
duckfn has a single feature, duckdb-1-5, which enables the logical types DuckDB added in 1.5
(currently TIME_NS):
= { = "0.0.3", = ["duckdb-1-5"] }
Quick start
use ;
/// ```sql
/// SELECT double_it(21); -- 42
/// SELECT double_it(NULL); -- NULL
/// SELECT double_it(13); -- error: unlucky input
/// ```
// Generate the extension entry point (name must be lowercase + underscores).
duckfn_entrypoint!;
The wrapper, the logical types and the registration are all generated, so the snippet above is
entirely safe Rust. The only place unsafe shows up is manual registration:
#[duck_custom_register] calls quack-rs' unsafe fn register_scalar / register_aggregate /
register_table.
Calling the generated module of a #[duck_scalar_function] exposes builders such as
scalar_function_builder() and scalar_overload_builder(), so you can register overloads or
function sets yourself.
Documentation
The full guide — every attribute and its arguments, the type mapping, the error model, and a runnable example extension — lives at https://shijianjs.github.io/duckfn/:
| Page | Contents |
|---|---|
| Attributes | All attributes, shared arguments, and manual registration. |
| Scalar functions | Return shapes, NULL handling, overloads. |
| Aggregate functions | Row handlers, state types, parallel aggregation. |
| Table functions | Row structs, named parameters, streaming. |
| Type casts | Overriding CAST for one source/target pair. |
| Replacement scans | Making SELECT * FROM 'data.points' work. |
| SQL macros | Macros from Rust or from .sql files. |
| Type mapping | DuckDB ↔ Rust types, nullability and known gaps. |
| Errors and panics · Architecture | Error handling, expansion, registration and adapters. |
中文文档:https://shijianjs.github.io/duckfn/zh-Hans/
Rust API reference: https://docs.rs/duckfn
License
Licensed under the MIT License.