# О команде dbt lint

Доступно в v2

`dbt lint` — это быстрый SQL-линтер, встроенный в dbt v2 и доступный локально или на dbt platform. Для dbt lint требуется версия v2 или более поздняя. Если вы используете более раннюю версию, [обновите или установите dbt](https://docs.getdbt.tech/docs/dbt-versions/dbt-upgrade/upgrading-to-v2.md?version=2).

Он совместим с SQLFluff: читает вашу конфигурацию `.sqlfluff`, использует те же коды правил (например, `CP01`, `RF03`) и учитывает комментарии подавления `-- noqa`. Совместимость не означает идентичность: `dbt lint` и SQLFluff могут возвращать разные результаты для одного и того же файла с одной и той же конфигурацией. См. раздел [Соответствие правил SQLFluff](#rule-parity-with-sqlfluff).

Вы можете использовать существующую конфигурацию SQLFluff с минимальными изменениями. В дальнейшем dbt Labs планирует следовать актуальной спецификации правил SQLFluff.

примечание

`dbt lint` входит в состав dbt v2 и отличается от `dbt sqlfluff lint` в dbt platform CLI. Подробнее о SQLFluff в platform CLI см. в разделе [Настройка dbt platform CLI](https://docs.getdbt.tech/docs/platform/configure-dbt-cli.md). [Линтинг в Studio IDE](https://docs.getdbt.tech/docs/platform/studio-ide/lint-format.md) по-прежнему использует SQLFluff.

## Тесты производительности

На проектах размером от 1 тыс. до 10 тыс. моделей `dbt lint` работает в *40–250 раз* быстрее SQLFluff при использовании всех ядер и в *280–1500 раз* быстрее SQLFluff в однопоточном режиме.

dbt Labs провела эти тесты с SQLFluff 4.2.1 на проектах dbt с диалектом Snowflake, содержащих от 1 тыс. до 10 тыс. моделей, на MacBook Pro с 12-ядерным процессором Apple M4 Pro и 24 ГБ оперативной памяти.

## Использование

```shell
dbt lint [FILE] [flags]
```

`[FILE]` — необязательный аргумент. Если не указать `[FILE]`, `dbt lint` проверит все SQL-файлы в вашем проекте.

## Флаги

| Флаг                                      | Описание                                                                                                                                                                                                                                        |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--fix`                                   | Автоматически исправляет нарушения правил, для которых доступно автоматическое исправление. Правила, нарушения которых нельзя исправить автоматически, перечислены в разделе [Правила без автоматического исправления](#rules-without-autofix). |
| `--config <path>`                         | Путь к файлу конфигурации `.sqlfluff`. Переопределяет автоматическое обнаружение.                                                                                                                                                               |
| `--rules`                                 | Список кодов правил для включения, разделённых запятыми. Переопределяет конфигурацию.                                                                                                                                                           |
| `--exclude-rules`                         | Список кодов правил для отключения, разделённых запятыми. Переопределяет конфигурацию.                                                                                                                                                          |
| `--changed`                               | Проверяет только файлы, изменённые в текущем рабочем дереве git.                                                                                                                                                                                |
| `--format human\|json\|github-annotation` | Формат вывода. По умолчанию — `human`. Используйте `json` для машиночитаемого вывода или `github-annotation` для интеграции с GitHub Actions.                                                                                                   |
| `--jinja-render-mode <mode>`              | Способ рендеринга Jinja в `dbt lint` перед проверкой. Допустимые значения: `symbolic` (по умолчанию), `rendered` или `turbo`. Переопределяет конфигурацию `.sqlfluff`. См. раздел [Режимы рендеринга Jinja](#jinja-render-modes).               |

## Конфигурация

`dbt lint` автоматически обнаруживает ближайший файл `.sqlfluff` в дереве каталогов вашего проекта. Флаги CLI `--rules` и `--exclude-rules` имеют приоритет над значениями в файле конфигурации. О создании файла `.sqlfluff` см. в разделе [Файлы конфигурации SQLFluff](https://docs.sqlfluff.com/en/stable/configuration/setting_configuration.html).

### Требование к шаблонизатору

`dbt lint` и `dbt format` поддерживают только шаблонизатор SQLFluff `dbt`, но не `jinja`. Явно укажите шаблонизатор в файле `.sqlfluff`:

```ini
[sqlfluff]
templater = dbt
```

Если в файле `.sqlfluff` не задано `templater = dbt`, при каждом запуске в журнал записывается следующее предупреждение, но выполнение всё равно продолжается:

```text
[warning] [NotSupported (dbt9000)]: SQLFluff templater 'jinja' is not supported. dbt lint/format only supports SQLFluff templater 'dbt'. Continuing anyway.
```

Установка `templater = dbt` устраняет это предупреждение.

## Правила, специфичные для dbt

`dbt lint` также включает пять правил, специфичных для dbt, с префиксом кода `DBT`. Эти правила выявляют особенности использования dbt, которые не обнаруживает обычный SQL-линтер, например жёстко заданные имена отношений вместо `ref()`.

По умолчанию эти правила отключены. Чтобы включить их, добавьте строку `rules` в файл `.sqlfluff`:

```ini
[sqlfluff]
templater = dbt
dialect = your_dialect
rules = DBT02,DBT03,DBT04,DBT05
```

| Код     | Имя с точечной нотацией              | Правило                                                                                                                       |
| ------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `DBT01` | `dbt.import_ctes`                    | Каждый вызов `ref()/source()` должен импортироваться через CTE верхнего уровня, а не использоваться непосредственно в запросе |
| `DBT02` | `dbt.join_condition_or`              | Условие `ON` в `JOIN` не должно содержать `OR`                                                                                |
| `DBT03` | `dbt.function_wrapped_filter_column` | В сравнении ссылка на столбец не должна быть обёрнута в вызов функции                                                         |
| `DBT04` | `dbt.leading_wildcard_like`          | Шаблон `LIKE/ILIKE` не должен начинаться с подстановочного символа                                                            |
| `DBT05` | `dbt.hard_coded_reference`           | Ссылка `ref()/source()` не должна быть жёстко задана строковым литералом                                                      |

## Режимы рендеринга Jinja

Прежде чем `dbt lint` сможет проверить модель, ему нужно преобразовать SQL с шаблонами Jinja в обычный SQL. Большинство шаблонов Jinja корректно обрабатываются во время линтинга, но некоторые макросы запрашивают информацию у вашей платформы данных, например список столбцов таблицы. При этом `dbt lint` никогда не подключается к платформе, поэтому такие вызовы не получают реального ответа. Настройка `jinja_render_mode` определяет, как `dbt lint` обрабатывает эти вызовы, и от неё зависит, какие нарушения вы увидите.

Для большинства проектов рекомендуется оставить значение по умолчанию — `symbolic`. Доступны три режима:

| Режим                                          | Краткое описание                                                                                                          |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| [`symbolic` (по умолчанию)](#symbolic-default) | Выполняет обычный рендеринг Jinja и подставляет заполнитель вместо результатов, которые невозможно получить от платформы. |
| [`rendered`](#rendered)                        | Выполняет рендеринг Jinja с пустыми значениями-заглушками, никак не отмечая, что они подставные.                          |
| [`turbo`](#turbo)                              | Полностью пропускает выполнение и проверяет буквальный текст шаблона.                                                     |

### Symbolic (по умолчанию)

Выполняет Jinja, но отслеживает, какие значения получены из интроспективных вызовов адаптера, таких как `adapter.execute`, `adapter.get_relation` и `adapter.get_columns_in_relation`. Поскольку во время линтинга эти вызовы не могут обратиться к хранилищу данных, `dbt lint` заменяет их результат заполнителем, а не вводящим в заблуждение пустым значением. Всё остальное обрабатывается как обычно.

Оставьте режим по умолчанию. Он даёт наименьшее количество ложных срабатываний в проектах, использующих интроспективные макросы.

Например, модель, которая перебирает результат интроспективного вызова:

```jinja
{% set cols = adapter.get_columns_in_relation(ref('orders')) %}
select {{ cols | map(attribute='name') | join(', ') }}
from {{ ref('orders') }}
```

для целей линтинга преобразуется примерно в следующее:

```sql
select your_columns
from orders
```

`dbt lint` не сообщает о нарушениях в самом значении-заполнителе, но проверяет всё остальное в запросе как обычно.

### Rendered

Выполняет Jinja со значениями-заглушками, используемыми на этапе разбора. Интроспективные вызовы адаптера возвращают пустые результаты без каких-либо указаний на то, что эти значения не настоящие, поэтому та же модель преобразуется в:

```sql
select
from orders
```

Пустой список `select` может привести к появлению SQL, который ваш проект никогда не выполнил бы, и `dbt lint` проверяет именно этот нереалистичный результат. Сравните `rendered` с `symbolic`, если разбираетесь с неожиданным нарушением.

### Turbo

Никогда не выполняет Jinja. Анализирует синтаксис шаблона, сохраняет написанный вами SQL как есть и заменяет каждое выражение `{{ ... }}` заполнителем, в том числе выражения, не связанные с интроспекцией:

```sql
select your_expression
from your_expression
```

Используйте этот режим, если рендеринг модели выполняется слишком медленно или завершается ошибкой. Это самый быстрый режим, но он не видит ничего из того, что генерируют макросы.

### Настройка режима рендеринга

Задайте режим для отдельного запуска с помощью `--jinja-render-mode`. Флаг работает как с `dbt lint`, так и с `dbt format`:

```shell
dbt lint --jinja-render-mode rendered
dbt format --jinja-render-mode turbo
```

Задайте его для всего проекта в секции `[dbt]` файла `.sqlfluff`:

```ini
[dbt]
jinja_render_mode = rendered
```

Флаг CLI имеет приоритет над файлом конфигурации.

### Варианты рендеринга

В режимах `symbolic` и `turbo` одна модель может давать несколько возможных вариантов SQL. Когда `dbt lint` доходит до блока `{% if %}`, условие которого не удаётся вычислить, он проверяет несколько ветвей, а не пытается угадать, какую из них вы имели в виду. Каждый такой результат называется *вариантом рендеринга*, и `dbt lint` сообщает о нарушениях, найденных во всех этих вариантах.

`render_variant_limit` ограничивает число вариантов, которые `dbt lint` создаёт для каждой модели; значение по умолчанию — `5`. Задайте его в секции `[sqlfluff]` файла `.sqlfluff`:

```ini
[sqlfluff]
render_variant_limit = 10
```

Увеличение лимита расширяет охват проверки, но увеличивает время линтинга, поскольку каждый дополнительный вариант требует ещё одного рендеринга шаблона. Уменьшение значения до `1` ограничивает `dbt lint` одним вариантом для каждой модели.

## Игнорирование файлов и каталогов

Используйте файл `.sqlfluffignore` в корне проекта, чтобы исключить пути, которые вы пока не готовы проверять, например `dbt_packages/` или `models/legacy/`.

`.sqlfluffignore` использует синтаксис в стиле `.gitignore`. Полный справочник по шаблонам см. в [документации SQLFluff по `.sqlfluffignore`](https://docs.sqlfluff.com/en/stable/configuration.html#id2).

```text
# .sqlfluffignore
dbt_packages/
models/legacy/
snapshots/
```

Когда будете готовы проверять эти пути, удалите соответствующие записи из `.sqlfluffignore`.

### Уменьшение количества лишних сообщений на вкладке Problems в Studio IDE

Studio IDE автоматически проверяет SQL и показывает нарушения на вкладке **Problems**. Если вы видите много предупреждений о стиле и пока не готовы их исправлять, добавьте каталоги моделей в `.sqlfluffignore`, чтобы сразу убрать эти нарушения с вкладки **Problems**. По мере устранения нарушений постепенно удаляйте записи об игнорировании.

## Подавление нарушений

`dbt lint` поддерживает полный синтаксис подавления SQLFluff:

| Подавление                 | Область действия                          |
| -------------------------- | ----------------------------------------- |
| `-- noqa`                  | Подавляет все нарушения в строке          |
| `-- noqa: CP01, RF03`      | Подавляет указанные правила в этой строке |
| `-- noqa-file`             | Подавляет все нарушения в файле           |
| `-- sqlfluff:disable CP01` | Отключает правило в файле                 |

## Поддерживаемые диалекты

В настоящее время `dbt lint` поддерживает следующие диалекты:

* Snowflake
* BigQuery
* DuckDB
* Redshift
* Databricks
* SparkSQL (сейчас является псевдонимом Databricks)

Поддержка дополнительных диалектов появится в ближайшее время.

## dbt format

Для `dbt format` (также доступной как `dbt fmt`) требуется версия v2 или более поздняя. Команда автоматически форматирует SQL-файлы в соответствии с правилами оформления (`LT*`) из файла `.sqlfluff`. В отличие от `dbt lint`, она не выводит диагностические сообщения. При запуске команда применяет исправления без вывода сообщений, непосредственно в файлах.

```shell
dbt format [FILE] [flags]
dbt fmt [FILE] [flags]
```

`[FILE]` — необязательный аргумент. Если его не указать, `dbt format` отформатирует все SQL-файлы в проекте. Вы также можете передать отдельный файл или каталог, чтобы ограничить область выполнения.

Если в `.sqlfluff` не задано `templater = dbt`, `dbt format` записывает предупреждение в журнал и продолжает работу. См. раздел [Требование к шаблонизатору](#templater-requirement).

### Флаги форматирования

| Флаг                                 | Описание                                                                                                                                                                                                |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--check`                            | Проверяет форматирование файлов, не записывая изменения. Завершается с ненулевым кодом, если хотя бы один файл не отформатирован. Никогда не изменяет файлы.                                            |
| `--layout KEY=VALUE`, `-l KEY=VALUE` | Переопределяет правила оформления в стиле SQLFluff для текущего запуска. Повторите флаг, чтобы задать несколько параметров. См. раздел [Параметры оформления](#layout-options).                         |
| `--jinja-render-mode MODE`           | Задаёт способ рендеринга Jinja в `dbt format` перед форматированием. Допустимые значения: `symbolic` (по умолчанию), `rendered` или `turbo`. См. раздел [Режимы рендеринга Jinja](#jinja-render-modes). |

### Параметры оформления

Флаг `--layout` принимает пары `KEY=VALUE` и переопределяет соответствующие правила оформления из файла `.sqlfluff` для одного запуска. Он переопределяет только переданные ключи; все остальные правила оформления `dbt format` по-прежнему читает из файла `.sqlfluff`. Поддерживаются следующие ключи:

| Ключ                       | Описание                           |
| -------------------------- | ---------------------------------- |
| `indent=NUMBER`            | Задаёт число пробелов для отступа. |
| `commas=leading\|trailing` | Задаёт расположение запятых.       |
| `line-length=NUMBER`       | Задаёт максимальную длину строки.  |

Например, чтобы отформатировать код с запятыми в начале строк и отступом в два пробела:

```shell
dbt format -l commas=leading -l indent=2
```

### Проверка форматирования в CI

Используйте `--check` для проверки без внесения изменений, чтобы разрешать принятие pull request только при соблюдении форматирования. Если все файлы отформатированы, команда завершается с кодом `0`. Если какой-либо файл не отформатирован, команда завершается с кодом `1`, оставляет файл без изменений и возвращает ошибку следующего вида:

```text
[error] [Generic (dbt1000)]: Formatting check failed for models/staging/stg_orders.sql. Run `dbt fmt` to format it.
```

```shell
dbt format --check                                # check all SQL files
dbt format --check models/staging                 # check a directory
dbt format --check models/staging/stg_orders.sql  # check a single file
```

Чтобы устранить ошибки проверки, запустите `dbt format` без `--check`.

## Соответствие правил SQLFluff

`dbt lint` стремится обеспечить высокую степень соответствия SQLFluff, но не гарантирует полного совпадения по каждому правилу, и небольшие различия будут существовать всегда. Одна из известных областей различий — правила оформления и отступов, такие как `LT02`.

Поскольку [линтинг в Studio IDE](https://docs.getdbt.tech/docs/platform/studio-ide/lint-format.md) по-прежнему использует SQLFluff, действие **Lint file** в Studio IDE и команда `dbt lint` могут сообщать о разных нарушениях в одном и том же коде проекта. Аналогично, задания CI, использующие версию dbt v2, вызывают `dbt lint` вместо SQLFluff, поэтому результаты [заданий CI](https://docs.getdbt.tech/docs/deploy/continuous-integration.md#sql-linting) могут отличаться от результатов SQLFluff.

Если вам нужно поведение SQLFluff, вы можете выполнять линтинг в Studio IDE, где по-прежнему используется SQLFluff, либо запускать SQLFluff локально с шаблонизатором для отдельного движка dbt v1. Дополнительные сведения см. в разделе [Ограничения dbt v2](https://docs.getdbt.tech/docs/dbt/supported-features.md#limitations).

## Преобразование предупреждений линтера в ошибки

По умолчанию нарушения [правил без автоматического исправления](#rules-without-autofix) отображаются как предупреждения и не приводят к завершению `dbt lint` с ошибкой. Нарушения, которые можно исправить с помощью `--fix`, отображаются как ошибки и приводят к завершению команды с ошибкой.

Если вы хотите, чтобы нарушение правила отображалось как ошибка, а не предупреждение, добавьте имя правила в `warn_error_options` в файле `dbt_project.yml`. Имя правила можно найти в выводе линтера. Например, правило `ST05` отображается как `PreferCTE`:

```yaml
flags:
  warn_error_options:
    error:
      - PreferCTE
```

Если вместо этого задать `error: all`, dbt преобразует в ошибки все предупреждения, в том числе предупреждения команд, отличных от `lint`:

```yaml
flags:
  warn_error_options:
    error: all
```

Чтобы преобразовать в ошибку нарушение одного правила линтера, укажите имя этого правила, как в первом примере. См. раздел [Предупреждения](https://docs.getdbt.tech/reference/global-configs/warnings.md).

В задании CI флажок **Linting** запускает `dbt lint` с использованием этого файла проекта. Выберите **Stop running on error**, чтобы задание завершалось с ошибкой, когда линтер сообщает об ошибке. О включении линтинга см. в разделах [Настройка заданий CI](https://docs.getdbt.tech/docs/deploy/ci-jobs.md#set-up-ci-jobs) и [Линтинг SQL](https://docs.getdbt.tech/docs/deploy/continuous-integration.md#sql-linting).

## Ограничения

Учитывайте следующие ограничения:

### Правила без автоматического исправления

Следующие правила сообщают о нарушениях, но не поддерживают автоматическое исправление с помощью `--fix`. Для их исправления требуется изменить порядок SQL-фрагментов или более существенно переработать их размещение, что механизм сопоставления с исходным кодом (на основе `macro_spans`) не может безопасно выполнить внутри SQL с шаблонами Jinja:

* **Псевдонимы:** `AL03`, `AL04`, `AL06`, `AL08`
* **Ссылки:** `RF01`, `RF02`, `RF04`, `RF05`
* **Структура:** `ST03`, `ST04`, `ST05`, `ST06`, `ST07`, `ST09`, `ST10`, `ST11`
* **Неоднозначность / соглашения:** `AM01`, `AM06`, `CV08`, `CV09`, `CV12`

Чтобы выполнение завершалось с ошибкой при нарушении одного из этих правил, см. раздел [Преобразование предупреждений линтера в ошибки](#promoting-lint-warnings-to-errors).

### Один проход исправлений

`--fix` выполняет один проход и не повторяет его до устранения всех нарушений в файле. Исправление, применённое одним правилом, может выявить нарушение другого правила при следующем запуске. Например, `AL09` удаляет псевдоним, совпадающий с исходным именем, после чего `RF02` может отметить ссылку, которая теперь не содержит квалификатора. Повторно запускайте `dbt lint --fix`, пока в выводе не останется нарушений.

## Часто задаваемые вопросы

 Почему dbt lint не проверяет все возможные результаты моего шаблона Jinja?

`dbt lint` проверяет ограниченный набор [вариантов рендеринга](#render-variants) для каждой модели, а не все варианты SQL, которые ваши макросы могут сгенерировать при любых сочетаниях входных данных.

Это осознанный выбор. Число возможных результатов растёт комбинаторно с увеличением количества условий в шаблоне, которые не удаётся вычислить, поэтому проверка всех результатов требует больших затрат и выявляет нарушения в SQL, который ваш проект может никогда не выполнить. Вместо этого `dbt lint` поочерёдно изменяет одно невычисленное условие, не превышая `render_variant_limit`.

Учитывайте одно следствие: когда `dbt lint` проверяет ветвь, которую ваш проект никогда не выполняет, он может выявить нарушение в этом неиспользуемом пути, чаще всего внутри макроса стороннего пакета. Если у вас есть замечания к этому подходу, создайте issue в [репозитории dbt-core на GitHub](https://github.com/dbt-labs/dbt/issues) с меткой `Linter`.

 Почему dbt lint не сообщает о нарушениях в некоторых макросах?

`dbt lint` проверяет SQL, создаваемый всеми макросами, но не может вычислить результат макросов, которые зависят от запросов к вашему хранилищу данных. Интроспективные вызовы адаптера, такие как `adapter.execute` и `adapter.get_columns_in_relation`, не имеют реального результата во время линтинга.

То, как `dbt lint` обрабатывает эту ситуацию, зависит от [режима рендеринга](#jinja-render-modes). В режиме по умолчанию `symbolic` команда `dbt lint` подставляет заполнитель везде, где должен появиться результат интроспективного вызова, и не сообщает о нарушениях в этом заполнителе, поскольку сообщения о нарушениях в искусственно созданном результате дают больше шума, чем полезной информации. Остальная часть макроса проверяется как обычно.

Это поведение похоже на действие настройки `ignore_templated_areas` в SQLFluff.

## Обратная связь

Если вы столкнулись с неожиданным поведением или у вас есть предложения, создайте issue в [репозитории dbt-labs/dbt на GitHub](https://github.com/dbt-labs/dbt/issues) и добавьте метку `Linter`.
