dbt-duckhaven
A dbt adapter that runs your DuckDB models on DuckHaven
compute through the DuckHaven API. Instead of dbt
opening its own in-process DuckDB, every statement is routed through the DuckHaven
session API, which dispatches it to an agent. dbt authenticates as a DuckHaven
service-account personal access token (PAT), and identifies itself to the server via the
connector's User-Agent (dbt-duckhaven/<version>), so a dbt run is fully governed and
attributable.
It reuses dbt-duckdb wholesale — the DuckDB
dialect, the macros, and the Iceberg-aware materializations — and swaps only the
connection layer for the duckhaven-sql-connector session
client.
Install
pip install dbt-duckhaven
Configure (profiles.yml)
my_project:
target: dev
outputs:
dev:
type: duckhaven
host: https://duckhaven.internal # DuckHaven API base URL
workspace: analytics # DuckHaven workspace slug
token: "{{ env_var('DUCKHAVEN_PAT') }}" # a DuckHaven PAT (dh_pat_…)
agent: 00000000-0000-0000-0000-000000000000 # optional agent UUID; omit to auto-pick
catalog: sales # required — dbt "database" → Polaris catalog
schema: analytics # dbt "schema" → Polaris namespace
threads: 4
host, workspace, token, and catalog are required; agent is optional (omit to let
the API pick a compatible connected agent). dbt init scaffolds these prompts for you.
If the DuckHaven deployment runs elastic compute and has scaled to zero, the first
connection of a run now waits while an agent starts — up to five minutes — instead of
failing with Failed to connect. Later connections in the same run reuse the warm agent,
so the cost is paid once. A run against a deployment that cannot start compute at all
still fails immediately rather than waiting out that budget.
A DuckHaven server can restrict which agents a profile may target, and a denial fails the
run at connection time with Failed to connect. If you named an agent and see "Agent
not found", the id may be right but restricted to people who have been granted it —
a restricted agent you hold no grant on is hidden rather than reported as forbidden, so it
reads identically to a deleted one. "requires the 'use' tier" means the opposite: the
agent is visible to you but your grant is too low. Omitting agent narrows auto-pick to
agents you may use, so it can report no agent available rather than falling back to one
you cannot run on.
What works
Materializations: table, seed, incremental, ephemeral, and snapshots (both the
timestamp and check strategies). Generic and singular tests.
Incremental strategies
append, delete+insert, merge, and microbatch.
merge and microbatch require the agent's DuckDB to be 1.5.3 or newer — that is when
duckdb-iceberg gained MERGE INTO for Iceberg tables. The adapter checks the agent's
version and simply won't offer the strategies below that, so you get a clear dbt error
instead of a mid-run failure.
merge emits the explicit MERGE INTO … WHEN MATCHED THEN UPDATE SET … WHEN NOT MATCHED THEN INSERT … form that duckdb-iceberg documents, rather than dbt-duckdb's DuckDB-native
UPDATE BY NAME / INSERT BY NAME. merge_update_columns, merge_exclude_columns, and
incremental_predicates work as usual. dbt-duckdb's DuckDB-only extras — merge_clauses,
merge_returning_columns, merge_on_using_columns, merge_update_condition,
merge_insert_condition, merge_update_set_expressions — raise a compile-time error
rather than being silently ignored, because they depend on syntax Iceberg does not support.
Because Iceberg writes are merge-on-read, a merge target must leave write.update.mode /
write.delete.mode at merge-on-read; DuckDB fails the statement otherwise.
Microbatch batches run serially, by design: concurrent batches would each take their
own session (an agent admission slot) and race each other to commit to the same Iceberg
table. Size batch_size so one batch finishes inside the server's 600s per-statement
limit. Note that each batch leaves a session-local temp table behind for the life of the
run (dbt-duckdb's incremental materialization only cleans those up on MotherDuck), and a
session has a fixed 256 MiB budget — so very high batch counts in one run are untested.
Not supported (yet)
viewmaterialization — DuckDB's Iceberg REST catalog does not implementCREATE VIEW, so view models fail. Usetableinstead.- Python models — DuckHaven agents execute SQL only; a Python model fails clearly.
- Re-running
dbt seedover an existing seed, on an older server — dbt's seed reset emitsTRUNCATE TABLE. DuckHaven's statement policy admits it, but only since the release that added it; against a server predating that, the statement is rejected and you needdbt seed --full-refresh, which drops and recreates instead. The adapter does not detect which case you are in: the server'sGET /api/versionreports a build version and a coarse API-contract number, butTRUNCATEadmission was an additive change that moves neither, and there is no feature registry to check — so the adapter cannot choose for you. Note that on IcebergTRUNCATEis not the cheap metadata-only operation the name suggests — it writes positional delete files just as the equivalentDELETEwould. on-schema-changefor incremental models — the ALTER-driven flow is untested here. (Snapshots do handle a new column: that path is covered.)externalmaterialization / dbt-duckdb source plugins — out of scope.
Model constraints
Polaris/Iceberg does not enforce relational constraints. In model contracts, not_null is
enforced (rejected on write), while primary_key, foreign_key, and unique are
documentation-only (not enforced), and check constraints are not supported. The
adapter advertises this to dbt so contracts don't promise enforcement the backend can't
provide.
Server requirement. The adapter needs a DuckHaven build whose SQL session/statement API is enabled and whose statement policy admits
DESCRIBE(used for column metadata).
Concurrency
threads: N opens N DuckHaven sessions, each holding one agent admission slot for
the duration of the run. There is no deadlock, but keep threads ≤ the agent's
admission capacity to avoid queueing/starvation. threads: 2–4 is a good default.
Cancelling a run (Ctrl-C, or a failure with --fail-fast) cancels the in-flight statement
on each session, so the agent's admission slots are freed promptly instead of running the
abandoned queries to completion.
License
Apache-2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbt_duckhaven-0.4.0.tar.gz.
File metadata
- Download URL: dbt_duckhaven-0.4.0.tar.gz
- Upload date:
- Size: 22.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cce6f8a405b5bbd8755cd51d2bf92f63c474fedf1972f4e62c38e5303d54cffc
|
|
| MD5 |
5346b7448f40bc632012c715daaf263b
|
|
| BLAKE2b-256 |
6db7fe81806f375b76a9c644dbb033fe75ef3dd7b6e8dd10febb0fd6dcd2e48e
|
Provenance
The following attestation bundles were made for dbt_duckhaven-0.4.0.tar.gz:
Publisher:
release.yml on tamasmrtn/duckhaven-clients
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbt_duckhaven-0.4.0.tar.gz -
Subject digest:
cce6f8a405b5bbd8755cd51d2bf92f63c474fedf1972f4e62c38e5303d54cffc - Sigstore transparency entry: 2311413844
- Sigstore integration time:
-
Permalink:
tamasmrtn/duckhaven-clients@4f63c7f2857a5423a9db55ed2601ff71e2d76dc2 -
Branch / Tag:
refs/tags/dbt-duckhaven-v0.4.0 - Owner: https://github.com/tamasmrtn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f63c7f2857a5423a9db55ed2601ff71e2d76dc2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dbt_duckhaven-0.4.0-py3-none-any.whl.
File metadata
- Download URL: dbt_duckhaven-0.4.0-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c4c3e22f7dc084beb862b541fe32e704ca5221abadf96b7a40c50fa32acc1af
|
|
| MD5 |
5130d4687a28db10dcf658d78dd3d260
|
|
| BLAKE2b-256 |
c4403e8b3a964b312e5ac1de1b366bdec39fb085b08b020387a3754ee16767da
|
Provenance
The following attestation bundles were made for dbt_duckhaven-0.4.0-py3-none-any.whl:
Publisher:
release.yml on tamasmrtn/duckhaven-clients
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbt_duckhaven-0.4.0-py3-none-any.whl -
Subject digest:
3c4c3e22f7dc084beb862b541fe32e704ca5221abadf96b7a40c50fa32acc1af - Sigstore transparency entry: 2311413860
- Sigstore integration time:
-
Permalink:
tamasmrtn/duckhaven-clients@4f63c7f2857a5423a9db55ed2601ff71e2d76dc2 -
Branch / Tag:
refs/tags/dbt-duckhaven-v0.4.0 - Owner: https://github.com/tamasmrtn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f63c7f2857a5423a9db55ed2601ff71e2d76dc2 -
Trigger Event:
push
-
Statement type: