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,
table or copy 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
- Host file system access: read and write through DuckDB's virtual file system (
s3://,http(s)://withhttpfs, in-memory) from any callback — aggregate functions included — plus one-line helpers such asduckfn::duck_vfs::read_string/write_string/append_string(duckdb-1-5feature) - Interop with other crates: the time wrapper types (
DuckDate,DuckTimestamp/_S/_Ms/_Ns,DuckTimestampTz,DuckTime) convert to and fromchrono,DuckUuid↔uuid, andDuckDecimal<W, S>↔rust_decimal(chrono/uuid/rust_decimalfeatures) — out-of-range values, DuckDB'sinfinityand digit-losing conversions come back as errors, never as a panic or a silent truncation - Works with DuckDB's official multi-platform extension CI
Status: early / experimental. APIs may change before
1.0.
Installation
[]
= "0.0.8"
# 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 four optional features. duckdb-1-5 enables what DuckDB's 1.5 C API added: the logical
types from DuckDB 1.5 (currently TIME_NS), copy functions, and host file-system access. The other
three are interop and independent of each other: chrono converts the time wrapper types to and from
chrono, uuid converts DuckUuid to and from
uuid, and rust_decimal converts DuckDecimal<W, S> to and from
rust_decimal — so the epoch / 128-bit / scaled-integer
arithmetic lives in duckfn rather than in every extension:
= { = "0.0.8", = ["duckdb-1-5", "chrono", "uuid", "rust_decimal"] }
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. |
| Copy functions | Custom file formats for COPY ... TO / COPY ... FROM, built on runtime dynamic columns. |
| 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.