Learning Platform
Глоссарий Troubleshooting
Урок 09.02 · 22 мин
Продвинутый
AdapterCookiecutterScaffoldProject setup

Cookiecutter scaffold: стартер для нового adapter

Писать adapter с нуля — boilerplate. 50+ файлов, точная структура каталогов, правильные setup.py ключи, корректный __init__.py для регистрации в dbt. dbt Labs maintains official cookiecutter template, который генерирует всё это за одну команду.

В этом уроке разбираем, что генерирует scaffold, как его использовать, и что нужно изменить под ваш warehouse.


sdist vs wheel, PyPI: packaging Python проектов

Что такое cookiecutter

Cookiecutter — Python tool для project templating. Принимает template directory с placeholder’ами ({{ adapter_name }}) и генерирует конкретный проект на их основе.

pip install cookiecutter
cookiecutter https://github.com/dbt-labs/dbt-database-adapter-scaffold

Cookiecutter спросит:

adapter_name [my_adapter]: oceanbase
project_name [dbt-oceanbase]: dbt-oceanbase
author_name [your_name]: Lev Neganov
author_email [[email protected]]: [email protected]
copyright_year [2026]: 2026
adapter_description [A dbt adapter for ...]: A dbt adapter for OceanBase
license [Apache-2.0]: Apache-2.0
python_requires [>=3.9]: >=3.9

И сгенерирует ~50 файлов в ./dbt-oceanbase/.


Что генерируется: общая структура

dbt-oceanbase/
├── README.md
├── LICENSE
├── setup.py
├── pyproject.toml
├── MANIFEST.in
├── .gitignore
├── dev-requirements.txt
├── .github/
│   └── workflows/
│       └── ci.yml
├── dbt/
│   ├── adapters/
│   │   ├── __init__.py
│   │   └── oceanbase/
│   │       ├── __init__.py
│   │       ├── __version__.py
│   │       ├── connections.py     # ← ConnectionManager
│   │       ├── credentials.py     # ← Credentials dataclass
│   │       ├── relation.py        # ← Relation subclass
│   │       ├── column.py          # ← Column subclass
│   │       ├── impl.py            # ← Adapter implementation
│   │       └── exceptions.py
│   └── include/
│       └── oceanbase/
│           ├── __init__.py
│           ├── dbt_project.yml    # ← Default project config для adapter
│           ├── profile_template.yml  # ← Шаблон profiles.yml
│           ├── sample_profiles.yml
│           └── macros/
│               ├── adapters.sql   # ← Adapter-specific macros
│               ├── catalog.sql
│               ├── materializations/
│               └── relations/
└── tests/
    ├── __init__.py
    ├── conftest.py
    └── functional/
        └── adapter/
            ├── test_basic.py     # ← Имплементирует dbt-tests-adapter suite
            ├── test_simple_copy.py
            └── ...

Это production-grade scaffold: setup.py с правильными dependencies, GitHub Actions CI, тестовая инфраструктура подключённая к dbt-tests-adapter, документация.


Главные файлы и что в них

setup.py / pyproject.toml

# setup.py
from setuptools import setup, find_namespace_packages

setup(
    name='dbt-oceanbase',
    version='1.0.0',
    description='A dbt adapter for OceanBase',
    long_description=open('README.md').read(),
    long_description_content_type='text/markdown',
    author='Lev Neganov',
    author_email='[email protected]',
    license='Apache-2.0',
    url='https://github.com/levn/dbt-oceanbase',

    packages=find_namespace_packages(include=['dbt.*']),
    package_data={
        'dbt': [
            'include/oceanbase/dbt_project.yml',
            'include/oceanbase/sample_profiles.yml',
            'include/oceanbase/profile_template.yml',
            'include/oceanbase/macros/*.sql',
            'include/oceanbase/macros/**/*.sql',
        ],
    },

    install_requires=[
        'dbt-adapters>=1.7,<2.0',
        'dbt-common>=1.10,<2.0',
        'dbt-core>=1.8,<2.0',
        # Warehouse client library:
        'pymysql>=1.0',   # ← OceanBase compatible с MySQL protocol
    ],

    python_requires='>=3.9',
)

Ключевые моменты:

  • find_namespace_packages(include=['dbt.*']) — это namespace package pattern. dbt-core видит все adapter’ы под dbt.adapters.*.
  • package_data — включить SQL macros в pip-package. Без этого dbt не найдёт их в runtime.
  • install_requires: dbt-adapters, dbt-common, dbt-core — обязательно. Plus warehouse client library (pymysql для OceanBase, snowflake-connector для Snowflake, etc).
  • python_requires — supported Python versions.

dbt/adapters/oceanbase/__init__.py

from dbt.adapters.oceanbase.connections import OceanBaseConnectionManager
from dbt.adapters.oceanbase.connections import OceanBaseCredentials
from dbt.adapters.oceanbase.impl import OceanBaseAdapter

from dbt.adapters.base import AdapterPlugin
from dbt.include import oceanbase

Plugin = AdapterPlugin(
    adapter=OceanBaseAdapter,
    credentials=OceanBaseCredentials,
    include_path=oceanbase.PACKAGE_PATH,
)

Это регистрационная точка для dbt. Когда dbt вызывает pip show dbt-oceanbase, он находит Plugin = AdapterPlugin(...) и регистрирует:

  • adapter: главный класс
  • credentials: dataclass для profiles.yml
  • include_path: путь к SQL macros

Без этого dbt не знает, что adapter существует.

dbt/adapters/oceanbase/credentials.py

from dataclasses import dataclass
from typing import Optional

from dbt.adapters.contracts.connection import Credentials


@dataclass
class OceanBaseCredentials(Credentials):
    host: str
    port: int = 2881
    user: str = 'root'
    password: str = ''
    database: str = 'test'
    schema: str = ''

    @property
    def type(self) -> str:
        return 'oceanbase'

    @property
    def unique_field(self) -> str:
        return self.host

    def _connection_keys(self):
        return ('host', 'port', 'user', 'database', 'schema')

Это dataclass, который описывает структуру profiles.yml для adapter’а. Detailed разбор в следующем уроке.

dbt/adapters/oceanbase/connections.py

from contextlib import contextmanager
from typing import Optional

import pymysql

from dbt.adapters.base import BaseConnectionManager
from dbt.adapters.contracts.connection import (
    AdapterResponse,
    Connection,
    ConnectionState,
)
from dbt.adapters.events.logging import AdapterLogger
from dbt.adapters.exceptions import FailedToConnectError


logger = AdapterLogger('OceanBase')


class OceanBaseConnectionManager(BaseConnectionManager):
    TYPE = 'oceanbase'

    @classmethod
    def open(cls, connection: Connection) -> Connection:
        if connection.state == ConnectionState.OPEN:
            return connection

        credentials = connection.credentials
        try:
            handle = pymysql.connect(
                host=credentials.host,
                port=credentials.port,
                user=credentials.user,
                password=credentials.password,
                database=credentials.database,
            )
        except pymysql.Error as e:
            raise FailedToConnectError(str(e))

        connection.handle = handle
        connection.state = ConnectionState.OPEN
        return connection

    @classmethod
    def get_response(cls, cursor) -> AdapterResponse:
        rows_affected = cursor.rowcount
        return AdapterResponse(
            _message=f'OK',
            rows_affected=rows_affected,
        )

    @contextmanager
    def exception_handler(self, sql: str):
        try:
            yield
        except pymysql.Error as e:
            self.release()
            raise RuntimeError(str(e))

    def cancel(self, connection: Connection):
        connection.handle.close()

Это минимальный ConnectionManager. Детальный разбор в уроке 4.

dbt/adapters/oceanbase/impl.py

from dbt.adapters.sql import SQLAdapter

from dbt.adapters.oceanbase.connections import OceanBaseConnectionManager
from dbt.adapters.oceanbase.relation import OceanBaseRelation
from dbt.adapters.oceanbase.column import OceanBaseColumn


class OceanBaseAdapter(SQLAdapter):
    ConnectionManager = OceanBaseConnectionManager
    Relation = OceanBaseRelation
    Column = OceanBaseColumn

    @classmethod
    def date_function(cls) -> str:
        return 'CURDATE()'

    @classmethod
    def convert_text_type(cls, agate_table, col_idx):
        return 'VARCHAR(255)'

    @classmethod
    def convert_number_type(cls, agate_table, col_idx):
        decimals = agate_table.aggregate(agate.MaxPrecision(col_idx))
        return 'DECIMAL(15, 2)' if decimals else 'BIGINT'

    @classmethod
    def convert_datetime_type(cls, agate_table, col_idx):
        return 'DATETIME'

    @classmethod
    def convert_boolean_type(cls, agate_table, col_idx):
        return 'BOOLEAN'

    # Inherit from SQLAdapter — другие methods наследуются

dbt/include/oceanbase/macros/adapters.sql

-- macros/adapters.sql

{% macro oceanbase__list_schemas(database) %}
  {% call statement('list_schemas', fetch_result=True, auto_begin=False) %}
    SHOW DATABASES
  {% endcall %}
  {{ return(load_result('list_schemas').table) }}
{% endmacro %}


{% macro oceanbase__check_schema_exists(information_schema, schema) %}
  {% call statement('check_schema_exists', fetch_result=True, auto_begin=False) %}
    SELECT COUNT(*)
    FROM information_schema.schemata
    WHERE schema_name = '{{ schema }}'
  {% endcall %}
  {{ return(load_result('check_schema_exists').table) }}
{% endmacro %}


{% macro oceanbase__create_schema(relation) %}
  {%- call statement('create_schema') -%}
    CREATE DATABASE IF NOT EXISTS {{ relation.without_identifier() }}
  {%- endcall -%}
{% endmacro %}


{% macro oceanbase__drop_schema(relation) %}
  {%- call statement('drop_schema') -%}
    DROP DATABASE IF EXISTS {{ relation.without_identifier() }}
  {%- endcall -%}
{% endmacro %}


{% macro oceanbase__get_columns_in_relation(relation) %}
  {% call statement('get_columns_in_relation', fetch_result=True) %}
    SHOW COLUMNS FROM {{ relation }}
  {% endcall %}
  {% set table = load_result('get_columns_in_relation').table %}
  {{ return(...) }}
{% endmacro %}

OceanBase использует MySQL-compatible syntax: SHOW DATABASES, SHOW COLUMNS. Это override default’ов (которые используют ANSI information_schema).


После генерации: что менять

Cookiecutter генерирует scaffold, не working adapter. Дальше вам надо:

Шаг 1: Customize credentials

В credentials.py добавьте поля специфичные для вашего warehouse:

@dataclass
class OceanBaseCredentials(Credentials):
    host: str
    port: int = 2881
    user: str = 'root'
    password: str = ''
    database: str = 'test'
    schema: str = ''
    
    # OceanBase-specific:
    tenant: Optional[str] = None
    cluster: Optional[str] = None
    
    # Standard:
    threads: int = 4

Шаг 2: Implement connection

В connections.py:

@classmethod
def open(cls, connection: Connection) -> Connection:
    credentials = connection.credentials
    
    # OceanBase connection строка: user@tenant#cluster
    if credentials.tenant:
        user_str = f'{credentials.user}@{credentials.tenant}'
        if credentials.cluster:
            user_str += f'#{credentials.cluster}'
    else:
        user_str = credentials.user
    
    handle = pymysql.connect(
        host=credentials.host,
        port=credentials.port,
        user=user_str,
        password=credentials.password,
        database=credentials.database,
    )
    
    connection.handle = handle
    connection.state = ConnectionState.OPEN
    return connection

Шаг 3: Configure types

В impl.py:

@classmethod
def convert_text_type(cls, agate_table, col_idx):
    # OceanBase: max VARCHAR length 65535
    return 'VARCHAR(65535)'

@classmethod
def convert_number_type(cls, agate_table, col_idx):
    return 'DOUBLE'

Шаг 4: Run dbt-tests-adapter suite

cd dbt-oceanbase/
pip install -e .
pip install -r dev-requirements.txt
pytest tests/functional/adapter/

Most tests падут изначально. Read failures, fix one by one. Это test-driven adapter development.

Шаг 5: Setup local development

# Запустить OceanBase локально (Docker)
docker run -d --name oceanbase \
    -p 2881:2881 \
    oceanbase/oceanbase-ce:latest

# Создать test profiles.yml
mkdir -p ~/.dbt
cat > ~/.dbt/profiles.yml << EOF
dbt-oceanbase-test:
  target: dev
  outputs:
    dev:
      type: oceanbase
      host: localhost
      port: 2881
      user: root
      password: ''
      database: test
EOF

# Test через example project
git clone https://github.com/dbt-labs/dbt-jaffle-shop
cd dbt-jaffle-shop
dbt debug --profiles-dir ~/.dbt --profile dbt-oceanbase-test
dbt run

Какие тесты в scaffold

Scaffold включает tests/functional/adapter/ — это suite от dbt-tests-adapter package, который dbt Labs maintains для testing любого adapter’а.

Главные test classes:

  • BaseSimpleMaterializations — view, table, ephemeral базовые тесты
  • BaseIncremental — incremental тесты
  • BaseSeeds — seed CSV -> table
  • BaseSnapshots — snapshot strategies
  • BaseDocsGenerate — docs generation
  • BaseSingularTestTimezones — singular tests
  • BaseDocsGenerateBase — catalog generation

Каждый класс — это base class с тестами. Ваш scaffold имеет local subclasses, например:

# tests/functional/adapter/test_basic.py
from dbt.tests.adapter.basic.test_base import BaseSimpleMaterializations
from dbt.tests.adapter.basic.test_singular_tests import BaseSingularTests

class TestSimpleMaterializationsOceanBase(BaseSimpleMaterializations):
    pass  # Inherit all tests, nothing custom

class TestSingularTestsOceanBase(BaseSingularTests):
    pass

Running pytest tests/functional/adapter/test_basic.py запускает full suite. Каждый test:

  1. Создаёт temp dbt project
  2. Запускает dbt commands (run, test, seed)
  3. Проверяет результат (table created, rows correct, etc.)

Если ваш adapter работает correctly — tests pass. Иначе failures указывают на bugs.

Полная suite в dbt-tests-adapter package — ~50 test classes. Это specification, что должен делать adapter для dbt-compatibility.


CI Setup (GitHub Actions)

Scaffold генерирует .github/workflows/ci.yml:

name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      oceanbase:
        image: oceanbase/oceanbase-ce:latest
        ports:
          - 2881:2881
    
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11']
    
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      - run: pip install -e .
      - run: pip install -r dev-requirements.txt
      - run: pytest tests/functional/adapter/ -v

Run dbt-tests-adapter suite на каждый push. Production-grade.


Когда НЕ использовать scaffold

Scaffold — отправная точка. В нескольких сценариях лучше начать с zero:

  1. Existing similar adapter — если делаете fork существующего (например, dbt-postgres -> dbt-greenplum) — fork существующий repo, не scaffold.

  2. Не SQL warehouse — scaffold optimized для SQLAdapter. Для BaseAdapter (REST API, Spark-like) — лучше скопировать структуру с dbt-spark или dbt-bigquery.

  3. Learning purposes — если изучаете adapter API, scaffold скрывает много deta’el. Лучше написать с нуля для понимания.

Но для production adapter scaffold — best starting point.


Попробуй сам

  1. Установите cookiecutter:

    pip install cookiecutter
  2. Сгенерируйте scaffold для гипотетического warehouse:

    cookiecutter https://github.com/dbt-labs/dbt-database-adapter-scaffold
    # adapter_name: testdb
    # project_name: dbt-testdb
    # ... остальные defaults
  3. Изучите generated файлы:

    cd dbt-testdb/
    find . -type f -name "*.py" -o -name "*.sql" -o -name "*.yml" | head -30
  4. Откройте dbt/adapters/testdb/impl.py. Это MyAdapter template. Найдите все TODO комментарии — это места, где нужно ваш код.

  5. Прочитайте tests/functional/adapter/test_basic.py. Это инкорпорированные base tests от dbt-tests-adapter.

  6. Запустите pip install -e .. Adapter зарегистрирован в pip namespace. Проверьте:

    python -c "from dbt.adapters.testdb import Plugin; print(Plugin)"

    Должно вывести AdapterPlugin object.

  7. Bonus: подключите к реальному warehouse (например, SQLite через sqlite3 library) и сделайте hello-world. Это часы работы.


Ключевые выводы

  1. dbt-database-adapter-scaffold — official cookiecutter template для adapter’ов. Генерирует ~50 файлов: src, tests, CI, packaging.

  2. AdapterPlugin в __init__.py — регистрационная точка. dbt находит adapter через namespace package pattern.

  3. package_data в setup.py — включить SQL macros в pip-package. Без этого dbt не находит них в runtime.

  4. dbt-tests-adapter — official test suite. Scaffold подключает её. Прохождение всех тестов — критерий Trusted Adapter.

  5. 5 steps после scaffold: customize credentials, implement connection, configure types, run tests, setup local dev.

  6. Scaffold — отправная точка, не финал. Production adapter — месяцы работы после scaffold.

Проверка знанийKnowledge check
После cookiecutter генерации adapter не регистрируется в dbt — dbt debug говорит 'unknown type: oceanbase'. Причина?
ОтветAnswer
Несколько возможных причин. Систематический debug:\n\n**Cause 1 — Не установлен в pip namespace**:\n\n```bash\ncd dbt-oceanbase/\npip install -e .\n```\n\nWithout this, dbt не находит `dbt.adapters.oceanbase` в Python path. Это первое что check.\n\nVerify:\n\n```bash\npip show dbt-oceanbase\n# Should print package info, location\n\npython -c "from dbt.adapters.oceanbase import Plugin; print(Plugin)"\n# Should print AdapterPlugin object\n```\n\nЕсли вторая команда падает с ImportError — package не установлен или path неправильный.\n\n**Cause 2 — Wrong namespace package configuration**:\n\n`setup.py` должен иметь:\n\n```python\npackages=find_namespace_packages(include=['dbt.*']),\n```\n\nНЕ `find_packages()` (без namespace). `dbt` — это namespace package разделённый между dbt-core, dbt-adapters, dbt-oceanbase, ваш adapter.\n\nVerify:\n\n```bash\npython -c "import dbt.adapters; print(dbt.adapters.__path__)"\n# Should list multiple paths: dbt-core/, dbt-adapters/, dbt-oceanbase/\n```\n\nЕсли только один path — namespace не работает.\n\n**Cause 3 — `__init__.py` не имеет Plugin**:\n\nВ `dbt/adapters/oceanbase/__init__.py` должно быть:\n\n```python\nfrom dbt.adapters.oceanbase.impl import OceanBaseAdapter\nfrom dbt.adapters.oceanbase.connections import OceanBaseCredentials\nfrom dbt.adapters.base import AdapterPlugin\nfrom dbt.include import oceanbase\n\nPlugin = AdapterPlugin(\n adapter=OceanBaseAdapter,\n credentials=OceanBaseCredentials,\n include_path=oceanbase.PACKAGE_PATH,\n)\n```\n\nЕсли `Plugin` не определён — dbt не находит регистрацию.\n\nVerify:\n\n```bash\npython -c "from dbt.adapters.oceanbase import Plugin; print(type(Plugin))"\n# Should print: <class 'dbt.adapters.base.plugin.AdapterPlugin'>\n```\n\n**Cause 4 — Wrong type name in profiles.yml**:\n\nВ `profiles.yml`:\n\n```yaml\ndev:\n type: oceanbase # ← должно совпадать с OceanBaseCredentials.type property\n```\n\nИ в credentials.py:\n\n```python\n@property\ndef type(self) -> str:\n return 'oceanbase' # ← должно соответствовать profile\n```\n\nMismatch -> 'unknown type'.\n\n**Cause 5 — Include path неправильно**:\n\nВ `dbt/include/oceanbase/__init__.py` должно быть:\n\n```python\nfrom pathlib import Path\nPACKAGE_PATH = Path(__file__).parent\n```\n\nЭто используется в `AdapterPlugin(include_path=oceanbase.PACKAGE_PATH)` для нахождения macros и project config.\n\n**Cause 6 — Conflicting installations**:\n\nЕсли установлены конфликтующие versions:\n\n```bash\npip list | grep dbt\n# Multiple dbt-adapters? dbt-core версия mismatch?\n```\n\nFix: `pip install --force-reinstall dbt-oceanbase\'\n\n**Cause 7 — Python path issues**:\n\nVirtual environment не активирован или wrong Python:\n\n```bash\nwhich python\nwhich pip\n# Should be same venv\n\nwhich dbt\n# Should be in same venv\n```\n\nIf mismatch — install в wrong env.\n\n**Systematic debug**:\n\n```bash\n# Step 1\npip install -e .\npip show dbt-oceanbase\n\n# Step 2\npython -c "from dbt.adapters.oceanbase import Plugin; print(Plugin)"\n\n# Step 3\npython -c "from dbt.adapters.factory import FACTORY; print(FACTORY.plugins.keys())"\n# Should include 'oceanbase'\n\n# Step 4\ndbt --debug debug --profile dbt-oceanbase\n# Read full debug output\n```\n\n`FACTORY.plugins` — internal dict в dbt-core. Если ваш adapter не в keys — registration failed.\n\n**Common scaffold issues**:\n\n1. Forgot `pip install -e .` — most common.\n2. `find_packages` вместо `find_namespace_packages` — scaffold обычно сделает correctly, but check.\n3. Typo в `type` property — оstrich-easy.\n4. Old `dbt` directory remains в site-packages с conflict — `pip uninstall dbt-oceanbase` then re-install.\n\nЭто **первая crucial step** после scaffold — make sure adapter registered. До этого никакой test не запустится.
Проверка знанийKnowledge check
После cookiecutter scaffold, dbt-tests-adapter suite даёт 50 failures. Senior спрашивает: 'это нормально или сломанный adapter?'
ОтветAnswer
**Это абсолютно нормально**. Scaffold — отправная точка, не working adapter.\n\n**Что генерирует scaffold**:\n\nScaffold создаёт **structural** код — папки, файлы, скeleton. Но конкретная implementation для каждого warehouse-specific behavior — **ваша работа**.\n\n**Типичная картина после scaffold**:\n\n```\n=========================== short test summary ===========================\nFAILED tests/functional/adapter/test_basic.py::TestSimpleMaterializationsOceanBase::test_base\nFAILED tests/functional/adapter/test_basic.py::TestSimpleMaterializationsOceanBase::test_ephemeral\nFAILED tests/functional/adapter/test_basic.py::TestSingularTestsOceanBase::test_singular_test\nFAILED tests/functional/adapter/test_seeds.py::TestSeedsOceanBase::test_simple_seed\nFAILED tests/functional/adapter/test_incremental.py::TestIncrementalOceanBase::test_incremental\n... 45 more failures\n=========================== 50 failed, 5 passed in 120s ===========================\n```\n\n**Почему**:\n\nScaffold заполняет defaults через SQLAdapter inheritance. Но **default behavior** обычно не совпадает с warehouse-specific нюансами:\n\n1. **Type conversions wrong**: scaffold returns 'TEXT' для convert_text_type. OceanBase ожидает 'VARCHAR(255)'. Tests требующие столбцов с правильным типом — fail.\n\n2. **SQL syntax wrong**: scaffold uses ANSI `information_schema.tables`. OceanBase использует `SHOW TABLES` для list_relations. Test пытается найти relation — fails.\n\n3. **Quoting policy wrong**: scaffold uses default (mixed). OceanBase может быть case-insensitive. Test проверяет quoted relation — fails.\n\n4. **Connection management**: scaffold имеет skeleton open(), но specific OceanBase parameters не handled. Connection пуйка.\n\n5. **DML syntax**: `CREATE TABLE AS` may work, but `MERGE INTO` syntax отличается между warehouses. Incremental tests fail.\n\n**Это by design**:\n\nScaffold предоставляет **structure**, не **content**. Content — это knowledge about your specific warehouse, который только вы можете предоставить.\n\n**Workflow для adapter development**:\n\n**Step 1 — Run все tests, list failures**:\n\n```bash\npytest tests/functional/adapter/ -v 2>&1 | grep FAILED > failures.txt\n```\n\n**Step 2 — Pick один test, run в isolation**:\n\n```bash\npytest tests/functional/adapter/test_basic.py::TestSimpleMaterializationsOceanBase::test_base -v --tb=long\n```\n\nRead full error. Какой query упал? Какая expected vs actual?\n\n**Step 3 — Fix one issue**:\n\nЕсли error на SQL syntax — override macro. Если на Python — override method in impl.py.\n\nNote: dbt-tests-adapter не only checks 'does it work'. Many tests check **conformance** к dbt's expected behavior. E.g., test_simple_seed проверяет что after `dbt seed` data в table identical к CSV. Если ваш adapter accidentally uppercases column names — test fails.\n\n**Step 4 — Re-run, repeat**:\n\n```bash\npytest tests/functional/adapter/test_basic.py::TestSimpleMaterializationsOceanBase -v\n```\n\nVerify fix works. If new failures appear — your fix может broke other tests. Iterate.\n\n**Step 5 — Group fixes**:\n\nSome fixes solve multiple tests:\n\n- Fixing quoting -> 10 tests pass\n- Fixing type conversions -> 5 tests pass\n- Fixing SHOW TABLES vs information_schema -> 8 tests pass\n\nПриоритизируйте по impact.\n\n**Typical timeline для full pass**:\n\n| Stage | Tests passing | Time |\n|-------|--------------|------|\n| Scaffold | 5 / 50 (basic Python) | 0 |\n| Connection fixed | 15 / 50 | 1 week |\n| Quoting + types | 30 / 50 | 1 month |\n| All basic materializations | 40 / 50 | 2 months |\n| All advanced (snapshots, incremental) | 48 / 50 | 4 months |\n| Edge cases | 50 / 50 | 6+ months |\n\nFull adapter implementation — **months of focused work**.\n\n**For Trusted Adapter Program** требуется 100% pass плюс additional checks (security, docs, maintenance commitment).\n\n**Expectation setting**:\n\nЕсли вы говорите команде 'I'll write adapter for OceanBase':\n\n- **PoC**: 1 week (5-15 tests pass, basic queries work)\n- **Internal use**: 2-3 months (most tests pass, daily use OK)\n- **Public release**: 6 months (all tests pass, docs, CI/CD, security)\n- **Trusted Adapter Program**: 9-12 months (review process, community feedback)\n\nЭто **не weekend project**. Senior должен заранее set expectations.\n\n**Encouragement**: scaffold + dbt-tests-adapter дает **test-driven workflow**. Tests показывают exactly что нужно изменить. Без них — flying blind. **Use them**.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Что генерирует dbt-database-adapter-scaffold cookiecutter?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5