Перейти к основному содержимому

Пользовательские функции (User-defined functions)

Пользовательские функции (UDF) позволяют определять и регистрировать собственные функции непосредственно в вашем хранилище данных. Подобно макросам, UDF способствуют повторному использованию кода, но в отличие от макросов они являются объектами в хранилище. Это означает, что одну и ту же логику можно использовать не только в dbt, но и в других инструментах — например, в BI-системах, ноутбуках для data science и т.д.

UDF особенно полезны для переиспользования логики в нескольких инструментах, стандартизации сложных бизнес‑расчётов, повышения производительности вычислительно тяжёлых операций (поскольку они компилируются и оптимизируются движком запросов вашего хранилища), а также для контроля версий кастомной логики внутри вашего dbt‑проекта.

dbt создаёт, обновляет и переименовывает UDF в процессе выполнения DAG. UDF создаётся в хранилище до той модели, которая на него ссылается. Подробнее о том, как dbt строит UDF в проекте, см. listing and building UDFs.

Дополнительную информацию о конфигурациях и свойствах UDF см. в разделах Function properties и Function configurations.

Предварительные требования​

  • Убедитесь, что вы используете v2 Stable или v1 Latest канала выпуска платформы dbt либо dbt v1.11+.

  • Используйте один из следующих адаптеров:

    • BigQuery
    • Snowflake
    • Redshift
    • Postgres
    • Databricks
    • Starburst/Trino (dbt-trino v1.11+)
Поддержка UDF

UDF на JavaScript поддерживаются в dbt v1.12 и выше на Snowflake и BigQuery.

Другие языки (например, Java, Scala) в настоящее время не поддерживаются для UDF.

Полный список поддерживаемых возможностей UDF см. в разделе Limitations ниже.

Определение UDF в dbt​

В dbt можно определять UDF на SQL, Python и JavaScript (доступны в dbt v1.12+). UDF на Python поддерживаются в Snowflake, BigQuery и Databricks (требуется Unity Catalog). UDF на JavaScript поддерживаются в Snowflake и BigQuery.

Выполните следующие шаги, чтобы определить UDF в dbt:

  1. Создайте файл SQL, Python или JavaScript в каталоге functions. Например, эта UDF проверяет, представляет ли строка положительное целое число:

    Определение SQL UDF в SQL‑файле.

    functions/is_positive_int.sql
    # syntax for BigQuery, Snowflake, and Databricks
    REGEXP_INSTR(a_string, '^[0-9]+$')

    # syntax for Redshift and Postgres
    SELECT REGEXP_INSTR(a_string, '^[0-9]+$')

    # syntax for Starburst/Trino
    CASE WHEN regexp_like(a_string, '^[0-9]+$') THEN 1 ELSE 0 END

Примечание: Вы можете указать конфигурации в блоке config в SQL-файле или в соответствующем YAML-файле свойств на шаге 2.

  1. Укажите имя функции и определите конфигурацию, свойства, тип возвращаемого значения и необязательные аргументы в соответствующем YAML-файле свойств.

    functions/is_positive_int.yml
    functions:
    - name: is_positive_int # required
    description: My UDF that returns 1 if a string represents a naked positive integer (like "10", "+8" is not allowed). # optional
    config:
    schema: udf_schema
    database: udf_db
    volatility: deterministic
    arguments: # optional
    - name: a_string # required if arguments is specified
    data_type: string # required if arguments is specified
    description: The string that I want to check if it's representing a positive integer (like "10")
    default_value: "'1'" # optional, available in Snowflake and Postgres
    returns: # required
    data_type: integer # required

    Для Starburst/Trino укажите в data_type аргумента тип данных, который поддерживает движок, например varchar вместо string. Не задавайте default_value. В Starburst Galaxy задайте database: galaxy и schema: functions.

Зависимость volatility от хранилища данных

В dbt параметр volatility принимается для UDF на SQL, Python и JavaScript, но его обработка зависит от хранилища данных. Для UDF на SQL и Python в BigQuery параметр volatility игнорируется, а dbt выводит предупреждение. Для UDF на JavaScript в BigQuery значения deterministic и non-deterministic применяются при создании UDF; значение stable не поддерживается. В Snowflake все поддерживаемые значения volatility применяются при создании UDF. Дополнительную информацию см. в разделе volatility.

  1. Выполните одну из следующих команд dbt build, чтобы собрать UDF и создать их в хранилище:

    Сборка всех UDF:

    dbt build --select "resource_type:function"

    Или сборка конкретной UDF:

    dbt build --select is_positive_int

При выполнении dbt build файл свойств (functions/is_positive_int.yml) и соответствующий файл SQL, Python или JavaScript совместно используются для формирования инструкции CREATE FUNCTION.

Сформированная инструкция CREATE FUNCTION зависит от используемого адаптера. Например:

CREATE OR REPLACE FUNCTION udf_db.udf_schema.is_positive_int(a_string STRING DEFAULT '1')
RETURNS INTEGER
LANGUAGE SQL
IMMUTABLE
AS $$
REGEXP_INSTR(a_string, '^[0-9]+$')
$$;
  1. Используйте UDF в модели с помощью макроса {{ function(...) }}. Например:

    models/my_model.sql
    select
    maybe_positive_int_column,
    {{ function('is_positive_int') }}(maybe_positive_int_column) as is_positive_int
    from {{ ref('a_model_i_like') }}

При использовании --defer function() ссылается на существующую UDF в среде, на которую перенаправляются ссылки (например, в production), если функция не выбрана или ещё не создана в вашей целевой среде. Для этого требуется манифест состояния, указанный с помощью --state или эквивалентной переменной окружения (например, DBT_ENGINE_STATE), который dbt использует, чтобы определить, куда перенаправлять ссылки. Это позволяет успешно запускать модели, зависящие от UDF, в рабочих процессах непрерывной интеграции и разработки. Подробнее см. в разделе Настройка выбора на основе состояния.

  1. Выполните dbt compile, чтобы увидеть, как формируется ссылка на UDF. В следующем примере {{ function('is_positive_int') }} заменяется именем UDF udf_db.udf_schema.is_positive_int.

    models/my_model.sql
    select
    maybe_positive_int_column,
    udf_db.udf_schema.is_positive_int(maybe_positive_int_column) as is_positive
    from analytics.dbt_schema.a_model_i_like

    В DAG создаётся узел UDF на основе SQL/Python‑файла и YAML‑описания, и появляется зависимость is_positive_int → my_model.

    Узел UDF в графе зависимостейУзел UDF в графе зависимостей

После определения UDF ваши изменения применяются к UDF в хранилище при следующем запуске dbt build, если вы изменили что-либо из следующего:

  • Файл SQL, Python или JavaScript, содержащий тело функции (is_positive_int.sql, is_positive_int.py или is_positive_int.js в этих примерах)
  • Её конфигурации
  • Её свойства, определённые в файле .yml (например, arguments или returns)

dbt обнаруживает все эти изменения при использовании state:modified.

Определение перегруженных UDF​

Используйте свойство overloads (доступно в dbt v1.12+), чтобы определить несколько сигнатур аргументов для одной функции. Это позволяет вызывать функцию с одним и тем же именем, передавая входные данные разных типов, без создания отдельных UDF для каждого варианта. overloads поддерживается для SQL UDF в Snowflake и Postgres, а также для UDF на Python и JavaScript в Snowflake.

Чтобы определить перегруженные UDF:

  1. Добавьте список overloads в определение функции в YAML-файле свойств. Каждая запись использует defined_in для ссылки на отдельный файл и может содержать необязательные свойства arguments и returns:

    functions/is_positive_int.yml
    functions:
    - name: is_positive_int
    arguments:
    - name: a_string
    data_type: string
    returns:
    data_type: integer
    overloads:
    - defined_in: is_positive_int_numeric # references functions/is_positive_int_numeric.sql
    arguments:
    - name: a_num
    data_type: numeric
    returns: # optional, inherits from root function if omitted
    data_type: integer
  2. Создайте отдельный файл для тела каждой перегрузки.

    Например, тело основной функции, принимающей аргумент типа string:

    functions/is_positive_int.sql
    # Snowflake syntax
    REGEXP_INSTR(a_string, '^[0-9]+$')

    И тело перегрузки, принимающей аргумент типа numeric:

    functions/is_positive_int_numeric.sql
    # Snowflake syntax
    CASE WHEN a_num > 0 THEN 1 ELSE 0 END

Все перегрузки объединяются в один узел DAG (основную функцию), поэтому они создаются и выбираются вместе. При повторной попытке dbt пропускает успешно созданные перегрузки и повторно запускает только те, при создании которых произошла ошибка. При создании функции dbt формирует отдельный оператор CREATE FUNCTION для каждой перегрузки, используя одно и то же имя функции, но разные типы аргументов.

Дополнительную информацию см. в разделе overloads.

Использование UDF в unit‑тестах​

Вы можете использовать unit tests для проверки моделей, которые ссылаются на UDF. Перед запуском unit‑тестов убедитесь, что функция существует в хранилище. Чтобы гарантировать это для unit‑теста, выполните:

dbt build --select "+my_model_to_test" --empty

Следуя примеру из раздела Определение UDF в dbt, ниже приведён пример unit‑теста, который проверяет модель, вызывающую UDF:

tests/test_is_positive_int.yml
unit_tests:
- name: test_is_positive_int
description: "Проверьте, что моя логика is_positive_int учитывает крайние случаи"
model: my_model
given:
- input: ref('a_model_i_like')
rows:
- { maybe_positive_int_column: 10 }
- { maybe_positive_int_column: -4 }
- { maybe_positive_int_column: +8 }
- { maybe_positive_int_column: 1.0 }
expect:
rows:
- { maybe_positive_int_column: 10, is_positive: true }
- { maybe_positive_int_column: -4, is_positive: false }
- { maybe_positive_int_column: +8, is_positive: true }
- { maybe_positive_int_column: 1.0, is_positive: true }

Просмотр и сборка UDF​

Используйте команду list, чтобы вывести список UDF в проекте: dbt list --select "resource_type:function" или dbt list --resource-type function.

Используйте команду build, чтобы выбирать UDF при сборке проекта: dbt build --select "resource_type:function".

Подробнее о выборе UDF см. в примерах в разделе Node selector methods.

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

  • UDF на других языках (например, Java или Scala) пока не поддерживаются.
  • UDF на JavaScript поддерживаются только в Snowflake и BigQuery. Использование UDF на JavaScript с неподдерживаемым адаптером вызывает ошибку разбора.
  • UDF на Python поддерживаются только в Snowflake, BigQuery и Databricks (при использовании dbt v1 или dbt v2). Другие хранилища данных пока не поддерживаются для UDF на Python.
  • В настоящее время поддерживаются только скалярные и агрегатные функции. Подробнее см. в разделе Поддерживаемые типы функций.
  • Свойство overloads поддерживается для UDF на SQL в Snowflake и Postgres, а также для UDF на Python и JavaScript в Snowflake.
Когда следует использовать UDF вместо макроса?

Нашли ошибку?

0
Loading