Пользовательская функция (UDF)
Эта возможность поддерживается в закрытом предварительном просмотре в ClickHouse Cloud. Пожалуйста, свяжитесь со службой поддержки ClickHouse по адресу https://clickhouse.cloud/support для получения доступа.
ClickHouse может вызывать любую внешнюю исполняемую программу или скрипт для обработки данных.
Конфигурация исполняемых пользовательских функций может находиться в одном или нескольких XML-файлах.
Путь к конфигурации указывается в параметре user_defined_executable_functions_config.
Конфигурация функции содержит следующие настройки:
| Parameter | Description | Required | Default Value |
|---|---|---|---|
name | Имя функции | Yes | - |
command | Имя скрипта для выполнения или команда, если execute_direct имеет значение false | Yes | - |
argument | Описание аргумента с type и необязательным name аргумента. Каждый аргумент описывается в отдельной настройке. Указание имени требуется, если имена аргументов являются частью сериализации для формата пользовательской функции, такого как Native или JSONEachRow | Yes | c + argument_number |
format | Формат, в котором аргументы передаются команде. Ожидается, что вывод команды также будет использовать тот же формат | Yes | - |
return_type | Тип возвращаемого значения | Yes | - |
return_name | Имя возвращаемого значения. Указание имени возвращаемого значения требуется, если оно является частью сериализации для формата пользовательской функции, такого как Native или JSONEachRow | Optional | result |
type | Тип исполняемой сущности. Если type имеет значение executable, то запускается одна команда. Если задано executable_pool, то создаётся пул команд | Yes | - |
max_command_execution_time | Максимальное время выполнения в секундах для обработки блока данных. Эта настройка применяется только для команд executable_pool | Optional | 10 |
command_termination_timeout | Время в секундах, в течение которого команда должна завершить работу после закрытия её канала. По истечении этого времени процессу, выполняющему команду, отправляется сигнал SIGTERM | Optional | 10 |
command_read_timeout | Таймаут чтения данных из stdout команды в миллисекундах | Optional | 10000 |
command_write_timeout | Таймаут записи данных в stdin команды в миллисекундах | Optional | 10000 |
pool_size | Размер пула команд | Optional | 16 |
send_chunk_header | Определяет, нужно ли отправлять количество строк перед отправкой блока данных на обработку | Optional | false |
execute_direct | Если execute_direct = 1, то command будет искаться в папке user_scripts, заданной параметром user_scripts_path. Дополнительные аргументы скрипта можно указать, используя пробел в качестве разделителя. Пример: script_name arg1 arg2. Если execute_direct = 0, command передаётся как аргумент для bin/sh -c | Optional | 1 |
lifetime | Интервал перезагрузки функции в секундах. Если установлено значение 0, функция не перезагружается | Optional | 0 |
deterministic | Определяет, является ли функция детерминированной (возвращает одинаковый результат для одинаковых входных данных) | Optional | false |
Команда должна читать аргументы из STDIN и выводить результат в STDOUT. Команда должна обрабатывать аргументы итеративно, то есть после обработки блока аргументов она должна ожидать следующий блок.
Выполнимые пользовательские функции
Примеры
UDF из inline-скрипта
Создайте test_function_sum, вручную установив execute_direct в 0, используя конфигурацию в формате XML или YAML.
- XML
- YAML
Файл test_function.xml (/etc/clickhouse-server/test_function.xml при настройках путей по умолчанию).
Файл test_function.yaml (/etc/clickhouse-server/test_function.yaml при настройках путей по умолчанию).
UDF из скрипта на Python
В этом примере мы создаём UDF, который читает значение из STDIN и возвращает его в виде строки.
Создайте test_function, используя конфигурацию в формате XML или YAML.
- XML
- YAML
Файл test_function.xml (/etc/clickhouse-server/test_function.xml при стандартных настройках пути).
Файл test_function.yaml (/etc/clickhouse-server/test_function.yaml при стандартных настройках пути).
Создайте файл скрипта test_function.py в каталоге user_scripts (/var/lib/clickhouse/user_scripts/test_function.py при стандартных настройках пути).
Считать два значения из STDIN и вернуть их сумму в виде JSON-объекта
Создайте test_function_sum_json с именованными аргументами и форматом JSONEachRow, используя конфигурацию в формате XML или YAML.
- XML
- YAML
Файл test_function.xml (/etc/clickhouse-server/test_function.xml при использовании путей по умолчанию).
Файл test_function.yaml (/etc/clickhouse-server/test_function.yaml при использовании путей по умолчанию).
Создайте файл скрипта test_function_sum_json.py в каталоге user_scripts (/var/lib/clickhouse/user_scripts/test_function_sum_json.py при использовании путей по умолчанию).
Использование параметров в настройке command
Исполняемые пользовательские функции могут принимать константные параметры, задаваемые в настройке command (это работает только для пользовательских функций типа executable).
Также требуется опция execute_direct, чтобы исключить уязвимости, связанные с расширением аргументов оболочкой.
- XML
- YAML
Файл test_function_parameter_python.xml (/etc/clickhouse-server/test_function_parameter_python.xml при настройках пути по умолчанию).
Файл test_function_parameter_python.yaml (/etc/clickhouse-server/test_function_parameter_python.yaml при настройках пути по умолчанию).
Создайте файл скрипта test_function_parameter_python.py в каталоге user_scripts (/var/lib/clickhouse/user_scripts/test_function_parameter_python.py при настройках пути по умолчанию).
UDF на основе shell-скрипта
В этом примере мы создаём shell-скрипт, который умножает каждое значение на 2.
- XML
- YAML
Файл test_function_shell.xml (/etc/clickhouse-server/test_function_shell.xml при стандартных настройках пути).
Файл test_function_shell.yaml (/etc/clickhouse-server/test_function_shell.yaml при стандартных настройках пути).
Создайте файл скрипта test_shell.sh в каталоге user_scripts (/var/lib/clickhouse/user_scripts/test_shell.sh при стандартных настройках пути).
Обработка ошибок
Некоторые функции могут выбрасывать исключение, если данные некорректны. В этом случае запрос отменяется, а клиенту возвращается текст ошибки. При распределённой обработке, когда исключение происходит на одном из серверов, остальные серверы также пытаются прервать выполнение запроса.
Вычисление выражений аргументов
Почти во всех языках программирования один из аргументов может не вычисляться для некоторых операторов.
Обычно это операторы &&, || и ?:.
В 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.