User Define Function
用户可以通过UDF机制来扩展Doris的能力。通过这篇文档,用户能够创建自己的UDF。
编写UDF函数
在使用UDF之前,用户需要先在Doris的UDF框架下,编写自己的UDF函数。在be/src/udf_samples/udf_sample.h|cpp
文件中是一个简单的UDF Demo。
编写一个UDF函数需要以下几个步骤。
编写函数
创建对应的头文件、CPP文件,在CPP文件中实现你需要的逻辑。CPP文件中的实现函数格式与UDF的对应关系。
非可变参数
对于非可变参数的UDF,那么两者之间的对应关系很直接。 比如INT MyADD(INT, INT)
的UDF就会对应IntVal AddUdf(FunctionContext* context, const IntVal& arg1, const IntVal& arg2)
。
AddUdf
可以为任意的名字,只要创建UDF的时候指定即可。- 实现函数中的第一个参数永远是
FunctionContext*
。实现者可以通过这个结构体获得一些查询相关的内容,以及申请一些需要使用的内存。具体使用的接口可以参考udf/udf.h
中的定义。 - 实现函数中从第二个参数开始需要与UDF的参数一一对应,比如
IntVal
对应INT
类型。这部分的类型都要使用const
引用。 - 返回参数与UDF的参数的类型要相对应。
可变参数
对于可变参数,可以参见以下例子,UDFString md5sum(String, ...)
对应的 实现函数是StringVal md5sumUdf(FunctionContext* ctx, int num_args, const StringVal* args)
md5sumUdf
这个也是可以任意改变的,创建的时候指定即可。- 第一个参数与非可变参数函数一样,传入的是一个
FunctionContext*
。 - 可变参数部分由两部分组成,首先会传入一个整数,说明后面还有几个参数。后面传入的是一个可变参数部分的数组。
类型对应关系
UDF Type | Argument Type |
---|---|
TinyInt | TinyIntVal |
SmallInt | SmallIntVal |
Int | IntVal |
BigInt | BigIntVal |
LargeInt | LargeIntVal |
Float | FloatVal |
Double | DoubleVal |
Date | DateTimeVal |
Datetime | DateTimeVal |
Char | StringVal |
Varchar | StringVal |
Decimal | DecimalVal |
编译UDF函数
编译Doris
在Doris根目录下执行sh build.sh
就会在output/udf/
生成对应headers|libs
编写CMakeLists.txt
基于上一步生成的headers|libs
,用户可以使用CMakeLists
等工具引入该依赖;在CMakeLists
中,可以通过向CMAKE_CXX_FLAGS
添加-I|L
分别指定headers|libs
路径;然后使用add_library
添加动态库。例如,在be/src/udf_samples/CMakeLists.txt
中,使用add_library(udfsample SHARED udf_sample.cpp)
增加了一个udfsample
动态库。后面需要写上涉及的所有源文件(不包含头文件)。
执行编译
在该目录下创建一个build
目录并在build
下执行cmake ../
生成Makefile
,并执行make
就会生成对应动态库。
创建UDF函数
通过上述的步骤后,你可以得到一个动态库。你需要将这个动态库放到一个能够通过HTTP协议访问到的位置。然后执行创建UDF函数在Doris系统内部创建一个UDF,你需要拥有AMDIN权限才能够完成这个操作。
CREATE [AGGREGATE] FUNCTION
name ([argtype][,...])
[RETURNS] rettype
PROPERTIES (["key"="value"][,...])
说明:
- PROPERTIES中
symbol
表示的是,执行入口函数的对应symbol,这个参数是必须设定。你可以通过nm
命令来获得对应的symbol,比如nm libudfsample.so | grep AddUdf
获得到的_ZN9doris_udf6AddUdfEPNS_15FunctionContextERKNS_6IntValES4_
就是对应的symbol。 - PROPERTIES中
object_file
表示的是从哪里能够下载到对应的动态库,这个参数是必须设定的。 - name: 一个function是要归属于某个DB的,name的形式为
dbName
.funcName
。当dbName
没有明确指定的时候,就是使用当前session所在的db作为dbName
。
具体使用可以参见 CREATE FUNCTION
获取更详细信息。
使用UDF
用户使用UDF/UDAF必须拥有对应数据库的 SELECT
权限。
UDF的使用与普通的函数方式一致,唯一的区别在于,内置函数的作用域是全局的,而UDF的作用域是DB内部。当链接session位于数据内部时,直接使用UDF名字会在当前DB内部查找对应的UDF。否则用户需要显示的指定UDF的数据库名字,例如dbName
.funcName
。
删除UDF函数
当你不再需要UDF函数时,你可以通过下述命令来删除一个UDF函数, 可以参考 DROP FUNCTION
。