- исполняемые UDF запускают внешнюю программу или скрипт (Python, Bash и т. д.) и потоково передают им блоки данных через STDIN / STDOUT. Используйте их для интеграции существующего кода или инструментов без перекомпиляции ClickHouse. По сравнению с внутрипроцессными вариантами у них выше накладные расходы на каждый вызов, поэтому они лучше подходят для более сложной логики или случаев, когда нужна другая среда выполнения.
- пользовательские функции SQL определяются с помощью
CREATE FUNCTIONисключительно на SQL. Они подставляются/разворачиваются в план запроса (без отдельного процесса), что делает их лёгкими и идеально подходящими для повторного использования логики выражений или упрощения сложных вычисляемых столбцов. - Экспериментальные пользовательские функции WebAssembly выполняют код, скомпилированный в WebAssembly, внутри песочницы в процессе сервера. Они обеспечивают меньшие накладные расходы на каждый вызов, чем внешние исполняемые файлы, и лучшую изоляцию, чем нативные расширения, что делает их подходящими для пользовательских алгоритмов, написанных на языках, которые можно компилировать в WASM (например, C/C++/Rust).
- Экспериментальные исполняемые UDF на базе драйверов позволяют предоставляемому оператором “драйверу” преобразовывать фрагмент кода, указанный в
CREATE FUNCTION ... ENGINE = DriverName(...) AS '...', в исполняемый UDF при создании функции (например, путём компиляции). Они основаны на исполняемых UDF и требуют серверной конфигурации драйвера.
Исполняемые пользовательские функции
В ClickHouse Cloud исполняемые UDF общедоступны и создаются через консоль Cloud, Cloud API или Terraform. См. Пользовательские функции в Cloud, чтобы ознакомиться со специальным процессом для Cloud, включая среду выполнения Native для скомпилированных бинарных файлов.
user_defined_executable_functions_config.
Конфигурация функции включает следующие настройки:
Команда должна читать аргументы из
STDIN и выводить результат в STDOUT. Команда должна обрабатывать аргументы итеративно. То есть после обработки одного фрагмента аргументов она должна ждать следующий.
Исполняемые пользовательские функции
Примеры
UDF из встроенного скрипта
Создайтеtest_function_sum, вручную задав execute_direct значение 0, с помощью конфигурации XML или YAML.
- XML
- YAML
Файл
test_function.xml (/etc/clickhouse-server/test_function.xml при настройках пути по умолчанию)./etc/clickhouse-server/test_function.xml
Query
Result
UDF из скрипта Python
В этом примере мы создаём UDF, которая считывает значение изSTDIN и возвращает его в виде строки.
Создайте test_function, используя конфигурацию XML или YAML.
- XML
- YAML
Файл
test_function.xml (/etc/clickhouse-server/test_function.xml при использовании путей по умолчанию)./etc/clickhouse-server/test_function.xml
Создайте файл скрипта
test_function.py в папке user_scripts (/var/lib/clickhouse/user_scripts/test_function.py при использовании путей по умолчанию).
Query
Result
Прочитайте два значения из STDIN и верните их сумму в виде объекта JSON
Создайте test_function_sum_json с именованными аргументами и форматом JSONEachRow, используя конфигурацию в XML или YAML.
- XML
- YAML
Файл
test_function.xml (/etc/clickhouse-server/test_function.xml при настройках путей по умолчанию)./etc/clickhouse-server/test_function.xml
Создайте файл скрипта
test_function_sum_json.py в папке user_scripts (/var/lib/clickhouse/user_scripts/test_function_sum_json.py при настройках путей по умолчанию).
Query
Result
Использование параметров в настройке command
Пользовательские функции типа executable могут принимать константные параметры, заданные в настройке command (это работает только для пользовательских функций типа executable).
Также требуется параметр execute_direct, чтобы исключить уязвимость, связанную с подстановкой аргументов командной оболочкой.
- XML
- YAML
Файл
test_function_parameter_python.xml (/etc/clickhouse-server/test_function_parameter_python.xml, если используются пути по умолчанию)./etc/clickhouse-server/test_function_parameter_python.xml
Создайте файл скрипта
test_function_parameter_python.py в каталоге user_scripts (/var/lib/clickhouse/user_scripts/test_function_parameter_python.py, если используются пути по умолчанию).
Query
Result
UDF из shell-скрипта
В этом примере мы создаём shell-скрипт, который умножает каждое значение на 2.- XML
- YAML
Файл
test_function_shell.xml (/etc/clickhouse-server/test_function_shell.xml при пути по умолчанию)./etc/clickhouse-server/test_function_shell.xml
Создайте файл скрипта
test_shell.sh в папке user_scripts (/var/lib/clickhouse/user_scripts/test_shell.sh при пути по умолчанию).
/var/lib/clickhouse/user_scripts/test_shell.sh
Query
Result
Обработка ошибок
Некоторые функции могут сгенерировать исключение, если данные некорректны. В этом случае запрос отменяется, а клиенту возвращается текст ошибки. При распределённой обработке, если исключение возникает на одном из серверов, остальные серверы также пытаются прервать запрос.Вычисление выражений аргументов
Почти во всех языках программирования для некоторых операторов один из аргументов может не вычисляться. Обычно это операторы&&, || и ?:.
В ClickHouse аргументы функций (операторов) вычисляются всегда.
Это связано с тем, что вычисляются сразу целые части столбцов, а не каждая строка отдельно.
Выполнение функций при распределённой обработке запросов
При распределённой обработке запросов на удалённых серверах выполняется как можно больше этапов обработки запроса, а остальные этапы (слияние промежуточных результатов и всё последующее) — на сервере-инициаторе запроса. Это означает, что функции могут выполняться на разных серверах. Например, в запросеSELECT f(sum(g(x))) FROM distributed_table GROUP BY h(y),
- если
distributed_tableсодержит как минимум два сегмента, функции ‘g’ и ‘h’ выполняются на удалённых серверах, а функция ‘f’ — на сервере-инициаторе запроса. - если
distributed_tableсодержит только один сегмент, все функции ‘f’, ‘g’ и ‘h’ выполняются на сервере этого сегмента.
hostName, которая возвращает имя сервера, на котором она выполняется, чтобы можно было использовать GROUP BY по серверам в запросе SELECT.
Если функция в запросе выполняется на сервере-инициаторе запроса, но её нужно выполнить на удалённых серверах, можно обернуть её в агрегатную функцию ‘any’ или добавить в ключ GROUP BY.
Пользовательские функции SQL
Пользовательские функции на основе лямбда-выражений можно создавать с помощью оператора CREATE FUNCTION. Для удаления этих функций используйте оператор DROP FUNCTION.Пользовательские функции WebAssembly
Пользовательские функции WebAssembly (WASM UDF) позволяют выполнять пользовательский код, скомпилированный в WebAssembly, внутри процесса сервера ClickHouse.Быстрый старт
Включите экспериментальную поддержку WebAssembly в конфигурации ClickHouse:Дополнительная информация
Подробнее см. в документации Пользовательские функции WebAssembly.Исполняемые UDF на базе драйверов
Это экспериментальная возможность, которая в будущих релизах может измениться с нарушением обратной совместимости. Включите её с помощью настройки сервера
allow_experimental_executable_udf_drivers.ENGINE = DriverName(...), ClickHouse запускает create_command драйвера, передавая ему сигнатуру функции и тело кода; драйвер компилирует тело или иным образом его обрабатывает и выводит конфигурацию исполняемого UDF, которую ClickHouse затем сохраняет и загружает.
Это позволяет администраторам дать пользователям безопасный и строго ограниченный способ определять функции на произвольном языке (например, на C, компилируемом внутри изолированного контейнера), не предоставляя им доступ к конфигурационным файлам или файловой системе сервера. Набор доступных драйверов полностью контролируется оператором.
Включение драйверов
Исполняемые UDF на базе драйверов по умолчанию отключены. Чтобы их включить:-
Установите экспериментальный флаг в конфигурации сервера:
-
Укажите в
user_defined_executable_function_drivers_configодин или несколько файлов конфигурации драйверов (поддерживаются glob-шаблоны) и при необходимости задайтеdynamic_user_defined_executable_functions_path— каталог, где хранятся сгенерированные конфигурации исполняемых UDF:
SYSTEM RELOAD CONFIG, поэтому драйверы можно добавлять, изменять или удалять без перезапуска сервера.
Конфигурация драйвера
Драйвер описывается XML- или YAML-файлом с корневым элементом<driver>. Поддерживаются следующие поля:
Пример конфигурации драйвера:
Контракт вызова драйвера
При выполненииCREATE FUNCTION вызывается create_command с заданными переменными env и следующими аргументами:
--name <function_name>--return <return_type>(если указана секцияRETURNS)--args <signature>(если указана секцияARGUMENTS), где signature — это объявленный список аргументов, напримерx UInt8, y DateTime--<key> <value>для каждого объявленного аргумента движка, указанного вENGINE = DriverName(key = value)
AS) передаётся на стандартный ввод команды. Команда должна вывести конфигурацию исполняемой UDF в стандартный вывод. Формат определяется автоматически: вывод, начинающийся с <, обрабатывается как XML, иначе — как YAML. Имя функции, заданное в сгенерированной конфигурации, должно совпадать с именем создаваемой функции. Если create_command завершается с ненулевым кодом выхода, оператор завершается ошибкой с исключением, включающим код выхода и содержимое стандартного потока ошибок драйвера.
drop_command, если он указан, вызывается таким же образом (без тела кода в stdin) при удалении функции.
Создание FUNCTION
create_command драйвера, записывает сгенерированную конфигурацию в dynamic_user_defined_executable_functions_path, после чего её подхватывает существующий загрузчик исполняемых UDF. После этого функцию можно вызывать как и любую другую функцию.
Удаление функции
DROP FUNCTION вызывает drop_command драйвера (если она задана), удаляет сгенерированную динамическую конфигурацию и отдельный рабочий каталог функции, перезагружает загрузчик исполняемых UDF и удаляет сохранённый запрос.
Сохранение и перезапуск
Исходный запрос сохраняется в виде оператораATTACH FUNCTION ... в каталоге пользовательских SQL-объектов, поэтому функция сохраняется после перезапуска сервера. При запуске сгенерированные конфигурации из dynamic_user_defined_executable_functions_path загружаются напрямую, без повторного запуска драйвера. Если для сохранённого ATTACH FUNCTION нет соответствующей сгенерированной конфигурации (например, если динамический каталог был утерян), драйвер запускается повторно, чтобы создать её заново.
Ограничения
- Эта возможность экспериментальная и доступна только при включении
allow_experimental_executable_udf_drivers. - Исполняемые UDF на базе драйверов не поддерживаются с реплицируемым хранилищем пользовательских функций (
ON CLUSTERи<user_defined_zookeeper_path>), поскольку реплицируется только исходный запрос, а не сгенерированные артефакты. RESTOREисполняемого UDF на базе драйверов из резервной копии сохраняет запрос, но не запускает драйвер повторно; сгенерированная конфигурация материализуется позже в процессе восстановления при перезапуске.
Пример драйверов на C
В дереве исходного кода есть демонстрационные драйверы вprograms/server/user_defined_executable_function_drivers_config.d/, которые компилируют и выполняют тело функции на C. Это лишь примеры, и пакеты их не устанавливают:
DockerC- компилирует и выполняет код внутри изолированных контейнеров Docker (--network=none --read-only --cap-drop=ALL --security-opt=no-new-privileges, а также с ограничениями по памяти/CPU/PID), создавая UDFexecutable_pool.GVisorC- вариант, запускающий скомпилированный бинарный файл в среде выполненияrunscот gVisor.UnsafeC- компилирует и выполняет код напрямую на хосте, без песочницы. Как следует из названия, он не обеспечивает никакой изоляции и предназначен только для доверенных окружений и тестирования.