quack-rs: DuckDB extensions in Rust

A Rust SDK for building DuckDB loadable extensions — no C or C++ to write.

CI Crates.io Documentation License: MIT MSRV: 1.86.0


What is quack-rs?

quack-rs is a Rust SDK for building DuckDB loadable extensions without writing C++ or CMake. It wraps DuckDB's C Extension API — the C interface DuckDB exposes to loadable extensions — in safe builders and RAII types, and guards against the FFI pitfalls documented in the Pitfall Catalog, so you can focus on extension logic.

DuckDB's community-extensions FAQ still says that "Currently, writing a Rust-based DuckDB extension requires writing glue code in C++", and that a pure-Rust path is in development (DuckDB Community Extensions FAQ). That path now exists in two forms. DuckDB's own extension-template-rs targets the unstable C API v2 with the experimental duckdb-neo crate. quack-rs targets the stable C API v1, so one binary loads across DuckDB releases. Your repository holds Rust only: no C++, no CMake, and no glue layer of your own between your functions and the C API. (Some of the tools around it still build C: see Build requirements.)


What you can build

Extension typequack-rs support
Scalar functions✅ ScalarFunctionBuilder
Overloaded scalars✅ ScalarFunctionSetBuilder
Aggregate functions✅ AggregateFunctionBuilder
Overloaded aggregates✅ AggregateFunctionSetBuilder (per-overload return types)
Table functions✅ TableFunctionBuilder (raw) + TypedTableFunctionBuilder<S> (closure-based, typed scan state)
Cast / TRY_CAST functions✅ CastFunctionBuilder
Replacement scans✅ ReplacementScanBuilder
SQL macros (scalar)✅ SqlMacro::scalar
SQL macros (table)✅ SqlMacro::table
Copy functions (COPY TO / COPY FROM)✅ CopyFunctionBuilder (requires duckdb-1-5)

Note: Window functions have no counterpart in DuckDB's public C Extension API and cannot be implemented from Rust (or any language) via that API. See Known Limitations.


Why does this exist?

quack-rs was extracted from duckdb-behavioral, a production DuckDB community extension. Building that extension revealed the first undocumented pitfalls in DuckDB's Rust FFI surface — struct layouts, callback contracts, and initialization sequences that aren't covered anywhere in the DuckDB documentation or libduckdb-sys docs. The catalogue has grown since, from building and auditing quack-rs itself.

Three of those pitfalls caused extension-breaking bugs that passed 435 unit tests before being caught by end-to-end tests:

  1. A SEGFAULT on load (wrong entry point sequence)
  2. 6 of 7 functions silently not registered (undocumented function-set naming rule)
  3. Wrong aggregate results under parallel plans (combine callback not propagating configuration fields to fresh target states)

quack-rs rules out the first two: entry_point! performs the correct initialization sequence, and the function-set builders name every member. The third lives in your own combine callback, so no API can prevent it; AggregateTestHarness::combine lets you test for it without DuckDB. The full list is in the Pitfall Catalog.


Key features

  • No C++ in your repository — no CMakeLists.txt, no header files, no glue layer of your own
  • Every function kind the C API can register — scalar, aggregate, table, cast, replacement scan, copy function (duckdb-1-5) — plus SQL macros
  • Panic-safe FFI — the entry point and the callbacks quack-rs generates catch panics and report them as SQL errors; registration errors surface via Result
  • RAII memory management — LogicalType and FfiState<T> prevent leaks and double-frees
  • Type-safe builders — ScalarFunctionBuilder, AggregateFunctionBuilder, TableFunctionBuilder, CastFunctionBuilder, ReplacementScanBuilder
  • SQL macros — register CREATE MACRO statements without any FFI callbacks
  • Testable state — AggregateTestHarness<T> tests aggregate logic without a live DuckDB
  • Scaffold generator — generates a complete community extension project (Cargo.toml, Makefile, CI, description.yml, tests) from one function call
  • 38 pitfalls documented — every known DuckDB Rust FFI pitfall, with symptom, root cause and fix

New to DuckDB extensions? → Start with Quick Start

Adding quack-rs to an existing project? → See Installation

Writing your first function? → See Scalar Functions or Aggregate Functions

Want SQL macros without FFI callbacks? → See SQL Macros

Submitting a community extension? → See Community Extensions

Something broke? → See Pitfall Catalog