Skip to content

Make the example functions self-documenting in duckdb_functions() - #173

Open
rustyconover wants to merge 1 commit into
duckdb:mainfrom
rustyconover:document-example-functions
Open

rustyconover wants to merge 1 commit into
duckdb:mainfrom
rustyconover:document-example-functions

Conversation

@rustyconover

@rustyconover rustyconover commented Sep 20, 2026 •

Copy link
Copy Markdown

Why

I loaded all 346 extensions in the community-extensions catalog and diffed duckdb_functions() before and after LOAD:

Distinct function names registered 5,483
...carrying a description 29.6%
Function-registering extensions exposing zero descriptions 80.7% (201 of 249)
Functions whose parameters are col0, col1 rather than real names 51%
Settings registered by those same extensions 496 — 100% documented

That last row is the interesting one. Settings hit 100% not because their authors are more diligent, but because DBConfig::AddExtensionOption(name, description, ...) takes the description as a required positional argument — you cannot register an undocumented setting. Meanwhile loader.RegisterFunction(ScalarFunction), the overload this template uses and therefore the one nearly every extension uses, has nowhere to put a description at all.

The template is the highest-leverage place to fix this: it's what almost every extension author starts from.

What changed

RegisterFunction(ScalarFunction) becomes RegisterFunction(CreateScalarFunctionInfo) with a FunctionDescription attached.

// before
auto waddle_scalar_function =
    ScalarFunction("waddle", {LogicalType::VARCHAR}, LogicalType::VARCHAR, WaddleScalarFun);
loader.RegisterFunction(waddle_scalar_function);

// after
CreateScalarFunctionInfo info(
    ScalarFunction("waddle", {LogicalType::VARCHAR}, LogicalType::VARCHAR, WaddleScalarFun));

FunctionDescription desc;
desc.parameter_names = {"name"};
desc.description = "Returns `name` prefixed with a waddling duck.";
desc.examples = {"waddle('Jane')"};
desc.categories = {"string"};
info.descriptions.push_back(std::move(desc));

// ALTER_ON_CONFLICT adds an overload if a function with this name already exists
info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT;
loader.RegisterFunction(std::move(info));

on_conflict is set explicitly because that's what RegisterFunction(ScalarFunction) does internally; without it the registration would default to ERROR_ON_CONFLICT and the template's behaviour would quietly change.

Three files, no submodule pin changes:

  • src/waddle_extension.cpp — both example functions now register with a description, parameter names, an example and a category.
  • test/sql/waddle.test — asserts the metadata reaches the catalog, including that parameters[1] is name and not col0.
  • docs/README.md — a short ### Documenting your functions subsection.

CreateAggregateFunctionInfo and CreateTableFunctionInfo carry the same descriptions field; the README says so, though the template has no aggregate or table function example to convert.

Payoff

duckdb_functions() stops being a list of names and becomes a usable interface. Anything introspecting a DuckDB instance to discover what it can call — a user who just ran INSTALL foo FROM community, tooling, or an agent — gets parameter names, a one-line summary and a runnable example instead of col0 and NULL.

Register the example scalar functions through CreateScalarFunctionInfo with a
FunctionDescription attached, so that duckdb_functions() reports a description,
real parameter names, a runnable example and a category for each one.

The plain RegisterFunction(ScalarFunction) overload has nowhere to put this
metadata, so every extension copied from this template starts out undocumented
in the catalog.

Also assert the metadata in the SQL test and document the pattern in the README.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rustyconover
rustyconover marked this pull request as ready for review September 20, 2026 16:16
Tmonster added a commit to samansmink/extension-template that referenced this pull request Sep 22, 2026
Install an AWS SDK HttpClientFactory (modeled on duckdb-aws PR duckdb#173) whose
client forwards every request to HTTPUtil, so Glue API calls use DuckDB's
proxy / certificate settings and appear in the HTTP log
(CALL enable_logging('HTTP')). Requests go through HTTPUtil::Request so
DuckDB's retry and logging apply, and the calling ClientContext is passed
via a thread-local scope around each Glue API call so the log lands on the
right connection. The 'glue_network_calls_via_duckdb' setting (session
aware) switches back to the SDK's own transport. Aws::InitAPI and the
factory registration now happen at extension load.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AoxZFk3q1KdM3vWEaMuDn5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant