- 可执行 UDFs 会启动外部程序或脚本 (Python、Bash 等) ,并通过 STDIN / STDOUT 以流式方式向其传输数据块。可用于在无需重新编译 ClickHouse 的情况下集成现有代码或工具。与进程内方案相比,它们的单次调用开销更高,因此更适合较重的逻辑,或需要不同运行时的场景。
- SQL UDFs 使用
CREATE FUNCTION通过纯 SQL 定义。它们会被内联/展开到查询计划中 (不存在进程边界) ,因此开销较低,非常适合复用表达式逻辑或简化复杂的计算列。 - Experimental WebAssembly UDFs 会在服务器进程内的沙箱中运行编译为 WebAssembly 的代码。与外部可执行程序相比,它们的单次调用开销更低;与原生扩展相比,又具备更好的隔离性,因此适合用可编译为 WASM 的语言 (如 C/C++/Rust) 编写自定义算法。
- Experimental 基于 driver 的可执行 UDFs 允许由运维人员提供的 “driver” 在函数创建时,将
CREATE FUNCTION ... ENGINE = DriverName(...) AS '...'中提供的代码片段转换为可执行 UDF (例如通过编译) 。它们构建在可执行 UDFs 之上,并且需要服务器端 driver 配置。
可执行用户自定义函数
在 ClickHouse Cloud 中,可执行 UDF 目前处于 Public Beta 阶段,并通过 Cloud 控制台 UI 创建。有关 Cloud 特定工作流,请参见 Cloud 中的用户自定义函数。
user_defined_executable_functions_config 参数指定。
函数配置包含以下设置:
命令必须从
STDIN 读取参数,并将结果输出到 STDOUT。命令必须以迭代方式处理参数。也就是说,处理完一批参数后,它必须等待下一批参数。
可执行用户自定义函数
示例
来自内联脚本的 UDF
test_function_sum,并将 execute_direct 指定为 0。
- XML
- YAML
文件
test_function.xml (默认路径设置下为 /etc/clickhouse-server/test_function.xml) 。/etc/clickhouse-server/test_function.xml
Query
Result
基于 Python 脚本的 UDF
STDIN 读取一个值,并将其作为字符串返回。
使用 XML 或 YAML 配置创建 test_function。
- XML
- YAML
文件
test_function.xml (默认路径设置下为 /etc/clickhouse-server/test_function.xml) 。/etc/clickhouse-server/test_function.xml
在
user_scripts 文件夹中创建脚本文件 test_function.py (默认路径设置下为 /var/lib/clickhouse/user_scripts/test_function.py) 。
Query
Result
从 STDIN 读取两个值,并将它们的和作为 JSON 对象返回
test_function_sum_json。
- XML
- YAML
文件
test_function.xml (默认路径设置下为 /etc/clickhouse-server/test_function.xml) 。/etc/clickhouse-server/test_function.xml
在
user_scripts 文件夹中创建脚本文件 test_function_sum_json.py (默认路径设置下为 /var/lib/clickhouse/user_scripts/test_function_sum_json.py) 。
Query
Result
在 command 设置中使用参数
command 设置中配置的常量参数 (仅适用于 executable 类型的用户自定义函数) 。
此外,还需要启用 execute_direct 选项,以避免 shell 参数展开漏洞。
- XML
- YAML
文件
test_function_parameter_python.xml (默认路径设置下为 /etc/clickhouse-server/test_function_parameter_python.xml) 。/etc/clickhouse-server/test_function_parameter_python.xml
在
user_scripts 文件夹中创建脚本文件 test_function_parameter_python.py (默认路径设置下为 /var/lib/clickhouse/user_scripts/test_function_parameter_python.py) 。
Query
Result
通过 shell 脚本创建 UDF
- XML
- YAML
文件
test_function_shell.xml (默认路径配置下为 /etc/clickhouse-server/test_function_shell.xml) 。/etc/clickhouse-server/test_function_shell.xml
在
user_scripts 文件夹中创建脚本文件 test_shell.sh (默认路径配置下为 /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 函数,它返回其运行所在服务器的名称,以便在 SELECT 查询中按服务器进行 GROUP BY。
如果查询中的某个函数是在请求方服务器上执行的,但你需要它在远程服务器上执行,可以将其包裹在 ‘any’ 聚合函数中,或者将其添加到 GROUP BY 键中。
SQL 用户自定义函数
WebAssembly 用户自定义函数
快速入门
更多信息
基于驱动的可执行用户自定义函数
这是一项 Experimental 功能,未来的发行版中可能会引入不向后兼容的变更。请通过
allow_experimental_executable_udf_drivers 服务器设置启用它。ENGINE = DriverName(...) 创建函数时,ClickHouse 会运行该驱动的 create_command,并向其传递函数签名和代码主体;驱动会对代码主体进行编译或其他处理,然后输出一份可执行 UDF 配置,供 ClickHouse 存储和加载。
这样,管理员就能为用户提供一种安全且受限的方式,用任意语言定义函数 (例如,在沙箱容器中编译的 C) ,而无需授予他们访问服务器配置文件或文件系统的权限。可用驱动的范围完全由运维方控制。
启用驱动
-
在服务器配置中启用 Experimental 功能开关:
-
将
user_defined_executable_function_drivers_config配置为指向一个或多个驱动配置文件 (支持 glob) ,并可选择设置dynamic_user_defined_executable_functions_path,即用于存储生成的可执行 UDF 配置的目录:
SYSTEM RELOAD CONFIG 时刷新,因此无需重启服务器即可添加、更改或移除驱动。
驱动程序配置
<driver> 为顶层元素的 XML (或 YAML) 文件定义。支持以下字段:
驱动程序配置示例:
驱动调用约定
CREATE FUNCTION 时,会在设置好已配置的 env 变量后调用 create_command,并传入以下参数:
--name <function_name>--return <return_type>(如果存在RETURNS子句)--args <signature>(如果存在ARGUMENTS子句) ,其中 signature 是声明的参数列表,例如x UInt8, y DateTime- 对于在
ENGINE = DriverName(key = value)中提供的每个已声明 engine 参数,都会传入--<key> <value>
AS 后面的文本) 会被发送到该命令的标准输入。该命令必须将可执行 UDF 的配置输出到标准输出。格式会自动检测:以 < 开头的输出会被视为 XML,否则视为 YAML。生成配置中定义的函数名必须与正在创建的函数名一致。如果 create_command 以非零状态退出,该 statement 会失败,并抛出包含退出码和驱动标准错误输出的异常。
如果存在 drop_command,则在删除函数时也会以相同方式调用它 (但不会通过 stdin 传入代码主体) 。
创建函数
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 没有对应的已生成配置 (例如动态目录丢失了) ,则会重新运行驱动程序来重新创建它。
限制
- 该功能为 Experimental,受
allow_experimental_executable_udf_drivers控制。 - 基于 driver 的函数不支持复制型用户自定义函数存储 (
ON CLUSTER和<user_defined_zookeeper_path>) ,因为被复制的只有发起查询,不包括生成的制品。 - 对已备份的基于 driver 的函数执行
RESTORE时,会保留查询,但不会重新运行 driver;生成的 configuration 会在重启恢复期间稍后物化。
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 限制) ,并输出一个executable_poolUDF。GVisorC- 一种变体,在 gVisorrunsc运行时下运行已编译的可执行文件。UnsafeC- 直接在主机上编译并运行代码,不使用沙箱。顾名思义,它不提供任何隔离,仅适用于受信任环境和测试。