Learning Platform
Глоссарий Troubleshooting
Урок 10.03 · 25 мин
Продвинутый
AdapterMacrosRequired interfaceSQL

Обязательные macros для adapter

Production-grade adapter имеет ~15 обязательных macros в dbt/include/<name>/macros/adapters.sql. Эти macros — specification для adapter conformance. Each used by dbt-core в specific operations.

В этом уроке — полный checklist с примерами implementations.


Определение macros: синтаксис, аргументы, return (dbt I) adapter.dispatch: multi-warehouse macros (dbt II)

Главные macros и их роли

Required macros и их использование

Schema management macros

<adapter>__list_schemas(database)

Purpose: Return list of schemas в database.

Default (ANSI SQL):

{% macro default__list_schemas(database) %}
  {% call statement('list_schemas', fetch_result=True, auto_begin=False) %}
    SELECT DISTINCT schema_name
    FROM {{ information_schema_name(database) }}.SCHEMATA
  {% endcall %}
  {{ return(load_result('list_schemas').table) }}
{% endmacro %}

OceanBase override:

{% 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 %}

BigQuery override:

{% macro bigquery__list_schemas(database) %}
  {% set sql %}
    SELECT schema_name FROM `{{ database }}`.INFORMATION_SCHEMA.SCHEMATA
  {% endset %}
  {% set result = run_query(sql) %}
  {{ return(result) }}
{% endmacro %}

<adapter>__create_schema(relation)

Purpose: Create schema if not exists.

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

relation.without_identifier() returns database.schema (drops identifier).

OceanBase:

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

OceanBase uses CREATE DATABASE instead of CREATE SCHEMA.

<adapter>__drop_schema(relation)

{% macro default__drop_schema(relation) %}
  {%- call statement('drop_schema') -%}
    DROP SCHEMA IF EXISTS {{ relation.without_identifier() }} CASCADE
  {%- endcall -%}
{% endmacro %}

CASCADE — drops all relations в schema. Used by dbt clean.

<adapter>__check_schema_exists(information_schema, schema)

Purpose: Check if schema exists.

{% macro default__check_schema_exists(information_schema, schema) %}
  {% set sql -%}
    SELECT COUNT(*)
    FROM {{ information_schema }}.SCHEMATA
    WHERE SCHEMA_NAME = '{{ schema }}'
      AND CATALOG_NAME = '{{ information_schema.database }}'
  {%- endset %}
  {% set result = run_query(sql) %}
  {{ return(result) }}
{% endmacro %}

Relation introspection macros

<adapter>__list_relations_without_caching(schema_relation)

Purpose: List all tables/views в schema. Critical для cache initialization.

{% macro default__list_relations_without_caching(schema_relation) %}
  {% call statement('list_relations_without_caching', fetch_result=True) %}
    SELECT
      '{{ schema_relation.database }}' AS database,
      tablename AS name,
      schemaname AS schema,
      'table' AS type
    FROM {{ information_schema_name(schema_relation.database) }}.TABLES
    WHERE schemaname = '{{ schema_relation.schema }}'
    
    UNION ALL
    
    SELECT
      '{{ schema_relation.database }}' AS database,
      viewname AS name,
      schemaname AS schema,
      'view' AS type
    FROM {{ information_schema_name(schema_relation.database) }}.VIEWS
    WHERE schemaname = '{{ schema_relation.schema }}'
  {% endcall %}
  {{ return(load_result('list_relations_without_caching').table) }}
{% endmacro %}

Result format: 4-column table — database, name, schema, type.

OceanBase:

{% macro oceanbase__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,
      CASE table_type
        WHEN 'BASE TABLE' THEN 'table'
        WHEN 'VIEW' THEN 'view'
      END AS type
    FROM information_schema.tables
    WHERE table_schema = '{{ schema_relation.schema }}'
  {% endcall %}
  {{ return(load_result('list_relations_without_caching').table) }}
{% endmacro %}

<adapter>__get_columns_in_relation(relation)

Purpose: Get columns с types для relation.

{% macro default__get_columns_in_relation(relation) %}
  {% call statement('get_columns_in_relation', fetch_result=True) %}
    SELECT
      column_name,
      data_type,
      character_maximum_length,
      numeric_precision,
      numeric_scale
    FROM {{ information_schema_name(relation.database) }}.columns
    WHERE table_schema = '{{ relation.schema }}'
      AND table_name = '{{ relation.identifier }}'
    ORDER BY ordinal_position
  {% endcall %}
  {% set table = load_result('get_columns_in_relation').table %}
  {{ return(sql_convert_columns_in_relation(table)) }}
{% endmacro %}

sql_convert_columns_in_relation is helper from base — converts table rows to Column instances.

Snowflake:

{% macro snowflake__get_columns_in_relation(relation) %}
  {%- set sql -%}
    DESCRIBE TABLE {{ relation }}
  {%- endset -%}
  {%- set result = run_query(sql) -%}
  -- Parse Snowflake-specific output format
  ...
{% endmacro %}

DDL macros

<adapter>__rename_relation(from_relation, to_relation)

{% macro default__rename_relation(from_relation, to_relation) -%}
  {% call statement('rename_relation') -%}
    ALTER TABLE {{ from_relation }} RENAME TO {{ to_relation }}
  {%- endcall %}
{% endmacro %}

Note: RENAME TO {{ to_relation }} requires то-relation WITHOUT schema prefix (just identifier). Adapter-specific:

{% macro postgres__rename_relation(from_relation, to_relation) -%}
  ALTER TABLE {{ from_relation }} RENAME TO {{ to_relation.identifier }}
{% endmacro %}

{% macro snowflake__rename_relation(from_relation, to_relation) -%}
  ALTER TABLE {{ from_relation }} RENAME TO {{ to_relation }}
{% endmacro %}

Subtle differences.

<adapter>__truncate_relation(relation)

{% macro default__truncate_relation(relation) -%}
  {% call statement('truncate_relation') -%}
    TRUNCATE TABLE {{ relation }}
  {%- endcall %}
{% endmacro %}

Some warehouses не support TRUNCATE:

{% macro myadapter__truncate_relation(relation) -%}
  -- MyAdapter has no TRUNCATE — use DELETE
  {% call statement('truncate_relation') -%}
    DELETE FROM {{ relation }}
  {%- endcall %}
{% endmacro %}

<adapter>__drop_relation(relation)

{% macro default__drop_relation(relation) -%}
  {% call statement('drop_relation', auto_begin=False) -%}
    DROP {{ relation.type }} IF EXISTS {{ relation }} CASCADE
  {%- endcall %}
{% endmacro %}

relation.type — ‘table’, ‘view’, ‘cte’. CASCADE drops dependent objects.

<adapter>__alter_column_type(relation, column_name, new_column_type)

{% macro default__alter_column_type(relation, column_name, new_column_type) -%}
  {%- set tmp_column = column_name + "__dbt_alter" -%}
  {% call statement('alter_column_type') %}
    ALTER TABLE {{ relation }}
      ADD COLUMN {{ tmp_column }} {{ new_column_type }};
    
    UPDATE {{ relation }}
      SET {{ tmp_column }} = {{ column_name }};
    
    ALTER TABLE {{ relation }}
      DROP COLUMN {{ column_name }};
    
    ALTER TABLE {{ relation }}
      RENAME COLUMN {{ tmp_column }} TO {{ column_name }};
  {% endcall %}
{% endmacro %}

For warehouses без direct ALTER COLUMN TYPE (e.g., SQLite, older Snowflake).

For warehouses с direct ALTER:

{% macro postgres__alter_column_type(relation, column_name, new_column_type) -%}
  ALTER TABLE {{ relation }}
    ALTER COLUMN {{ column_name }} TYPE {{ new_column_type }}
    USING {{ column_name }}::{{ new_column_type }};
{% endmacro %}

DML helpers

<adapter>__create_table_as(temporary, relation, sql)

{% macro default__create_table_as(temporary, relation, sql) -%}
  {%- set sql_header = config.get('sql_header', none) -%}

  {{ sql_header if sql_header is not none }}

  CREATE {% if temporary %}TEMPORARY {% endif %}TABLE
    {{ relation.include(database=(not temporary), schema=(not temporary)) }}
  AS (
    {{ sql }}
  );
{%- endmacro %}

Temporary tables — created без db/schema (just identifier).

Snowflake имеет TRANSIENT tables:

{% macro snowflake__create_table_as(temporary, relation, sql) -%}
  {%- set transient = config.get('transient', default=false) -%}
  
  CREATE OR REPLACE
    {% if transient %}TRANSIENT {% endif %}
    {% if temporary %}TEMPORARY {% endif %}
    TABLE {{ relation }} AS ({{ sql }})
{%- endmacro %}

<adapter>__create_view_as(relation, sql)

{% macro default__create_view_as(relation, sql) -%}
  CREATE OR REPLACE VIEW {{ relation }} AS (
    {{ sql }}
  );
{%- endmacro %}

Standard.

BigQuery не supports OR REPLACE:

{% macro bigquery__create_view_as(relation, sql) -%}
  CREATE VIEW {{ relation }} AS ({{ sql }})
{%- endmacro %}

Timestamp macros

<adapter>__current_timestamp()

Purpose: Used by snapshots, audit.

{% macro default__current_timestamp() %}
  CURRENT_TIMESTAMP
{% endmacro %}

Snowflake:

{% macro snowflake__current_timestamp() %}
  current_timestamp::TIMESTAMP_NTZ
{% endmacro %}

BigQuery:

{% macro bigquery__current_timestamp() %}
  CURRENT_TIMESTAMP()
{% endmacro %}

<adapter>__current_timestamp_in_utc()

{% macro default__current_timestamp_in_utc() %}
  {{ adapter.dispatch('current_timestamp_in_utc_backcompat', 'dbt')() }}
{% endmacro %}

UTC-specific. Used когда need timezone-aware timestamps.


information_schema macros

<adapter>__information_schema_name(database)

Purpose: Return location of information_schema (varies by warehouse).

{% macro default__information_schema_name(database) %}
  {%- if database -%}
    {{ adapter.quote_as_configured(database, 'database') }}.information_schema
  {%- else -%}
    information_schema
  {%- endif -%}
{% endmacro %}

Snowflake:

{% macro snowflake__information_schema_name(database) %}
  {%- if database -%}
    {{ database }}.information_schema
  {%- else -%}
    information_schema
  {%- endif -%}
{% endmacro %}

BigQuery: information_schema is per-dataset:

{% macro bigquery__information_schema_name(database) %}
  -- BigQuery doesn't have a global information_schema
  -- This may not be applicable
{% endmacro %}

Grants macros (optional)

If adapter supports GRANT/REVOKE:

<adapter>__copy_grants()

Purpose: Return true if warehouse supports COPY GRANTS clause (Snowflake-specific).

{% macro default__copy_grants() %}
  {{ return(False) }}
{% endmacro %}

{% macro snowflake__copy_grants() %}
  {%- set copy_grants_config = config.get('copy_grants', false) -%}
  {{ return(copy_grants_config) }}
{% endmacro %}

<adapter>__support_multiple_grantees_per_dcl_statement()

{% macro default__support_multiple_grantees_per_dcl_statement() %}
  {{ return(True) }}
{% endmacro %}

True if can do GRANT SELECT TO role1, role2. False if must be separate statements.

<adapter>__get_show_grant_sql(relation)

{% macro default__get_show_grant_sql(relation) %}
  show grants on {{ relation }}
{% endmacro %}

Special: snapshot_get_time

For snapshots:

{% macro default__snapshot_get_time() -%}
  {{ current_timestamp() }}
{%- endmacro %}

get_catalog macros

For dbt docs generate:

{% macro default__get_catalog(information_schema, schemas) %}
  {% set query %}
    WITH tables AS (
        SELECT
            table_catalog AS table_database,
            table_schema,
            table_name,
            ...
        FROM {{ information_schema }}.tables
        WHERE table_schema IN ({% for schema in schemas %}'{{ schema }}'{%- if not loop.last %}, {% endif -%}{% endfor %})
    ),
    columns AS (
        SELECT
            ...
        FROM {{ information_schema }}.columns
    )
    SELECT * FROM tables JOIN columns USING (...)
  {% endset %}
  
  {{ return(run_query(query)) }}
{% endmacro %}

Generates metadata catalog для documentation site.


Complete adapter macros file template

-- dbt/include/myadapter/macros/adapters.sql

{% macro myadapter__list_schemas(database) %}
  ...
{% endmacro %}

{% macro myadapter__create_schema(relation) %}
  ...
{% endmacro %}

{% macro myadapter__drop_schema(relation) %}
  ...
{% endmacro %}

{% macro myadapter__check_schema_exists(information_schema, schema) %}
  ...
{% endmacro %}

{% macro myadapter__list_relations_without_caching(schema_relation) %}
  ...
{% endmacro %}

{% macro myadapter__get_columns_in_relation(relation) %}
  ...
{% endmacro %}

{% macro myadapter__rename_relation(from_relation, to_relation) %}
  ...
{% endmacro %}

{% macro myadapter__truncate_relation(relation) %}
  ...
{% endmacro %}

{% macro myadapter__drop_relation(relation) %}
  ...
{% endmacro %}

{% macro myadapter__current_timestamp() %}
  ...
{% endmacro %}

{% macro myadapter__create_table_as(temporary, relation, sql) %}
  ...
{% endmacro %}

{% macro myadapter__create_view_as(relation, sql) %}
  ...
{% endmacro %}

-- Optional: grants
{% macro myadapter__copy_grants() %}
  ...
{% endmacro %}

-- Optional: catalog
{% macro myadapter__get_catalog(information_schema, schemas) %}
  ...
{% endmacro %}

15+ macros minimum. Production adapter — 30-50.


Попробуй сам

  1. Find dbt-postgres macros file:
find $(pip show dbt-postgres | grep Location | cut -d' ' -f2) -name "adapters.sql"
  1. Open и read. Compare к default__ versions в dbt-adapters:
find $(pip show dbt-adapters | grep Location | cut -d' ' -f2) -name "adapters.sql"
  1. Identify which macros override vs inherit.

  2. Pick one macro (e.g., list_relations_without_caching). Compare implementation across 3 adapters (postgres, snowflake, duckdb).

  3. For your custom adapter, start с минимума:

    • list_schemas
    • check_schema_exists
    • create_schema, drop_schema
    • list_relations_without_caching
    • get_columns_in_relation
    • drop_relation
    • rename_relation
    • current_timestamp

8 macros = dbt run works для basic models.


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

  1. 15+ обязательных macros для production adapter. 8 minimum для basic dbt run.

  2. Schema management: list_schemas, create_schema, drop_schema, check_schema_exists.

  3. Relation introspection: list_relations_without_caching, get_columns_in_relation.

  4. DDL operations: rename_relation, truncate_relation, drop_relation, alter_column_type.

  5. DML helpers: create_table_as, create_view_as.

  6. Misc: current_timestamp, information_schema_name.

  7. Optional: grants macros, get_catalog (для docs generate).

  8. Default versions в dbt-adapters/dbt/include/global_project/macros/adapters/ — use ANSI SQL. Override only when warehouse-specific.

  9. Adapter-specific через <adapter>__macro_name. Dispatch finds it automatically.

  10. Reference: всегда смотрите dbt-postgres, dbt-snowflake, dbt-bigquery sources как inspiration.

Проверка знанийKnowledge check
Какие macros minimum для dbt run работало на adapter (НЕ только dbt debug)?
ОтветAnswer
Minimum для `dbt run` simple model — 8 macros плюс встроенные materializations.\n\n**Минимум**:\n\n**Schema management** (3):\n\n1. `list_schemas` — для cache initialization\n2. `create_schema` — first run создаёт target schema\n3. `check_schema_exists` — verify schema available\n\n**Relation introspection** (2):\n\n4. `list_relations_without_caching` — initialize cache, know existing relations\n5. `get_columns_in_relation` — для contract validation, type checks\n\n**DDL** (2):\n\n6. `drop_relation` — cleanup intermediate / backup, for re-runs\n7. `rename_relation` — backup-rename swap pattern\n\n**Helpers** (1):\n\n8. `current_timestamp` — for snapshots (если используете), audit hooks\n\n**Что НЕ обязательно для basic dbt run**:\n\n- `alter_column_type` — needed только для on_schema_change в incremental\n- `truncate_relation` — for table replacement OR `drop_relation` + create works\n- `drop_schema` — only for `dbt clean`\n- `copy_grants` — only for Snowflake-style copy_grants config\n- `get_catalog` — only for `dbt docs generate`\n- `snapshot_get_time` — only for snapshots\n\n**Materializations**:\n\nMaterializations либо inherit defaults from dbt-adapters package, либо override:\n\n```jinja\n-- Default works для most:\n-- materialization_view_default\n-- materialization_table_default\n-- materialization_incremental_default\n\n-- Override если нужны warehouse-specific:\n{% materialization view, adapter='myadapter' %}\n ...\n{% endmaterialization %}\n```\n\n**Built-in DDL macros используются by materializations**:\n\n- `create_table_as(temporary, relation, sql)` — for table materialization\n- `create_view_as(relation, sql)` — for view materialization\n\nIf default не работает, override:\n\n```jinja\n{% macro myadapter__create_table_as(temporary, relation, sql) %}\n CREATE TABLE {{ relation }} AS ({{ sql }})\n{% endmacro %}\n```\n\n**Iterative development plan**:\n\n**Step 1 — dbt debug passes**:\n\n```\n8 macros:\n list_schemas, check_schema_exists\nPlus:\n ConnectionManager (open, get_response, exception_handler, cancel)\n Credentials\n AdapterPlugin registered\n```\n\n**Step 2 — Simple view materialization works**:\n\nAdd:\n - create_schema\n - create_view_as (or inherit default)\n - list_relations_without_caching\n - drop_relation\n\nTest:\n```sql\n{{ config(materialized='view') }}\nSELECT 1 AS id\n```\n\n`dbt run` should succeed.\n\n**Step 3 — Simple table materialization works**:\n\nAdd:\n - create_table_as (or inherit)\n - rename_relation\n\nTest:\n```sql\n{{ config(materialized='table') }}\nSELECT 1 AS id\n```\n\n**Step 4 — Incremental works**:\n\nAdd:\n - get_columns_in_relation (already added)\n - get_incremental_merge_sql или get_incremental_delete_insert_sql (можно use defaults)\n - on_schema_change handling\n\n**Step 5 — Snapshots**:\n\nAdd:\n - snapshot_get_time\n - snapshot_string_as_time\n - snapshot_check_strategy, snapshot_timestamp_strategy\n - build_snapshot_table\n\n**Step 6 — Seeds**:\n\nAdd:\n - get_csv_sql\n - load_csv_rows\n - Type conversions (`convert_*_type` in impl.py)\n\n**Step 7 — Docs generate**:\n\nAdd:\n - get_catalog\n\n**Step 8 — Full conformance**:\n\nRun dbt-tests-adapter:\n\n```bash\npytest tests/functional/adapter/ -v\n```\n\nAddress each failure individually.\n\n**Realistic timeline**:\n\n- Week 1: 8 minimal macros, debug + simple view works\n- Week 2: table materialization\n- Week 3: incremental\n- Month 2: snapshots, seeds\n- Month 3: docs, catalog\n- Month 4-6: full conformance\n\n**Reference order — what's most critical first**:\n\n1. **Schema** (create_schema, list_schemas, check_schema_exists) — без этого первый run fails\n2. **Relation listing** (list_relations_without_caching) — без этого cache empty\n3. **Column introspection** (get_columns_in_relation) — нужно для cache + contracts\n4. **DDL** (drop_relation, rename_relation, create_table_as, create_view_as) — для materializations\n5. **Timestamp** (current_timestamp) — для snapshots/audit\n\n**Practical advice**:\n\n- Implement 8 macros в первый week\n- Test `dbt run` на simple model\n- Iterate based on failures\n- Use dbt-tests-adapter для discover gaps\n\nЭто **focused approach** для adapter development. Не try implement everything at once.
Проверка знанийKnowledge check
default__list_relations_without_caching использует information_schema. На warehouse без information_schema (e.g., SQLite, DuckDB pre-1.x) — что делать?
ОтветAnswer
Override macro для warehouse-specific introspection.\n\n**Problem**:\n\nANSI SQL мandates information_schema:\n- information_schema.SCHEMATA\n- information_schema.TABLES\n- information_schema.COLUMNS\n- information_schema.VIEWS\n\nBut some warehouses don't have it или have variant:\n\n- **SQLite**: no information_schema. Use `sqlite_master` table\n- **DuckDB**: has information_schema но also `duckdb_tables()` function\n- **Spark**: `SHOW TABLES`, `DESCRIBE TABLE`\n- **NoSQL** through SQL gateway: varies\n\n**Solution — Override macro**:\n\n**SQLite**:\n\n```jinja\n{% macro sqlite__list_relations_without_caching(schema_relation) %}\n {% call statement('list_relations_without_caching', fetch_result=True) %}\n SELECT\n '{{ schema_relation.database }}' AS database,\n name,\n 'main' AS schema, -- SQLite has only 'main'\n CASE type\n WHEN 'table' THEN 'table'\n WHEN 'view' THEN 'view'\n END AS type\n FROM sqlite_master\n WHERE type IN ('table', 'view')\n AND name NOT LIKE 'sqlite_%' -- exclude internal tables\n {% endcall %}\n {{ return(load_result('list_relations_without_caching').table) }}\n{% endmacro %}\n```\n\nIf schemas not supported, use 'main' as default.\n\n**DuckDB** (which DOES have information_schema):\n\n```jinja\n{% macro duckdb__list_relations_without_caching(schema_relation) %}\n {% call statement('list_relations_without_caching', fetch_result=True) %}\n SELECT\n table_catalog AS database,\n table_name AS name,\n table_schema AS schema,\n CASE table_type\n WHEN 'BASE TABLE' THEN 'table'\n WHEN 'VIEW' THEN 'view'\n END AS type\n FROM information_schema.tables\n WHERE table_schema = '{{ schema_relation.schema }}'\n AND table_catalog = '{{ schema_relation.database }}'\n {% endcall %}\n {{ return(load_result('list_relations_without_caching').table) }}\n{% endmacro %}\n```\n\n**Spark** (no information_schema, use SHOW TABLES):\n\n```jinja\n{% macro spark__list_relations_without_caching(schema_relation) %}\n {% call statement('list_relations_without_caching', fetch_result=True) %}\n SHOW TABLES IN {{ schema_relation.without_identifier() }}\n {% endcall %}\n -- Result needs parsing — different structure than information_schema\n {%- set table = load_result('list_relations_without_caching').table -%}\n {%- set relations = [] -%}\n {%- for row in table -%}\n {%- set rel = api.Relation.create(\n database=schema_relation.database,\n schema=row.namespace,\n identifier=row.tableName,\n type='table' -- SHOW TABLES doesn't distinguish — need additional check\n ) -%}\n {%- do relations.append(rel) -%}\n {%- endfor -%}\n {{ return(relations) }}\n{% endmacro %}\n```\n\n**Snowflake** (has information_schema but уlsо INFORMATION_SCHEMA.TABLES might be expensive):\n\n```jinja\n{% macro snowflake__list_relations_without_caching(schema_relation) %}\n {% call statement('list_relations_without_caching', fetch_result=True) %}\n SHOW OBJECTS IN {{ schema_relation }}\n {% endcall %}\n -- Parse Snowflake's SHOW OBJECTS result\n ...\n{% endmacro %}\n```\n\nSnowflake's `SHOW OBJECTS` faster than querying INFORMATION_SCHEMA for large databases.\n\n**BigQuery** (per-dataset information_schema):\n\n```jinja\n{% macro bigquery__list_relations_without_caching(schema_relation) %}\n {%- set sql -%}\n SELECT\n table_catalog AS database,\n table_name AS name,\n table_schema AS schema,\n CASE table_type\n WHEN 'BASE TABLE' THEN 'table'\n WHEN 'VIEW' THEN 'view'\n END AS type\n FROM \\\'{{ schema_relation.database }}.{{ schema_relation.schema }}.INFORMATION_SCHEMA.TABLES\\\'\n {%- endset -%}\n -- BigQuery requires per-dataset INFORMATION_SCHEMA\n {%- set result = run_query(sql) -%}\n {{ return(result) }}\n{% endmacro %}\n```\n\n**General pattern**:\n\n1. **Read warehouse docs** для introspection mechanisms\n2. **Choose primary mechanism** (information_schema, SHOW, system catalog)\n3. **Override macro** для warehouse-specific syntax\n4. **Return same format**: 4 columns (database, name, schema, type)\n5. **Test**: ensure both tables AND views returned\n\n**Production considerations**:\n\n**Performance**: list_relations_without_caching called **at start of каждого dbt run** для каждой schema in profile. Должен быть fast.\n\n- [X] Slow if uses sub-queries или joins на metadata tables\n- [OK] Use specialized syntax если faster (SHOW, SHOW OBJECTS)\n\n**Caching**: result cached by dbt for the run. Не called repeatedly.\n\n**Error handling**: if returns invalid format — entire run fails. Test thoroughly.\n\n**Columns в result table** (always 4):\n\n1. database — relation's database\n2. name — relation's identifier\n3. schema — relation's schema\n4. type — 'table' or 'view'\n\nIf warehouse имеет other types (materialized_view, external_table) — map к 'table' or 'view'.\n\n**Test для verification**:\n\n```python\ndef test_list_relations():\n # Setup: create table + view в warehouse\n setup_sql = '''\n CREATE TABLE test_table (id INT);\n CREATE VIEW test_view AS SELECT 1;\n '''\n \n relations = adapter.list_relations_without_caching(schema_relation)\n \n # Should have both\n assert len(relations) == 2\n assert any(r.type == 'table' and r.identifier == 'test_table' for r in relations)\n assert any(r.type == 'view' and r.identifier == 'test_view' for r in relations)\n```\n\nЭто **critical macro** — used everywhere в dbt internals. Get it right.

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Какие 8 minimum macros для basic dbt run работало?

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

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

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

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