Files
doris/docs/documentation/cn/extending-doris/user-defined-function.md

113 lines
5.2 KiB
Markdown

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# 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)`
1. `AddUdf`可以为任意的名字,只要创建UDF的时候指定即可。
2. 实现函数中的第一个参数永远是`FunctionContext*`。实现者可以通过这个结构体获得一些查询相关的内容,以及申请一些需要使用的内存。具体使用的接口可以参考`udf/udf.h`中的定义。
3. 实现函数中从第二个参数开始需要与UDF的参数一一对应,比如`IntVal`对应`INT`类型。这部分的类型都要使用`const`引用。
4. 返回参数与UDF的参数的类型要相对应。
#### 可变参数
对于可变参数,可以参见以下例子,UDF`String md5sum(String, ...)`对应的
实现函数是`StringVal md5sumUdf(FunctionContext* ctx, int num_args, const StringVal* args)`
1. `md5sumUdf`这个也是可以任意改变的,创建的时候指定即可。
2. 第一个参数与非可变参数函数一样,传入的是一个`FunctionContext*`
3. 可变参数部分由两部分组成,首先会传入一个整数,说明后面还有几个参数。后面传入的是一个可变参数部分的数组。
#### 类型对应关系
|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"][,...])
```
说明:
1. PROPERTIES中`symbol`表示的是,执行入口函数的对应symbol,这个参数是必须设定。你可以通过`nm`命令来获得对应的symbol,比如`nm libudfsample.so | grep AddUdf`获得到的`_ZN9doris_udf6AddUdfEPNS_15FunctionContextERKNS_6IntValES4_`就是对应的symbol。
2. PROPERTIES中`object_file`表示的是从哪里能够下载到对应的动态库,这个参数是必须设定的。
3. 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`