Skip to main content

One Rust function = one SQL function

The attribute generates the FFI wrapper, the column readers and writers, and the registration code. Everything on the left is safe Rust that you could have written for a plain library.

src/lib.rs
use duckfn::{
duck_error, duck_scalar_function, duckfn_entrypoint, DuckOptionResult,
};

/// The SQL below is what this function becomes, doc comment and all.
#[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))
}

// The symbol DuckDB looks for when it loads the extension.
duckfn_entrypoint!("my_ext");
duckdb -unsigned
-- the extension loads like any other
LOAD './my_ext.duckdb_extension';

SELECT double_it(21); -- 42
SELECT double_it(NULL); -- NULL
SELECT double_it(13); -- error: unlucky input

The comments are what each call returns. Loading needs -unsigned, because the extension talks to DuckDB's C API.

Same functions, two ways: four of them written both ways