Разработка адаптера для dbt v2
Это руководство находится в разработке. Оно составлено на основе внутренних документов команды адаптеров v2, эталонных реализаций DuckDB/ClickHouse, адаптера Exasol от сообщества и руководства по созданию адаптеров v1.
Объём работы для участников сообщества: Вы отвечаете только за этап 1 (базовая функциональность) — подключение, выполнение макросов и базовые материализации. Этап 2 (статический анализ SQL) выполняет dbt Labs после слияния вашего PR по этапу 1.
Шаг 1: Введение
Адаптеры dbt v2 устроены совсем иначе, чем адаптеры dbt v1. В v1 каждый адаптер был отдельным пакетом Python, реализующим разрозненный интерфейс Python. В v2 адаптеры находятся внутри монорепозитория на Rust и подключаются к хранилищам через драйверы ADBC (Arrow Database Connectivity) — соответственно изменилась и модель участия сообщества.
Это руководство поможет вам как участнику сообщества добавить новый адаптер v2 в dbt-labs/dbt. Перечень файлов в справочном разделе показывает примерно 13 файлов, которые затрагивает полноценный адаптер v2 от сообщества. В качестве примера во всём руководстве используется Exasol — адаптер v2, разработанный сообществом.
dbt v2 — это новый движок dbt на Rust. Адаптеры v2 написаны на Rust и находятся внутри монорепозитория dbt-labs/dbt, а не в отдельных пакетах Python.
Что изменилось в адаптерах
В dbt v1 каждый адаптер:
- Представляет собой отдельный пакет Python (например,
dbt-snowflakeиdbt-bigquery) - Разрабатывается сообществом и поддерживается независимо
- Динамически загружается во время выполнения через систему плагинов Python
- Использует коннекторы/драйверы Python SDK, поддерживаемые поставщиком
В dbt v2 адаптеры:
- Входят в монорепозиторий
dbt-labs/dbt, куда добавляются через PR - Написаны на Rust (при этом для логики SQL по-прежнему используются макросы Jinja SQL)
- Подключаются к хранилищам через драйверы ADBC — унифицированный интерфейс драйверов, абстрагирующий управление подключениями
- Организованы по функциональным направлениям, а не по хранилищам — поэтому исправление ошибки в одном крейте часто приносит пользу всем адаптерам
Практическое преимущество: для добавления нового адаптера требуется гораздо меньше кода, чем в v1, а по мере добавления адаптеров затраты на разработку снижаются благодаря увеличению объёма общей логики.
Что вы будете разрабатывать
Как участник сообщества вы создаёте базовую функциональность: подключение к вашему хранилищу, выполнение макросов dbt и поддержку базовых материализаций (dbt run, dbt build). Примерно 13 файлов из справочного раздела определяют полный объём работы.
Шаг 2: Предварительные требования
Необходимые знания
Прежде чем начать, вам следует:
- Немного познакомиться с Rust — быть экспертом по Rust не обязательно (скорее всего, большую часть кода напишет LLM). Нужно уметь читать Rust, понимать выражения сопоставления с образцом для перечислений и разбираться в ошибках компилятора. Если Rust для вас в новинку, просмотрите главу 6 The Rust Book (перечисления и сопоставление с образцом) — именно с этими понятиями вы будете сталкиваться чаще всего.
- Понимать основы dbt — как работает
profiles.yml, что такое материализации и что делают макросы диспетчеризации адаптеров. - Понимать устройство вашего движка запросов и его отличия от других. Следующие вопросы охватывают ключевые моменты разработки адаптера:
- Какой символ используется для заключения идентификаторов в кавычки?
- Поддерживаются ли имена из трёх частей или только из двух?
- Поддерживаются ли транзакции?
- Как наиболее эффективно получить все столбцы в схеме?
- Какой тип данных представляет строку?
- Какие поля подключения требуются вашему хранилищу?
- Иметь под рукой LLM-помощника для программирования (необязательно) — процесс разработки адаптера рассчитан на написание кода с помощью ИИ, и в руководстве отмечено, где его стоит использовать.
Ваш драйвер ADBC
dbt Labs не разрабатывает драйверы. Если у вашего хранилища ещё нет драйвера ADBC, сначала нужно создать его — это обязательное условие, которое нельзя пропустить.
Что на самом деле означает «наличие драйвера ADBC»
Для некоторых адаптеров (Snowflake, BigQuery, Databricks, Redshift, DuckDB, ClickHouse, Spark, SQL Server) v2 автоматически скачивает нужный бинарный файл драйвера из CDN dbt Labs при первом использовании. Пользователям вообще не нужно об этом думать.
У адаптеров сообщества нет поддержки CDN. Вместо этого dbt v2 ищет в системе пользователя разделяемую библиотеку по имени — например, libadbc_driver_exasol.dylib в macOS и libadbc_driver_exasol.so в Linux. Если файла нет, при выполнении возникает ошибка подключения.
Для вас как разработчика это означает две вещи:
- Бинарный файл драйвера должен где-то существовать. Это скомпилированная разделяемая библиотека (
.dylib/.so/.dll), реализующая ADBC C ABI для вашего хранилища. Драйвер Exasol — этоexarrow-rs, отдельный крейт Rust, который поддерживает Exasol; он не входит ни вdbt, ни вapache/arrow-adbc. Скорее всего, драйвер вашего хранилища будет находиться в аналогичном отдельном репозитории. - Вашим пользователям придётся устанавливать его вручную. В отличие от официальных адаптеров, автоматической загрузки здесь нет. Ваша документация (шаг 6) должна объяснять пользователям, где получить драйвер и как его установить.
Проверьте, существует ли уже драйвер для вашего хранилища:
- dbt-labs/arrow-adbc — форк dbt Labs
- apache/arrow-adbc — исходный репозиторий сообщества
- adbc-drivers — ADBC driver foundry, основная площадка сообщества для драйверов ADBC с активно разрабатываемыми драйверами для Trino, MySQL, BigQuery, Databricks, Snowflake и других систем; также содержит фреймворки Rust/Go (
driverbase-rs,template-rs) для создания новых драйверов с нуля - Собственная организация поставщика хранилища на GitHub (многие поставщики публикуют драйверы ADBC независимо)
dbt v2 загружает драйверы по имени разделяемой библиотеки из системных путей поиска (например, libadbc_driver_exasol.dylib в macOS) — исходный репозиторий может быть любым, если бинарный файл реализует ADBC C ABI. dbt Labs работает над механизмом подписи и проверки драйверов. Пока он не выпущен, пользователи сами отвечают за доверие к устанавливаемому бинарному файлу драйвера.
Если драйвера пока нет, его создание — отдельный проект, который предшествует добавлению адаптера. Это выходит за рамки помощи, которую может предоставить dbt Labs. Columnar специализируется на создании драйверов ADBC и может оказаться полезным ресурсом, если вам нужна помощь в разработке драйвера.
Вы переносите существующий адаптер v1?
Перейдите к контрольному списку ниже, а затем сразу к шагу 4. Вам не придётся переносить SQL-код макросов или логику подключения, но остальные части руководства (структура крейтов, шаблоны Rust, работа с ИИ) по-прежнему применимы.
Большинство участников сообщества не создают адаптеры с нуля — они переносят адаптер, который уже поддерживают или используют на Python для dbt v1. Если это ваш случай, у вас есть существенная фора, и для подавляющего большинства участников это наиболее реалистичный путь.
Прежде чем писать код на Rust, проверьте несколько вещей:
1. Существует ли уже адаптер v1 для вашего хранилища?
Обратитесь к списку адаптеров сообщества. Если адаптер существует, найдите его репозиторий на GitHub. SQL-код макросов и логику подключения можно использовать повторно почти без изменений.
2. Добавила ли dbt Labs заготовку для вашего хранилища?
Некоторые хранилища уже представлены в перечислении AdapterType v2, но ещё не реализованы полностью — вариант перечисления существует, а значит, часть базового кода уже на месте. Когда вы начнёте добавлять оставшийся код, компилятор покажет, чего именно ещё не хватает. В таком состоянии находятся следующие хранилища:
| Хранилище | Адаптер v1 | Статус в dbt v2 |
|---|---|---|
| Athena | dbt-athena | AdapterType::Athena существует — нужны аутентификация, макросы и ветви адаптера |
| Trino | dbt-trino | AdapterType::Trino существует — нужны аутентификация, макросы и ветви адаптера |
| Starburst | dbt-trino | AdapterType::Starburst существует — нужны аутентификация, макросы и ветви адаптера |
| Dremio | dbt-dremio | AdapterType::Dremio существует — нужны аутентификация, макросы и ветви адаптера |
| Oracle | dbt-oracle | AdapterType::Oracle существует — нужны аутентификация, макросы и ветви адаптера |
Для хранилищ, которых ещё совсем нет в AdapterType (MySQL, Hive, Vertica, SQL Server, Teradata и других), начните с шага 4.1, добавив вариант AdapterType.
Что переносится из v1 в v2
| Компонент v1 | Куда переносится в v2 | Примечания |
|---|---|---|
macros/adapters.sql | dbt-loader/.../dbt-<wh>/macros/adapters.sql | В основном прямой перенос — тот же префикс диспетчеризации <wh>__, те же имена макросов и шаблоны Jinja |
macros/catalog.sql | Туда же | SQL-код каталога (list_relations_without_caching, get_catalog) переносится почти дословно |
| Пользовательские материализации и переопределения адаптера | Туда же | Следите за конструкциями Jinja, которые могут ещё не поддерживаться в v2; их потребуется обработать отдельно |
Поля профиля в credentials.py / profile_template.yml | Структура DbConfig в dbt-schemas | Каждое поле профиля становится полем структуры — для необязательных полей используется Option<T> |
Формирование URI подключения / DSN в connections.py | dbt-auth/src/<wh>/mod.rs | Логика формирования URI хорошо вписывается в шаблон модуля аутентификации |
BaseRelation.quote_policy / правила регистра идентификаторов | Policy::new(...) в relation_object.rs | Структура имён из 3 или 2 частей и флаги кавычек напрямую соответствуют структуре Policy в v2 |
SQL-код получения метаданных каталога в макросах и adapter.py | get_relation.rs и макросы Jinja | Уже знакомые вам имена таблиц системного каталога и запросы переносятся напрямую |
Что намеренно не переносится
Некоторые компоненты не переносятся из v1 в v2 напрямую — для вас это означает меньше кода и меньше работы по поддержке:
- Методы
ConnectionManager(open,cancel,get_response,execute) — в v1 вы отвечали за их реализацию в соответствии со спецификацией Python DB API 2.0. В v2 управление подключениями полностью выполняет драйвер ADBC. - Иерархия классов адаптера Python и методы-обёртки выполнения — теперь это обрабатывают выражения
match adapter_type()в общем файлеadapter_impl.rs(на шаге 4.5 объясняется, как они работают). setup.py/ зависимости пакета отdbt-core— после слияния вашего PR упаковкой и распространением занимается dbt Labs. Не нужны ниsetup.py, ни выпуск в PyPI, ни фиксация версий.
Как использовать адаптер v1 при работе с ИИ
Макросы Jinja — ваш самый ценный ресурс. Передайте LLM ваш файл macros/adapters.sql из v1 вместе с эталонным adapters.sql (из перечня файлов в конце этого руководства) и попросите перенести макросы в структуру v2. Логика SQL остаётся прежней; ваша задача — проверить правильность диалекта и соответствие имён таблиц системного каталога.
Для конфигурации профиля и аутентификации передайте LLM ваш credentials.py или profile_template.yml из v1 вместе с эталонной структурой DbConfig и модулем аутентификации (см. перечень файлов ниже). Имена полей и параметры подключения обычно соответствуют друг другу напрямую.
Самое частое несоответствие: в connections.py v1 много кода Python для управления подключениями, у которого нет аналога в v2 — всем этим занимается драйвер. Сосредоточьтесь только на формировании URI/DSN и полях учётных данных — пропустите всё, что связано с курсорами, повторными попытками или пулом подключений.
Настройка машины для разработки
# 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
cd dbt
# Verify you can build
cargo build --bin dbt
Если возникают ошибки Z3:
brew install pkg-config z3
Если во время сборки заканчивается место на диске:
cargo clean # frees old build artifacts — you'll do this often
Процесс разработки
Основной цикл разработки одинаков независимо от того, пишете ли вы код вручную или с помощью ИИ-помощника:
- Добавьте
AdapterType::MyWarehouseв перечисление (шаг 4.1) - Выполните
cargo check -p <crate>— компилятор перечислит все ветви сопоставления, в которых не хватает вашего нового варианта - Используйте вывод компилятора, редактируемый файл и соответствующий эталонный файл (из перечня ниже), чтобы написать каждую ветвь. Если вы используете ИИ-помощника, передайте все три элемента в качестве контекста.
- Пример: чтобы заполнить ветвь
quote_char, передайте сообщение компилятора об отсутствииAdapterType::MyWarehouse, блокmatchфункцииquote_charизdbt-adapter-core/src/lib.rsи строку Exasol (Exasol => '"') как образец.
- Пример: чтобы заполнить ветвь
- Заполните ветвь; снова проверьте результат с помощью
cargo check - Повторяйте для каждого крейта, пока не останется ошибок
Каждый необработанный случай вызывает ошибку компиляции, поэтому у ИИ всегда есть точная спецификация для работы.
Какой контекст полезен для каждой ветви:
- Редактируемый файл — передайте соответствующий блок
matchили функцию. Например, блокmatch self.adapter_type()изadapter_impl.rs, в который нужно добавить новую ветвь. - Соответствующий эталонный файл из перечня ниже. Например, при написании модуля аутентификации передайте
crates/dbt-auth/src/exasol/mod.rsкак образец. - Вывод компилятора с ошибкой. Например:
error[E0004]: non-exhaustive patterns: AdapterType::MyWarehouse not covered - Особенности вашего хранилища — имена таблиц системного каталога и поля подключения из вашего
profiles.yml.
На что обратить внимание:
- Выдуманные пути к файлам — ИИ часто придумывает пути v2. Используйте перечень файлов ниже как достоверный источник.
- Всегда проверяйте результат средством проверки типов — выполняйте
cargo check -p <crate>после любых изменений, сгенерированных ИИ. - Шаблоны SQL-макросов из v1 могут не подходить для dbt v2 без изменений. Сверяйтесь с эталонным
adapters.sqlпо путиcrates/dbt-loader/src/dbt_macro_assets/dbt-exasol/macros/adapters.sqlв dbt-labs/dbt.
Шаг 3: Изучите архитектуру
Прежде чем писать код, полезно разобраться в слоях, с которыми вам предстоит работать.
Почему ADBC?
Ключевое архитектурное решение в dbt v2 — использование ADBC (Arrow Database Connectivity) в качестве унифицированного интерфейса драйверов.
В dbt v1 адаптеры подключались через драйверы Python — часто оборачивая pyodbc или проприетарные механизмы подключения. Каждый адаптер полностью отвечал за собственную логику подключения.
В v2 каждое хранилище подключается через драйвер ADBC — предварительно скомпилированный бинарный файл, который обрабатывает сетевой протокол, процедуры аутентификации и пул подключений. Код вашего адаптера ничего из этого не затрагивает. Для официальных адаптеров с поддержкой CDN dbt v2 скачивает этот драйвер автоматически при первом использовании. Для адаптеров сообщества пользователи устанавливают его вручную — поэтому шаг 2 посвящён поиску или созданию драйвера.
ADBC изначально ориентирован на колоночное представление данных на всём пути — если ваше хранилище поддерживает колоночный вывод (например, Arrow IPC или Arrow Flight SQL), данные передаются без преобразований. Это означает, что вам не нужно писать код управления подключениями — он находится в драйвере. Вы пишете расположенные над драйвером компоненты: конфигурацию для конкретного хранилища, аутентификацию, правила именования объектов relation, логику макросов и получение метаданных каталога.
Модель функциональных направлений
В отличие от v1, где у каждого хранилища был собственный монолитный класс адаптера (SnowflakeAdapter, BigqueryAdapter), в v2 логика адаптеров организована по функциональным направлениям, которые охватывают все хранилища:
dbt-xdbc— загрузка драйверов, подключения ADBC (все хранилища)dbt-auth— получение учётных данных (все хранилища, отдельные модули для каждого хранилища)dbt-adapter— единыйConcreteAdapter, в котором поведение для конкретного хранилища определяется черезmatch adapter_type(), а не через отдельные структуры для каждого хранилищаdbt-loader— макросы Jinja SQL для каждого хранилища
Добавляя новое хранилище, вы добавляете вариант в каждое функциональное направление, а не новый пакет верхнего уровня. Компилятор обеспечивает соблюдение этого правила: если вы добавите AdapterType::MyWarehouse, а какой-либо блок match adapter_type() не будет его обрабатывать, Rust не скомпилирует код. Например, если в adapter_impl.rs есть match self.adapter_type(), который обрабатывает Postgres, Snowflake, Exasol и другие хранилища, но не MyWarehouse, компилятор сообщит об ошибке именно в этой строке — поэтому вы всегда знаете, что осталось сделать.
Это существенное улучшение по сравнению с моделью наследования классов v1. В v1 новый подкласс адаптера неявно наследовал поведение родительского класса для любого метода, который вы не переопределили явно. Это могло скрывать ошибки, когда для хранилища незаметно использовалось неподходящее поведение по умолчанию. В v2 нет ни наследования, ни неявного поведения. Каждая функция, поведение которой зависит от хранилища, требует явной ветви сопоставления, поэтому ни один случай не остаётся незаметно необработанным.
Например:
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
Стек слоёв
Диаграмма ниже показывает, как запрос проекта dbt проходит через крейты во время выполнения — от конфигурации до выполнения SQL. Работа над вашим адаптером сосредоточена в средних слоях: конфигурация профиля, получение учётных данных, загрузка драйвера, логика объектов relation и макросы. За нижний слой (выполнение SQL в хранилище) отвечает dbt Labs.
profiles.yml / dbt_project.yml
│
▼
dbt-schemas ← структура конфигурации профиля для вашего хранилища
│
▼
dbt-auth ← получение учётных данных
│
▼
dbt-xdbc ← загрузка драйвера ADBC
│
▼
dbt-adapter ← типы relation, адаптер метаданных, методы адаптера
│
dbt-loader ← макросы Jinja SQL (материализации, каталог, утилиты)
│
▼
[dbt internal] ← выполнение SQL в хранилище (dbt Labs)
Карта крейтов
В Rust крейт — это пакет, единица компиляции, примерно соответствующая «библиотеке» или «модулю» в других языках. Монорепозиторий dbt-labs/dbt содержит несколько крейтов, каждый из которых отвечает за одно функциональное направление для всех хранилищ. Ниже приведена краткая карта шести крейтов, которые вы будете изменять при создании адаптера, в порядке работы с ними на шаге 4.
| Крейт | Расположение | Что вы изменяете |
|---|---|---|
dbt-adapter-core | crates/dbt-adapter-core/ | Вариант перечисления AdapterType, ветвь quote_char |
dbt-xdbc | crates/dbt-xdbc/ | Регистрация драйвера ADBC (перечисление Backend, имя библиотеки) |
dbt-schemas | crates/dbt-schemas/ | Структура конфигурации profiles.yml (вариант DbConfig) |
dbt-auth | crates/dbt-auth/ | Получение учётных данных, формирование URI подключения |
dbt-adapter | crates/dbt-adapter/ | Кавычки в relation, запросы к каталогу метаданных, ветви сопоставления адаптера, построитель столбцов, sql_types |
dbt-loader | crates/dbt-loader/ | Макросы Jinja SQL (dbt_macro_assets/dbt-<adapter>/) |
Шаг 4: Создайте новый адаптер
Этот шаг последовательно описывает все крейты, которые нужно изменить. Работайте с ними по порядку — каждый следующий этап опирается на предыдущий. После каждого подшага запускайте проверку типов, чтобы обнаружить пропущенные ветви сопоставления и ошибки типов:
cargo check -p <crate-name>
Замените <crate-name> на имя крейта, который вы только что изменили, — например, dbt-adapter-core, dbt-xdbc, dbt-schemas, dbt-auth, dbt-adapter или dbt-loader. Эти имена соответствуют карте крейтов.
4.1 — Зарегистрируйте тип адаптера
Крейт: crates/dbt-adapter-core/
AdapterType — центральное перечисление, которое идентифицирует ваше хранилище во всей кодовой базе. Сначала добавьте свой вариант сюда.
Выражения match в Rust должны обрабатывать все возможные варианты — общего случая по умолчанию здесь нет. Поэтому при добавлении AdapterType::MyWarehouse каждый блок match adapter_type() в кодовой базе, который не обрабатывает ваш вариант, становится ошибкой компиляции. Выполните cargo check -p <crate>, и компилятор выдаст полный список того, что ещё нужно написать.
// crates/dbt-adapter-core/src/lib.rs
pub enum AdapterType {
Postgres, Snowflake, Bigquery, Databricks, Redshift,
Spark, DuckDB, Fabric,
ClickHouse, Athena, Starburst, Trino, Datafusion, Dremio, Oracle,
Exasol, // ← already added
MyWarehouse, // ← add yours here
}
Также добавьте ветвь quote_char в том же файле. Для большинства хранилищ используйте двойные кавычки '"'; BigQuery и Databricks вместо них используют обратную кавычку:
// 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 — Зарегистрируйте драйвер ADBC
Крейт: crates/dbt-xdbc/
Здесь вы указываете v2, как найти и загрузить драйвер ADBC вашего хранилища во время выполнения.
| Файл | Что сделать | Обязательно? |
|---|---|---|
src/driver.rs | Добавьте вариант в перечисление Backend; добавьте имя библиотеки ADBC (например, "adbc_driver_exasol") и протокол FFI; также определите LoadStrategy | Да |
src/install.rs | Добавьте URL для загрузки из CDN и строки платформ | Нет — только если драйвер будет распространяться через CDN dbt Labs, что требует отдельного согласования с dbt Labs. По умолчанию драйверы адаптеров сообщества не размещаются в CDN. |
Перечисление Backend сопоставляется с именем разделяемой библиотеки ADBC (lib<name>.so / <name>.dll / lib<name>.dylib). Вы регистрируете сведения о драйвере, чтобы v2 знал, что загружать, — сам драйвер здесь не пишется.
Для пользовательских сопоставлений типов Arrow (например, DuckDB это потребовалось для HUGEINT и UTINYINT): добавляйте обработку типов для конкретного хранилища только в том случае, если ваш драйвер возвращает типы, которые не охватывает стандартная схема Arrow. Большинству хранилищ это не требуется.
4.3 — Добавьте профиль подключения
Крейт: crates/dbt-schemas/
| Файл | Что сделать |
|---|---|
src/schemas/profiles.rs | Добавьте вариант DbConfig и структуру конфигурации со всеми полями подключения |
Структура конфигурации должна включать всё, что пользователь может указать в своём profiles.yml для вашего хранилища. Пример 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>,
}
После добавления структуры конфигурации также добавьте DbConfig::MyWarehouse(Box<MyWarehouseDbConfig>) как новый вариант перечисления DbConfig. Это зарегистрирует новый тип конфигурации, чтобы остальная кодовая база знала о его существовании. После этого компилятор укажет все места, где читается DbConfig и нужен новый случай для вашего хранилища, — следуйте этим ошибкам, чтобы подключить его ко всей логике.
4.4 — Добавьте аутентификацию
Крейт: crates/dbt-auth/
| Файл | Что сделать | Обязательно? |
|---|---|---|
src/<warehouse>/mod.rs | Получение учётных данных — чтение полей конфигурации, переменных окружения, файлов ключей, токенов; формирование URI подключения ADBC и учётных данных | Да |
src/<warehouse>/init.rs | Генерация SQL инициализации — SQL, который должен выполняться при открытии подключения (например, USE SCHEMA, операторы SET и загрузка расширений) | Необязательно — только для хранилищ, которым требуется выполнять SQL при открытии подключения. Большинству хранилищ это не нужно. |
src/lib.rs | Зарегистрируйте новый модуль с помощью mod <warehouse>; и подключите его к сопоставлению диспетчеризации аутентификации | Да |
Модуль аутентификации преобразует DbConfig в действующее подключение ADBC с пройденной аутентификацией. Как минимум нужна базовая обработка учётных данных. Более сложные способы аутентификации (OAuth, SSO, пара ключей) можно добавлять постепенно.
Шаблон таков: прочитать поля конфигурации → сформировать URI → вызвать builder.with_parse_uri(uri), builder.with_username(user), builder.with_password(password).
4.5 — Создайте слой адаптера
Крейт: crates/dbt-adapter/
Это самый объёмный шаг. Вы добавляете поведение для конкретного хранилища в общий слой адаптера через ветви match adapter_type().
Большинство адаптеров — включая Exasol, Athena, Trino, Starburst, Dremio, Oracle и ClickHouse — добавляют ветви сопоставления непосредственно в общие файлы в src/relation/.
Только адаптеры с существенно нестандартной логикой relation (составные имена и правила регистра Snowflake; структура project/dataset в BigQuery) имеют собственный подкаталог в src/relation/.
Если ваше хранилище использует стандартные имена schema.table или database.schema.table с простыми правилами кавычек, скорее всего, ваш адаптер относится к простым. Начните с простого варианта и усложняйте его только тогда, когда этого потребует компилятор.
Тип relation и кавычки
Крейт relation сейчас реорганизуется по функциональным направлениям — Policy переносится из relation_object.rs в relation_impl.rs. Перед использованием сверяйте пути к файлам с текущим состоянием репозитория.
Файл: src/relation/relation_object.rs
Добавьте ветвь сопоставления, которая создаёт Relation с вашей политикой кавычек. Эта политика управляет тремя вещами: включается ли база данных в полные имена, заключается ли схема в кавычки и заключается ли идентификатор в кавычки.
Например, Exasol использует имя из 2 частей (schema.table, без префикса базы данных) и заключает обе части в кавычки:
// 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>,
Заранее определите, использует ли ваше хранилище имена из 2 или 3 частей и чувствительны ли идентификаторы к регистру. Например, Exasol по умолчанию приводит идентификаторы без кавычек к верхнему регистру — поэтому во всех SQL-запросах поиска по каталогу используются сравнения с upper().
Файл: src/relation/factory.rs
Добавьте ваш AdapterType в сопоставление create_static_relation, связав его с RelationStatic (общим статическим объектом relation, используемым для api.Relation в Jinja):
Databricks | Spark | Fabric | DuckDB | Exasol | Postgres | Redshift | Bigquery => {
let relation_type = RelationStatic { adapter_type, quoting };
StaticBaseRelationObject::new(Arc::new(relation_type))
}
Получение метаданных каталога
Файл: src/metadata/get_relation.rs
Добавьте ветвь сопоставления и функцию, которая запрашивает системный каталог вашего хранилища для поиска одного объекта relation по имени:
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
}
Используйте information_schema, если ваше хранилище поддерживает стандартный SQL, либо подходящие таблицы системного каталога (sys.*, information_schema.*).
Ветви сопоставления адаптера
Файл: src/adapter/adapter_impl.rs
После добавления AdapterType::MyWarehouse компилятор перечислит все блоки match self.adapter_type(), которым нужна новая ветвь. Большинство ветвей просто делегируют обработку существующей реализации. Например:
// 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
Для возможностей, которые ваш адаптер пока не поддерживает (например, valid_incremental_strategies), возвращайте unimplemented!() — для первоначального вклада в адаптер сообщества это допустимо. В эталонном PR есть несколько таких случаев.
Построитель столбцов
Файл: src/column/column_builder.rs
Добавьте ветвь сопоставления, определяющую, как пакеты записей Arrow вашего хранилища преобразуются в объекты столбцов dbt. Большинство адаптеров могут делегировать это build_postgres_like:
Exasol => Ok(Self::build_postgres_like(field, type_ops)),
Реализуйте собственную логику только в том случае, если ваше хранилище обрабатывает типы нестандартным образом.
4.6 — Напишите SQL-макросы
Крейт: crates/dbt-loader/
Создайте новый каталог по пути src/dbt_macro_assets/dbt-<adapter_type>/.
Загрузчик обнаруживает пакеты адаптеров, сканируя src/dbt_macro_assets/. Для регистрации каталога не нужно добавлять код Rust — достаточно создать его и файл dbt_project.yml.
| Файл | Что сделать |
|---|---|
dbt_macro_assets/dbt-<warehouse>/dbt_project.yml | Определение проекта плагина адаптера: name: dbt_<warehouse>, macro-paths: ["macros"] |
macros/adapters.sql | Макросы адаптера dbt v1: create_schema, drop_schema, drop_relation, rename_relation, truncate_relation, create_table_as, create_view_as, list_schemas, check_schema_exists, information_schema_name, current_timestamp, get_columns_in_relation, list_relations_without_caching |
v2 использует тот же механизм adapter.dispatch(), что и v1. Для переопределения реализаций по умолчанию ваши макросы используют префикс <warehouse>__:
{% 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 %}
Примечание: rename_relation использует только to_relation.identifier, а не полное имя объекта relation — синтаксис RENAME в Exasol не принимает полное имя целевого объекта.
Для получения метаданных каталога используйте системный каталог вашего хранилища. Например, Exasol запрашивает sys.* вместо 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 %}
Если ваше хранилище похоже на уже поддерживаемое (например, совместимо с Postgres), начните с делегирования макросам этого диалекта и переопределяйте только то, где поведение отличается:
{% macro mywarehouse__create_table_as(temporary, relation, sql) -%}
{{ return(postgres__create_table_as(temporary, relation, sql)) }}
{%- endmacro %}
Шаг 5: Протестируйте адаптер
Проверяйте типы после работы с каждым крейтом
Запускайте проверку типов после завершения работы с каждым крейтом, чтобы обнаружить пропущенные ветви сопоставления и ошибки типов:
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
Сквозное тестирование
Выполните настоящий dbt build в вашем хранилище. Как минимум проверьте материализации table, view, incremental и snapshot. Успешный dbt build без ошибок на jaffle-shop-classic — стандартный критерий приёмки адаптера сообщества.
# 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
Тестирование в CI для PR с адаптерами сообщества координируется с командой адаптеров dbt Labs — тестовая инфраструктура не распространяется публично. Когда ваш PR будет готов, свяжитесь с командой в #adapter-ecosystem, и она поможет вам проверить работу с хранилищем.
Участники сообщества не могут запускать CI самостоятельно. Конвейеру CI dbt Labs нужны учётные данные целевого хранилища — это означает, что dbt Labs потребуются настройки доступа к хранилищу, адаптер которого вы добавляете. Процесс передачи этих данных ещё формируется. Будьте готовы тесно взаимодействовать с командой адаптеров. Если возникнут затруднения, сообщите о них в #adapter-ecosystem.
Шаг 6: Подготовьте документацию адаптера
После того как ваш адаптер будет принят и станет доступен в релизе, подготовьте документацию, чтобы пользователи могли найти и настроить его.
Напишите руководство по настройке
Опишите конфигурацию profiles.yml для вашего хранилища — какие поля обязательны, какие необязательны, и приведите примеры значений. Следуйте формату существующих руководств по настройке адаптеров на docs.getdbt.com.
В отличие от официальных адаптеров, ваши пользователи не получат драйвер автоматически — dbt v2 не скачает его за них. Ваше руководство по настройке должно объяснять, где получить бинарный файл драйвера и как установить его, чтобы dbt v2 мог найти его во время выполнения. Без этого пользователи настроят корректный профиль, но всё равно получат ошибку подключения. Укажите точное имя библиотеки, которую ищет dbt v2 (например, libadbc_driver_<yourwarehouse>.dylib), и куда её поместить.
Общие рекомендации по документации
- Исходите из того, что читатель знает основы dbt, но не является экспертом по внутреннему устройству вашего хранилища.
- Приведите полный рабочий пример
profiles.yml. - Опишите все особенности хранилища (например, имена из 2 или 3 частей и чувствительность идентификаторов к регистру).
- Добавьте ссылку на документацию драйвера ADBC от поставщика хранилища.
Шаг 7: Расскажите о своём адаптере
dbt Labs проверяет и принимает PR с адаптерами сообщества в dbt-labs/dbt. Прежде чем рекомендовать адаптер пользователям, дождитесь слияния PR и выхода опубликованного релиза с этим адаптером.
Каналы сообщества
Присоединитесь к dbt Community Slack и найдите:
#adapter-ecosystem— основной канал для разработчиков адаптеров#db-<yourwarehouse>— если для вашего хранилища существует канал, сообщите пользователям, что поддержка v2 доступна. Примечание: пользователи адаптера v1 всё ещё будут работать с адаптером на Python, и им потребуется миграция.#proj-isv-adapters-in-fusion— сообщите команде адаптеров dbt Labs в этом канале, когда ваш PR будет готов к проверке.
Перед объявлением
Согласуйте с командой адаптеров: какие материализации вы планируете поддержать в первоначальной реализации, какие известные ограничения есть в вашем драйвере ADBC и каковы сроки. Это поможет избежать неожиданностей при проверке и сформировать у пользователей реалистичные ожидания.
Справочник: руководство по реализации по файлам
Адаптер v2, разработанный сообществом, затрагивает примерно 13 файлов, и все они находятся в публичном репозитории dbt-labs/dbt. Столбец «Пример Exasol» показывает, как это выглядит на практике — везде подставляйте имя вашего хранилища и его системный каталог.
| Обобщённый путь | Назначение | Пример Exasol |
|---|---|---|
.changes/unreleased/Features-*.yaml | Запись в журнале изменений | Одинаково для всех адаптеров |
crates/dbt-adapter-core/src/lib.rs | Добавление варианта AdapterType::<Warehouse> и ветви quote_char | AdapterType::Exasol |
crates/dbt-xdbc/src/driver.rs | Добавление варианта Backend::<Warehouse> — имени библиотеки ADBC и протокола FFI | Backend::Exasol, имя библиотеки "adbc_driver_exasol" |
crates/dbt-schemas/src/schemas/profiles.rs | Структура <Warehouse>DbConfig + вариант DbConfig::<Warehouse>, подключённый ко всем ветвям сопоставления конфигурации | Структура ExasolDbConfig + вариант DbConfig::Exasol |
crates/dbt-auth/src/<warehouse>/mod.rs | Модуль аутентификации — читает поля конфигурации, получает учётные данные, формирует URI подключения | src/exasol/mod.rs — формирует URI из хоста, порта, имени пользователя/пароля и параметров TLS |
crates/dbt-adapter/src/adapter/adapter_impl.rs | Все ветви исчерпывающего сопоставления match adapter_type() — большинство делегирует обработку существующим реализациям; для нереализованных возможностей используйте unimplemented!() | Exasol добавляет около 10 ветвей, большинство занимает 1–2 строки |
crates/dbt-adapter/src/column/column_builder.rs | Ветвь сопоставления построителя столбцов — преобразует пакеты записей Arrow в объекты столбцов dbt | Exasol => Ok(Self::build_postgres_like(...)) |
crates/dbt-adapter/src/metadata/get_relation.rs | Функция поиска по каталогу — запрашивает системные таблицы для поиска объекта relation по имени | exasol_get_relation() — запрашивает sys.exa_all_tables / sys.exa_all_views |
crates/dbt-adapter/src/relation/relation_object.rs | Ветвь сопоставления — политика кавычек и включение префикса базы данных в полные имена | Policy::new(false, true, true) — база данных исключена, схема и идентификатор заключены в кавычки |
crates/dbt-adapter/src/relation/factory.rs | Добавление хранилища в ветвь RelationStatic в create_static_relation | Exasol добавлен вместе с Postgres, Redshift, DuckDB и другими. |
crates/dbt-adapter/src/sql_types.rs | Ветви для имён столбцов метаданных — DATA_TYPE и имён столбцов для получения списка схем | Exasol использует стандартное имя столбца DATA_TYPE |
crates/dbt-loader/src/dbt_macro_assets/dbt-<warehouse>/dbt_project.yml | Определение проекта плагина макросов | dbt-exasol/dbt_project.yml |
crates/dbt-loader/src/dbt_macro_assets/dbt-<warehouse>/macros/adapters.sql | Все обязательные макросы адаптера | dbt-exasol/macros/adapters.sql — использует sys.* вместо information_schema |
Справочник: полезные команды
# 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