<div align="center">
<img src="https://raw.githubusercontent.com/shijianjs/duckfn/main/docs/static/img/duckfn-logo.svg" alt="duckfn logo" width="180" />
</div>
# duckfn
[English](https://github.com/shijianjs/duckfn/blob/main/duckfn/README.md) | [简体中文](https://github.com/shijianjs/duckfn/blob/main/duckfn/README.zh-CN.md)
[](https://github.com/shijianjs/duckfn)
[](https://shijianjs.github.io/duckfn/)
[](https://crates.io/crates/duckfn)
[](https://docs.rs/duckfn)
[](https://github.com/shijianjs/duckfn/blob/main/LICENSE)
[](https://zread.ai/shijianjs/duckfn)
**Write DuckDB extensions in plain Rust.**
`duckfn` is a runtime framework for building [DuckDB](https://duckdb.org) extensions on top of
DuckDB's C Extension API. Together with [`duckfn-macro`](https://crates.io/crates/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`](https://crates.io/crates/quack-rs) · [`libduckdb-sys`](https://crates.io/crates/libduckdb-sys)
- No `unsafe` to write: no `unsafe 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
```toml
[dependencies]
duckfn = "0.0.4"
# duckfn itself is built on these two crates; add them explicitly when you use
# their types or builders directly.
quack-rs = "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)
libduckdb-sys = { version = ">=1.4.4, <2", features = ["loadable-extension"] }
```
If you prefer the macros without the runtime, depend on
[`duckfn-macro`](https://crates.io/crates/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`):
```toml
duckfn = { version = "0.0.4", features = ["duckdb-1-5"] }
```
## Quick start
```rust
use duckfn::{duck_error, duck_scalar_function, duckfn_entrypoint, DuckOptionResult};
/// ```sql
/// SELECT double_it(21); -- 42
/// SELECT double_it(NULL); -- NULL
/// SELECT double_it(13); -- error: unlucky input
/// ```
#[duck_scalar_function]
pub fn double_it(v: Option<i64>) -> DuckOptionResult<i64> {
if v == Some(13) {
return Err(duck_error("unlucky input"));
}
Ok(v.map(|x| x * 2))
}
// Generate the extension entry point (name must be lowercase + underscores).
duckfn_entrypoint!("my_ext");
```
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](https://shijianjs.github.io/duckfn/docs/guide/attributes) | All attributes, shared arguments, and manual registration. |
| [Scalar functions](https://shijianjs.github.io/duckfn/docs/guide/scalar-functions) | Return shapes, `NULL` handling, overloads. |
| [Aggregate functions](https://shijianjs.github.io/duckfn/docs/guide/aggregate-functions) | Row handlers, state types, parallel aggregation. |
| [Table functions](https://shijianjs.github.io/duckfn/docs/guide/table-functions) | Row structs, named parameters, streaming. |
| [Copy functions](https://shijianjs.github.io/duckfn/docs/guide/copy-functions) | Custom file formats for `COPY ... TO` / `COPY ... FROM`, built on runtime dynamic columns. |
| [Type casts](https://shijianjs.github.io/duckfn/docs/guide/casts) | Overriding `CAST` for one source/target pair. |
| [Replacement scans](https://shijianjs.github.io/duckfn/docs/guide/replacement-scans) | Making `SELECT * FROM 'data.points'` work. |
| [SQL macros](https://shijianjs.github.io/duckfn/docs/guide/sql-macros) | Macros from Rust or from `.sql` files. |
| [Type mapping](https://shijianjs.github.io/duckfn/docs/guide/types) | DuckDB ↔ Rust types, nullability and known gaps. |
| [Errors and panics](https://shijianjs.github.io/duckfn/docs/guide/errors-and-panics) · [Architecture](https://shijianjs.github.io/duckfn/docs/internals/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](https://github.com/shijianjs/duckfn/blob/main/LICENSE).