Contribute a dbt Core v2 adapter
This guide is a work in progress — synthesized from internal Fusion v2 adapter team docs, DuckDB/ClickHouse reference implementations, the Exasol community adapter, and the v1 adapter creation guide.
Community contribution scope: You are responsible for Phase 1 (foundation) only — connecting, running macros, and basic materializations. Phase 2 (SQL static analysis) is led by dbt Labs after your Phase 1 PR is merged.
Step 1: Introduction
dbt Core v2 adapters work very differently from dbt Core v1 adapters. In v1, each adapter was a standalone Python package that implemented a fragmented Python interface. In dbt Core v2, adapters live inside a monorepo written in Rust, connected to warehouses via ADBC (Arrow Database Connectivity) drivers — and the community contribution model has changed accordingly.
This guide walks you through contributing a new dbt Core v2 adapter to dbt-core as a community member. The file breakdown in the reference section shows the ~13 files a complete community dbt Core v2 adapter touches. Exasol, a community contributed dbt Core v2 adapter, is the example used throughout this guide.
dbt Core v2 is the new Rust-based dbt engine. Adapters in Core v2 are written in Rust and live inside the dbt-core monorepo, rather than as standalone Python packages.
How adapters are different now
In dbt Core v1, every adapter is:
- A separate Python package (e.g.
dbt-snowflake,dbt-bigquery) - Community-owned and maintained independently
- Dynamically loaded at runtime via Python's plugin system
- Built upon vendor-maintained Python SDK connectors/drivers
In dbt Core v2, adapters are:
- Part of the
dbt-coremonorepo, contributed as PRs - Written in Rust (with Jinja SQL macros still used for SQL logic)
- Connected to warehouses via ADBC drivers — a unified driver interface that abstracts away connection management
- Organized by vertical (feature area) rather than by warehouse — meaning a bug fix in one crate often benefits all adapters
The practical upside: adding a new adapter requires far less code than it did in v1, and the cost of development decreases as more adapters are added because shared logic increases.
What you'll be building
As a community contributor, you're building the foundation: connect your warehouse, run dbt macros, and support basic materializations (dbt run, dbt build). The ~13 files in the reference section define the complete scope.
Step 2: Prerequisites
Background knowledge
Before starting, you should:
- Have some Rust familiarity — you do not need to be a Rust expert (an LLM will likely do most of the code writing). You need to be able to read Rust, understand enum match expressions, and make sense of compiler errors. If Rust is new to you, skim The Rust Book Chapter 6 (enums and pattern matching) — those are the concepts you'll encounter most.
- Understand dbt fundamentals — how
profiles.ymlworks, what materializations are, and what adapter dispatch macros do. - Understand your query engine and how it differs from others. The following questions cover the key considerations for building an adapter:
- What character is used for quoting identifiers?
- Are three-part names supported, or only two?
- Are transactions supported?
- What's the most performant way to get all the columns in a schema?
- What datatype represents a string?
- What connection fields does your warehouse require?
- Have an LLM coding assistant available (optional) — the adapter development workflow is designed with AI-assisted coding in mind, and this guide calls out where to use it.
Your ADBC driver
dbt Labs does not write drivers. If your warehouse doesn't have an ADBC driver yet, that work comes first — it's a prerequisite you can't skip.
What "having an ADBC driver" actually means
For certain adapters (Snowflake, BigQuery, Databricks, Redshift, DuckDB, ClickHouse, Salesforce, Spark, SQL Server), dbt Core v2 automatically downloads the correct driver binary from the dbt Labs CDN on first use. Users never have to think about it.
Community adapters don't have CDN support. Instead, Fusion looks for a shared library by name on the user's system — e.g. libadbc_driver_exasol.dylib on macOS, libadbc_driver_exasol.so on Linux. If the file isn't present, the connection fails at runtime.
This means two things for you as a contributor:
- The driver binary must exist somewhere. It's a compiled shared library (
.dylib/.so/.dll) that implements the ADBC C ABI for your warehouse. Exasol's driver isexarrow-rs, a separate Rust crate maintained by Exasol — not part ofdbt-coreand not inapache/arrow-adbc. Your warehouse's driver will likely live in a similar separate repo. - Your users will need to install it manually. Unlike first-party adapters, there's no auto-download. Your documentation (Step 6) needs to tell users where to get the driver and how to install it.
Check whether a driver already exists for your warehouse:
- dbt-labs/arrow-adbc — the dbt Labs fork
- apache/arrow-adbc — the upstream community repo
- adbc-drivers — the ADBC driver foundry, the primary community home for ADBC drivers, with active drivers for Trino, MySQL, BigQuery, Databricks, Snowflake, and more; also includes Rust/Go frameworks (
driverbase-rs,template-rs) for building new drivers from scratch - The warehouse vendor's own GitHub org (many vendors publish ADBC drivers independently)
Fusion loads drivers by shared library name from the system path (e.g. libadbc_driver_exasol.dylib on macOS) — the source repository is flexible, as long as the binary implements the ADBC C ABI. dbt Labs is working on a driver signing and verification mechanism. Until that ships, users are responsible for trusting the driver binary they install.
If no driver exists yet, building one is a separate project that comes before the adapter contribution. This is outside the scope of what dbt Labs can help with. Columnar specializes in building ADBC drivers and may be a useful resource if you need help getting a driver built.
Are you porting an existing v1 adapter?
Skip to the checklist below, then go straight to Step 4. You won't have macro SQL or connection logic to port, but the rest of the guide (crate structure, Rust patterns, AI workflow) still applies.
Most community contributors aren't building from scratch — they're porting an adapter they already maintain or use in Python for dbt Core v1. If that's you, you have a significant head start, and this is the most realistic path for the vast majority of contributors.
Before writing any Rust, check a few things:
1. Does a v1 adapter already exist for your warehouse?
Check the trusted adapters and community adapters lists. If one exists, find its GitHub repo — the macro SQL and connection logic are almost directly reusable.
2. Did dbt Labs already add a placeholder for your warehouse?
Some warehouses already appear in v2's AdapterType enum but aren't fully implemented yet — the enum variant exists, which means the boilerplate is partially in place. When you add the remaining code, the compiler shows you exactly what's still missing. The warehouses in this state are:
| Loading table... |
For warehouses not yet in AdapterType at all (MySQL, Hive, Vertica, SQL Server, Teradata, etc.), you start from Step 4.1 by adding the AdapterType variant.
What transfers from v1 to v2
| Loading table... |
What doesn't transfer by design
A few components won't transfer 1:1 from v1 to dbt Core v2 — meaning less code and maintenance for you:
ConnectionManagermethods (open,cancel,get_response,execute) — In v1, these were your responsibility to implement against the Python DB API 2.0 spec. In v2, connection management is owned entirely by the ADBC driver.- Python adapter class hierarchy and execution-wrapping methods — now handled by
match adapter_type()expressions in the sharedadapter_impl.rs(Step 4.5 explains how these work). setup.py/ package dependencies ondbt-core— dbt Labs handles packaging and distribution once your PR is merged. Nosetup.py, no PyPI release, and no version pinning required.
How to use the v1 adapter with AI
The Jinja macros are the biggest asset. Feed your LLM your v1 macros/adapters.sql alongside the reference adapters.sql (in the file breakdown at the bottom of this guide) and ask it to port the macros into the Fusion structure. The SQL logic is the same; you're confirming the dialect is right and the system catalog table names match.
For profile config and auth, feed your LLM your v1 credentials.py or profile_template.yml alongside the reference DbConfig struct and auth module (see the file breakdown below). Field names and connection parameters usually map directly.
The most common mismatch: v1 connections.py has a lot of Python connection-management code that has no equivalent in Core v2 — the driver handles all of that. Focus only on URI/DSN construction and credential fields — skip anything related to cursors, retries, or connection pooling.
Dev machine setup
# Rust
rustup # install from https://rustup.rs
rustup show # verify
# Go (needed for driver builds)
go version # verify
# Clone the repo
git clone https://github.com/dbt-labs/dbt-core
cd dbt-core
# Verify you can build
cargo build --bin dbt
If you hit Z3 errors:
brew install pkg-config z3
If disk fills during build:
cargo clean # frees old build artifacts — you'll do this often
Development workflow
The core development loop is the same whether you write code manually or with an AI assistant:
- Add
AdapterType::MyWarehouseto the enum (Step 4.1) - Run
cargo check -p <crate>— the compiler lists every match arm missing for your new variant - Use the compiler output + the file you're editing + the equivalent reference file (from the breakdown below) to write each arm. If using an AI assistant, paste all three in as context.
- Example: to fill in the
quote_chararm, paste the compiler error listingAdapterType::MyWarehouseas missing, thequote_charmatch block fromdbt-adapter-core/src/lib.rs, and the Exasol line (Exasol => '"') as the pattern to follow.
- Example: to fill in the
- Fill in the arm; verify with
cargo checkagain - Repeat for each crate until error-free
Every missing case is a compile error, so the AI always has a precise specification to work from.
What context helps for each arm:
- The file you're editing — paste the relevant
matchblock or function. For example: thematch self.adapter_type()block fromadapter_impl.rsthat needs a new arm. - The equivalent reference file from the breakdown below. For example: when writing your auth module, paste
crates/dbt-auth/src/exasol/mod.rsas the pattern. - The compiler error output. For example:
error[E0004]: non-exhaustive patterns: AdapterType::MyWarehouse not covered - Your warehouse's specifics — system catalog table names and the connection fields from your
profiles.yml.
Watch out for:
- Hallucinated file paths — AI often invents Fusion paths. Use the file breakdown below as ground truth.
- Always verify with the type checker — run
cargo check -p <crate>after any AI-generated changes. - SQL macro patterns from v1 may not apply cleanly in Fusion. Compare against the reference
adapters.sqlincrates/dbt-loader/src/dbt_macro_assets/dbt-exasol/macros/adapters.sqlin dbt-labs/dbt-fusion.
Step 3: Understand the architecture
Before writing code, it helps to understand the layers you'll be working in.
Why ADBC?
A key architectural decision in Core v2 is the use of ADBC (Arrow Database Connectivity) as the unified driver interface.
In dbt Core v1, adapters connected via Python drivers — often wrapping pyodbc or proprietary connection mechanisms. Each adapter owned its connection logic entirely.
In v2, each warehouse connects through an ADBC driver — a pre-compiled binary that handles the wire protocol, authentication handshakes, and connection pooling. Your adapter code never touches any of that. For CDN-supported first-party adapters, Fusion downloads this driver automatically on first use. For community adapters, users install it manually — which is why Step 2 covers finding or building a driver.
ADBC is column-native end-to-end — if your warehouse supports columnar output (e.g. Arrow IPC or Arrow Flight SQL), data flows through with zero conversion. This means you do not need to write connection management code — that lives in the driver. What you write is the warehouse-specific configuration, authentication, relation naming, macro logic, and catalog introspection that sits above the driver.
The vertical model
Unlike v1 where each warehouse had its own monolithic adapter class (SnowflakeAdapter, BigqueryAdapter), Core v2 organizes adapter logic by vertical — feature areas that span all warehouses:
dbt-xdbc— driver loading, ADBC connections (all warehouses)dbt-auth— credential resolution (all warehouses, per-warehouse modules)dbt-adapter— a singleConcreteAdapterwith warehouse-specific behavior driven bymatch adapter_type(), not per-warehouse structsdbt-loader— Jinja SQL macros per warehouse
When you add a new warehouse, you add a variant to each vertical, not a new top-level package. The compiler enforces this: if you add AdapterType::MyWarehouse and any match adapter_type() block doesn't handle it, Rust won't compile. For example, if adapter_impl.rs has a match self.adapter_type() that covers Postgres, Snowflake, Exasol, etc. but not MyWarehouse, the compiler reports an error at that exact line — so you always know what's left.
This is a meaningful improvement over v1's class inheritance model. In v1, a new adapter subclass would silently inherit parent behavior for any method you didn't explicitly override — which could mask bugs where a warehouse quietly used the wrong default. In v2 there's no inheritance and no implicit behavior. Every function that varies by warehouse requires an explicit match arm, so nothing falls through silently.
For example:
error[E0004]: non-exhaustive patterns: `AdapterType::MyWarehouse` not covered
--> crates/dbt-adapter/src/adapter/adapter_impl.rs:142:18
|
| match self.adapter_type() {
| ^^^^^^^^^^^^^^^^^^^ pattern `AdapterType::MyWarehouse` not covered
Layer stack
The diagram below shows how a dbt project request flows through the crates at runtime — from configuration to SQL execution. Your adapter work lives in the middle layers: profile config, credential resolution, driver loading, relation logic, and macros. The bottom layer (SQL execution against the warehouse) is owned by dbt Labs.
profiles.yml / dbt_project.yml
│
▼
dbt-schemas ← profile config struct for your warehouse
│
▼
dbt-auth ← credential resolution
│
▼
dbt-xdbc ← ADBC driver loading
│
▼
dbt-adapter ← relation types, metadata adapter, adapter methods
│
dbt-loader ← Jinja SQL macros (materializations, catalog, utils)
│
▼
[dbt internal] ← SQL execution against warehouse (dbt Labs)
Crate map
In Rust, a crate is a package — the unit of compilation, roughly equivalent to a "library" or "module" in other languages. The dbt-core monorepo has multiple crates, each responsible for one vertical slice of functionality across all warehouses. This is a quick-reference map of the six crates you'll touch to build your adapter, in the order you'll work through them in Step 4.
| Loading table... |
Step 4: Build a new adapter
This step walks you through each crate you need to touch. Work through them in order — each builds on the last. After each sub-step, run the type checker to catch missed match arms and type errors:
cargo check -p <crate-name>
Replace <crate-name> with the crate you just edited — e.g. dbt-adapter-core, dbt-xdbc, dbt-schemas, dbt-auth, dbt-adapter, or dbt-loader. These match the names in the crate map.
4.1 — Register the adapter type
Crate: crates/dbt-adapter-core/
AdapterType is the central enum that identifies your warehouse throughout the entire codebase. Add your variant here first.
Rust's match expressions must handle every possible variant — there's no catch-all default case. So when you add AdapterType::MyWarehouse, every match adapter_type() block in the codebase that doesn't handle your variant becomes a compile error. Run cargo check -p <crate> and the compiler hands you a complete list of exactly what still needs to be written.
// crates/dbt-adapter-core/src/lib.rs
pub enum AdapterType {
Postgres, Snowflake, Bigquery, Databricks, Redshift,
Salesforce, Spark, DuckDB, Fabric,
ClickHouse, Athena, Starburst, Trino, Datafusion, Dremio, Oracle,
Exasol, // ← already added
MyWarehouse, // ← add yours here
}
Also add a quote_char arm in the same file. Use double quotes '"' for most warehouses; BigQuery and Databricks use a backtick instead:
// crates/dbt-adapter-core/src/lib.rs
fn quote_char(&self) -> char {
match self {
// ... existing adapters ...
Exasol => '"', // double-quote (most warehouses)
MyWarehouse => '"', // ← add yours here
BigQuery | Databricks => '`',
}
}
4.2 — Register the ADBC driver
Crate: crates/dbt-xdbc/
This is where v2 learns how to find and load your warehouse's ADBC driver at runtime.
| Loading table... |
The Backend enum maps to the ADBC shared library name (lib<name>.so / <name>.dll / lib<name>.dylib). You're registering its identity so v2 knows what to load — you're not writing the driver here.
For custom Arrow type mappings (e.g. DuckDB needed this for HUGEINT, UTINYINT): only add warehouse-specific type handling if your driver returns types that Arrow's standard schema doesn't cover. Most warehouses don't need this.
4.3 — Add your connection profile
Crate: crates/dbt-schemas/
| Loading table... |
The config struct should include everything a user would put in their profiles.yml for your warehouse. Example from Exasol:
#[derive(Debug, Clone, Serialize, Deserialize, Default, DbtSchema, Merge)]
pub struct ExasolDbConfig {
pub user: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", alias = "pass")]
pub password: Option<String>,
pub host: Option<String>,
pub port: Option<StringOrInteger>,
pub database: Option<String>,
pub schema: Option<String>,
pub encryption: Option<bool>,
pub certificate_validation: Option<bool>,
pub certificate_fingerprint: Option<String>,
pub connection_timeout: Option<StringOrInteger>,
pub threads: Option<StringOrInteger>,
}
After adding the config struct, also add DbConfig::MyWarehouse(Box<MyWarehouseDbConfig>) as a new variant to the DbConfig enum. This registers your new config type so the rest of the codebase knows it exists. Once you do, the compiler will point you at every place that reads from DbConfig and needs a new case for your warehouse — follow those errors to wire it in.
4.4 — Add authentication
Crate: crates/dbt-auth/
| Loading table... |
The auth module turns a DbConfig into a live, authenticated ADBC connection. At minimum you need basic credential handling. More sophisticated auth (OAuth, SSO, key-pair) can be added incrementally.
The pattern is: read config fields → construct URI → call builder.with_parse_uri(uri), builder.with_username(user), builder.with_password(password).
4.5 — Build the adapter layer
Crate: crates/dbt-adapter/
This is the largest step. You're adding warehouse-specific behavior to the shared adapter layer via match adapter_type() arms.
Most adapters — including Exasol, Athena, Trino, Starburst, Dremio, Oracle, and ClickHouse — add match arms directly to the shared files in src/relation/.
Only adapters with highly custom relation logic (Snowflake's multi-part names and case rules; BigQuery's project/dataset structure) have their own subdirectory under src/relation/.
If your warehouse uses standard schema.table or database.schema.table naming with straightforward quoting, you're likely a simple adapter. Start simple and only add complexity if the compiler forces it.
Relation type and quoting
The relation crate is currently being verticalized — Policy is moving from relation_object.rs to relation_impl.rs. Verify file paths against the current repo before using them.
File: src/relation/relation_object.rs
Add a match arm that constructs a Relation with your quoting policy. The policy controls three things: whether the database is included in fully-qualified names, whether the schema is quoted, and whether the identifier is quoted.
For example, Exasol uses a 2-part name (schema.table, no database prefix) and quotes both:
// src/relation/relation_object.rs
Exasol => Box::new(Relation::new_with_policy(
Exasol,
RelationPath {
database: Some(database).filter(|s| !s.is_empty()),
schema: Some(schema),
identifier,
},
relation_type,
Policy::new(false, true, true),
// ↑ ↑ ↑
// database schema identifier
// disabled quoted quoted
custom_quoting,
None,
false,
false,
)) as Box<dyn BaseRelation>,
Decide up front whether your warehouse uses 2-part or 3-part names, and whether identifiers are case-sensitive. For example, Exasol uppercases unquoted identifiers by default — so all catalog lookup SQL uses upper() comparisons.
File: src/relation/factory.rs
Add your AdapterType to the create_static_relation match, wiring it to RelationStatic (the generic static relation used for Jinja's api.Relation):
Databricks | Spark | Fabric | DuckDB | Exasol | Postgres | Redshift | Salesforce | Bigquery => {
let relation_type = RelationStatic { adapter_type, quoting };
StaticBaseRelationObject::new(Arc::new(relation_type))
}
Catalog introspection
File: src/metadata/get_relation.rs
Add a match arm and a function that queries your warehouse's system catalog to look up a single relation by name:
AdapterType::Exasol => exasol_get_relation(
adapter, state, ctx, conn, database, schema, identifier, token,
),
fn exasol_get_relation(
adapter: &AdapterImpl,
state: &State,
ctx: &QueryCtx,
conn: &mut dyn Connection,
database: &str,
schema: &str,
identifier: &str,
token: CancellationToken,
) -> AdapterResult<Option<Box<dyn BaseRelation>>> {
let q_schema = schema.to_uppercase();
let q_ident = identifier.to_uppercase();
let sql = format!(
"select 'table' as \"type\" from sys.exa_all_tables \
where table_schema = '{q_schema}' and table_name = '{q_ident}' \
union all \
select 'view' from sys.exa_all_views \
where view_schema = '{q_schema}' and view_name = '{q_ident}'"
);
// execute, read result, return relation
}
Use information_schema if your warehouse supports standard SQL, or system catalog tables (sys.*, information_schema.*) as appropriate.
Adapter match arms
File: src/adapter/adapter_impl.rs
After adding AdapterType::MyWarehouse, the compiler will enumerate every match self.adapter_type() block that needs a new arm. Most arms simply delegate to an existing pattern. For example:
// Column builder — delegate to postgres-like builder
Exasol => Ok(Self::build_postgres_like(field, type_ops)),
// Schema column name for listing relations
Exasol => "name",
// DATA_TYPE column name in information schema
AdapterType::Exasol => "DATA_TYPE", // in src/sql_types.rs
For capabilities your adapter doesn't support yet (e.g. valid_incremental_strategies), return unimplemented!() — that's fine for an initial community adapter contribution. The reference PR has several of these.
Column builder
File: src/column/column_builder.rs
Add a match arm for how your warehouse's Arrow record batches map to dbt column objects. Most adapters can delegate to build_postgres_like:
Exasol => Ok(Self::build_postgres_like(field, type_ops)),
Only implement custom logic if your warehouse has unusual type handling.
4.6 — Write your SQL macros
Crate: crates/dbt-loader/
Create a new directory at src/dbt_macro_assets/dbt-<adapter_type>/.
The loader discovers adapter packages by scanning src/dbt_macro_assets/. You don't need to add any Rust code to register your directory — creating it and the dbt_project.yml is sufficient.
| Loading table... |
v2 uses the same adapter.dispatch() pattern as v1. Your macros use the <warehouse>__ prefix to override defaults:
{% macro exasol__create_table_as(temporary, relation, sql) -%}
create or replace table {{ relation }} as (
{{ sql }}
)
{%- endmacro %}
{% macro exasol__create_view_as(relation, sql) -%}
create or replace view {{ relation }} as (
{{ sql }}
)
{%- endmacro %}
{% macro exasol__drop_relation(relation) -%}
{% call statement('drop_relation', auto_begin=False) -%}
drop {{ relation.type }} if exists {{ relation }} cascade
{%- endcall %}
{% endmacro %}
{% macro exasol__rename_relation(from_relation, to_relation) -%}
{% call statement('rename_relation') -%}
rename {{ from_relation.type }} {{ from_relation }} to {{ to_relation.identifier }}
{%- endcall %}
{% endmacro %}
Note: rename_relation uses only to_relation.identifier, not the full relation — Exasol's RENAME syntax doesn't take a fully qualified target.
For catalog introspection, use your warehouse's system catalog. For example, Exasol queries sys.* instead of information_schema:
{% macro exasol__list_relations_without_caching(schema_relation) -%}
{% call statement('list_relations_without_caching', fetch_result=True) -%}
select
'{{ schema_relation.database }}' as "database",
table_name as "name",
table_schema as "schema",
'table' as "type"
from sys.exa_all_tables
where upper(table_schema) = upper('{{ schema_relation.schema }}')
union all
select
'{{ schema_relation.database }}' as "database",
view_name as "name",
view_schema as "schema",
'view' as "type"
from sys.exa_all_views
where upper(view_schema) = upper('{{ schema_relation.schema }}')
{%- endcall %}
{{ return(load_result('list_relations_without_caching').table) }}
{%- endmacro %}
{% macro exasol__information_schema_name(database) -%}
sys
{%- endmacro %}
If your warehouse is similar to an existing one (e.g. Postgres-compatible), start by delegating to that dialect's macros and only override where behavior differs:
{% macro mywarehouse__create_table_as(temporary, relation, sql) -%}
{{ return(postgres__create_table_as(temporary, relation, sql)) }}
{%- endmacro %}
Step 5: Test your adapter
Type check after each crate
Run the type checker after completing work in each crate to catch missed match arms and type errors:
cargo build -p dbt-adapter-core
cargo build -p dbt-xdbc
cargo build -p dbt-schemas
cargo build -p dbt-auth
cargo build -p dbt-adapter
cargo build -p dbt-loader
End-to-end test
Run a real dbt build against your warehouse. At minimum, exercise table, view, incremental, and snapshot materializations. A clean dbt build on jaffle-shop-classic is the standard acceptance bar for a community adapter.
# Build the CLI
cargo build --bin dbt
# Create a test project (or clone jaffle-shop-classic)
./target/debug/dbt init
# Run against your warehouse
./target/debug/dbt build --project-dir <your-project>
CI testing
CI testing for community adapter PRs is coordinated with the dbt Labs adapters team — the test infrastructure is not publicly distributed. When your PR is ready, reach out in #adapter-ecosystem and the adapters team will work with you on warehouse validation.
Community contributors cannot run CI independently. dbt Labs' CI pipeline requires warehouse credentials for the target warehouse — which means dbt Labs needs access configs for whatever warehouse you're contributing. The process for this handoff is still being defined. Expect to coordinate closely with the adapters team. If you hit friction, flag it in #adapter-ecosystem.
Step 6: Document your adapter
Once your adapter is merged and available in a release, document it so users can find and configure it.
Write a setup guide
Document the profiles.yml configuration for your warehouse — what fields are required, what's optional, and example values. Follow the format of existing adapter setup guides on docs.getdbt.com.
Unlike first-party adapters, your users won't get the driver automatically — Fusion won't download it for them. Your setup guide must explain where to get the driver binary and how to install it so Fusion can find it at runtime. Without this, users will configure a valid profile and still get a connection error. Include the exact library name Fusion looks for (e.g. libadbc_driver_<yourwarehouse>.dylib) and where to put it.
General documentation guidelines
- Assume the reader knows dbt fundamentals but is not an expert on your warehouse inner workings.
- Include a complete working
profiles.ymlexample. - Document any warehouse-specific quirks (e.g. 2-part vs 3-part naming, identifier case sensitivity).
- Link to the warehouse vendor's ADBC driver documentation.
Step 7: Promote your adapter
dbt Labs reviews and merges community adapter PRs into dbt-core. Wait until the PR is merged and the adapter ships in a published release before directing users to it.
Community channels
Join the dbt Community Slack and find:
#adapter-ecosystem— the main channel for adapter developers#db-<yourwarehouse>— if a channel exists for your warehouse, let users know v2 support is available. Note: v1 adapter users will still be on the Python-based adapter and will need to migrate.#proj-isv-adapters-in-fusion— ping the dbt Labs adapters team here once your PR is up for review.
Before you announce
Align with the adapters team on: which materializations you're targeting in the initial implementation, any known gaps in your ADBC driver, and timeline. This prevents surprises during review and sets accurate expectations for users.
Reference: File-by-file implementation guide
A community contributed v2 adapter touches roughly 13 files, all in the public dbt-labs/dbt-core repo. The "Exasol example" column shows what it looks like in practice — substitute your warehouse name and system catalog throughout.
| Loading table... |
Reference: Useful commands
# Type check a specific crate
cargo build -p <crate-name>
# Build the CLI binary
cargo build --bin dbt
# Run a full dbt build against your warehouse
./target/debug/dbt build --project-dir <your-project>
# Free disk space
cargo clean
Was this page helpful?
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.