1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// SPDX-License-Identifier: MIT
// Copyright 2026 Tom F. <https://github.com/tomtom215/>
// My way of giving something small back to the open source community
// and encouraging more Rust development!
//! # quack-rs
//!
//! A production-grade Rust SDK for building `DuckDB` loadable extensions.
//!
//! ## Overview
//!
//! `quack-rs` encapsulates the hard-won FFI knowledge required to build `DuckDB`
//! community extensions in Rust. It provides:
//!
//! - A correct, panic-free entry point helper via the [`entry_point`](mod@entry_point) module
//! - Type-safe builders for registering aggregate functions ([`aggregate`])
//! - Safe vector reading and writing helpers ([`vector`])
//! - A generic [`FfiState<T>`][aggregate::state::FfiState] that eliminates raw pointer management
//! - Documented solutions to every known `DuckDB` Rust FFI pitfall
//!
//! ## Quick Start
//!
//! ```rust,no_run
//! // In your extension's src/lib.rs, write the entry point manually:
//! use quack_rs::entry_point::init_extension;
//!
//! #[no_mangle]
//! pub unsafe extern "C" fn my_extension_init_c_api(
//! info: libduckdb_sys::duckdb_extension_info,
//! access: *const libduckdb_sys::duckdb_extension_access,
//! ) -> bool {
//! unsafe {
//! init_extension(info, access, quack_rs::DUCKDB_API_VERSION, |con| {
//! // register_my_function(con)?;
//! Ok(())
//! })
//! }
//! }
//! ```
//!
//! ## Architecture
//!
//! The SDK is organized into focused modules:
//!
//! | Module | Purpose |
//! |--------|---------|
//! | [`callback`] | Safe `extern "C"` callback wrapper macros (`scalar_callback!`, `table_scan_callback!`) |
//! | [`chunk_writer`] | Auto-sizing chunk writer for table scan callbacks (auto `set_size` on drop) |
//! | [`data_chunk`] | Ergonomic wrapper for `DuckDB` data chunks |
//! | [`entry_point`](mod@entry_point) | Helper for the correct `{name}_init_c_api` C entry point |
//! | [`connection`] | `Connection` facade + `Registrar` trait for version-agnostic registration |
//! | [`aggregate`] | Builders for aggregate function registration |
//! | [`scalar`] | Builder for scalar function registration |
//! | [`cast`] | Builder for custom type cast functions |
//! | [`table`] | Builders for table function registration (raw `TableFunctionBuilder` + closure-based `TypedTableFunctionBuilder<S>`) |
//! | [`replacement_scan`] | `SELECT * FROM 'file.xyz'` replacement scan registration |
//! | [`sql_macro`] | SQL macro registration (`CREATE MACRO`) — no FFI callbacks |
//! | [`vector`] | Safe helpers for reading/writing `DuckDB` data vectors |
//! | [`vector::complex`] | STRUCT / LIST / MAP / ARRAY vector access (child vectors, offsets) |
//! | [`vector::struct_reader`] | Batched [`StructReader`][vector::StructReader] for STRUCT input vectors |
//! | [`vector::struct_writer`] | Batched [`StructWriter`][vector::StructWriter] for STRUCT output vectors |
//! | [`types`] | `DuckDB` type system wrappers (`TypeId`, `LogicalType`) |
//! | [`interval`] | `INTERVAL` → microseconds conversion with overflow checking |
//! | [`error`] | `ExtensionError` for FFI error propagation |
//! | [`config`] | RAII wrapper for `DuckDB` database configuration |
//! | [`value`] | RAII wrapper for `DuckDB` values with typed extraction |
//! | [`tls`] | Type-erased TLS configuration provider for HTTP-capable extensions |
//! | [`warning`] | Structured security warning API (`ExtensionWarning`, `WarningCollector`) |
//! | [`secrets`] | Secrets manager bridge trait (`SecretsManager`, `SecretEntry`) |
//! | [`validate`] | Community extension compliance validators |
//! | [`validate::description_yml`] | Parse and validate `description.yml` metadata |
//! | [`scaffold`] | Project generator for new extensions (no C++ glue needed) |
//! | [`testing`] | Test harness for aggregate state logic |
//! | [`prelude`] | Convenience re-exports of the most commonly used items |
//! | `catalog` | Catalog entry lookup (`duckdb-1-5` feature) |
//! | `client_context` | Client context access (`duckdb-1-5` feature) |
//! | `config_option` | Extension-defined configuration options (`duckdb-1-5` feature) |
//! | `copy_function` | Custom `COPY TO` handlers (`duckdb-1-5` feature) |
//! | `table_description` | Table metadata queries (`duckdb-1-5` feature) |
//!
//! ## Safety
//!
//! All `unsafe` code within this SDK is sound and documented. Extension authors
//! must write `unsafe extern "C"` callback functions (required by `DuckDB`'s C API),
//! but the SDK's helpers minimize the surface area of unsafe code within those
//! callbacks. Every `unsafe` block inside this crate has a `// SAFETY:` comment
//! explaining the invariants being upheld.
//!
//! ## Design Principles
//!
//! 1. **Thin wrapper**: every abstraction must pay for itself in reduced boilerplate
//! or improved safety. When in doubt, prefer simplicity.
//! 2. **No panics across FFI**: `unwrap()` is forbidden in FFI callbacks and entry points.
//! 3. **Bounded version range**: `libduckdb-sys` uses `>=1.4.4, <2` to support `DuckDB` 1.4.x
//! and 1.5.x (including v1.5.1) while preventing silent adoption of breaking changes in
//! future major releases.
//! 4. **Testable business logic**: state structs have zero FFI dependencies.
//!
//! ## Pitfalls
//!
//! See [`LESSONS.md`](https://github.com/tomtom215/quack-rs/blob/main/LESSONS.md)
//! for all 16 known `DuckDB` Rust FFI pitfalls, including symptoms, root causes, and fixes.
//!
//! ## Pitfall L1: COMBINE must propagate config fields
//!
//! `DuckDB`'s segment tree creates fresh zero-initialized target states via
//! `state_init`, then calls `combine` to merge source into them. This means
//! your `combine` callback MUST copy ALL configuration fields from source to
//! target — not just accumulated data. Any field that defaults to zero will
//! be wrong at finalize time, producing silently incorrect results.
//!
//! See [`aggregate::callbacks::CombineFn`] for details.
// DuckDB's C API and duckdb_string_t layout assume 64-bit pointers.
compile_error!;
// DuckDB 1.5.0+ modules — gated behind the `duckdb-1-5` feature flag.
/// The `DuckDB` C API version string required by [`duckdb_rs_extension_api_init`][libduckdb_sys::duckdb_rs_extension_api_init].
///
/// This constant corresponds to `DuckDB` releases v1.4.x, v1.5.0, and v1.5.1.
/// The C API version did **not** change between v1.5.0 and v1.5.1. If you are
/// targeting a different `DuckDB` release, consult the `DuckDB` changelog for the
/// C API version.
///
/// # Pitfall P2: C API version ≠ `DuckDB` release version
///
/// The `-dv` flag passed to `append_extension_metadata.py` must be this value
/// (`"v1.2.0"`), **not** the `DuckDB` release version (`"v1.4.4"` / `"v1.5.0"` /
/// `"v1.5.1"`). Using the wrong value causes the metadata script to fail silently
/// or produce incorrect metadata.
///
/// See `LESSONS.md` → Pitfall P2 for full details.
pub const DUCKDB_API_VERSION: &str = "v1.2.0";