Quick start
The whole page in three steps:
Prerequisites
-
Rust 1.86 or newer (
rust-versioninCargo.toml). -
just — the tool the recipes call:
cargo install just -
make (inside Git Bash on Windows) and Python 3 — the official DuckDB
extension-ci-toolsbuild/test flow, whichjust buildruns under the hood (make configure+make debug).just testuses the same flow. -
A DuckDB binary 1.4 or newer (
duckdbonPATH, or point at it withjust DUCKDB=/path/to/duckdb …).
1. The extension name
This repository is duckfn_palette and the name is already consistent in the five places that have to
agree — Cargo.toml ([package] name and [[example]] name), EXTENSION_NAME in the Makefile, the
entry-point symbol in src/extension/mod.rs, the Justfile and the CI workflow. Keep it that way: editing
one of them alone makes LOAD fail on the symbol lookup.
A project freshly created from duckfn-extension-template
runs just rename <new_name> (scripts/rename.sh), which rewrites all five plus every occurrence in the
docs, and regenerates the Cargo.lock entry. What still needs a human pass afterwards:
AGENTS.md— fill in the{{PROJECT_GOAL}}placeholder and the function-name prefix.LICENSE— the copyright holder.community-extension/description.yml—extension.name/description/maintainers/repo; the field-by-field reasoning is in community extensions.- Before the first release, make sure the repository has a
mainbranch and anoriginremote:just release_tagpushes both.
2. Build
just build # = make configure && make debug
The artifact is build/debug/duckfn_palette.duckdb_extension. There is no C++ step and no local DuckDB
build: the extension is compiled against DuckDB's headers and dispatches through its API table when it
is loaded.
3. Load and call it
just repl # a DuckDB REPL with the extension already loaded
-- or by hand; -unsigned is required for a locally built extension
duckdb -unsigned -c "LOAD './build/debug/duckfn_palette.duckdb_extension';"
Some of the functions, running right here — the site preloads the extension from the repository's latest
release, so no local LOAD is needed here (a hand-built extension still needs -unsigned; see the
traps below). Click Run on any block.
SELECT plt_to_hex(plt_convert(plt_srgb(1, 0.5, 0), 'oklch')) AS as_oklch,
plt_to_hex(plt_mix(plt_named('red'), plt_named('blue'), 0.5)) AS mixed;
-- A NULL argument is short-circuited to NULL before the function body ever runs.
SELECT plt_to_hex(plt_convert(plt_srgb(1, 0, 0), 'lab')) AS converted,
plt_convert(NULL, 'lab') IS NULL AS null_in_null_out;
The failure path is a runnable block too — it declares that it is supposed to fail:
SELECT plt_convert(plt_srgb(1, 0, 0), 'nope'); -- error: unknown color space
A single query from the command line, without a REPL:
just sql "SELECT plt_to_hex(plt_named('rebeccapurple'))"
4. Run the tests
just test # make configure + make debug + make test
The faster loop is in Testing.
Traps
-unsignedis mandatory when you load a locally built extension. Without it DuckDB refuses the file.- The artifact file name must stay
<extension name>.duckdb_extension. DuckDB finds the entry-point symbol through the file name, so a copy calledwin.duckdb_extensionfails withdid not contain function "duckfn_palette_init_c_api". make testdoes not rebuild. After changing Rust code runjust ci-build(ormake debug) first, otherwise the tests run against the previous artifact.
One more, on Windows: if make debug reports the artifact is in use, a DuckDB process is
holding build/debug/duckfn_palette.duckdb_extension (usually a just repl left open) — close that
process and rebuild. A .duckdb_extension is not a renamed DLL: DuckDB's metadata lives at the end of
the file, so copying a DLL over it produces The metadata at the end of the file is invalid.