#外置UDF

YashanDB支持在PL中调用外部Java方法或者外部C函数,实现数据库中的外置UDF功能。分布式部署中不可使用此功能。

# 环境要求

使用外置UDF(Java)要求数据库服务器已安装JDK(1.8及其以上版本),并配置如下环境变量:

# 如下路径需更换为实际的jdk安装路径
$ export LD_LIBRARY_PATH=/etc/jdk-18.0.2/lib/server:$LD_LIBRARY_PATH

# 创建自定义库

使用外置UDF首先需要将外置UDF所需要的自定义库创建到数据库中。

Java语言的外置UDF,YashanDB支持创建包含自定义Java函数的.class文件或者jar包的自定义库。

C语言的外置UDF,YashanDB支持创建包含C函数的.dll(Windows)和.so(Linux)动态库文件。

YashanDB通过CREATE LIBRARY中的CREATE LIBRARY语句创建自定义库。

创建自定义库时不检查库文件是否存在,在执行外置UDF时才检查并加载库文件。

示例(单机、共享集群部署)

--创建Java语言的自定义库
CREATE OR REPLACE LIBRARY ya_java_lib IS
'/home/yasdb/example/UDFexample.class';
/

UDFexample.class对应UDFexample.java文件内容如下:

package example;

public class UDFexample {
    public static String execJdbcexample(int ctrls) {
        switch (ctrls) {
        case 1:return "Hello";
        case 2:return "World";
        default:return "!";
        }
    }

    public static void main(String[] args) {
       String a = execJdbcexample(1);
    }
}
--创建C语言的自定义库
CREATE OR REPLACE LIBRARY ya_c_lib IS
'/home/yasdb/example/libUDFexample.so';
/

libUDFexample.so对应的UDFexample.c文件内容如下:

#include "string.h"
#include "yacli.h"

YacResult udfExample(YacHandle hProc)
{
    YacInt32  id;
    YacChar   buf[1024];

    YAC_CALL(yepGetInt32(hProc, 0, &id, NULL));
    (YacVoid)snprintf(buf, 1024, "Hello World, id: %d", id);
    YAC_CALL(yepReturnString(hProc, buf));

    return YAC_SUCCESS;
}

当class文件、jar包、so文件或dll文件内容发生变化时,需重新生成自定义库。

# 定义Java语言的外置UDF

通过创建一个函数,实现对Java语言的外置UDF的定义,语法如下:

syntax
CREATE OR REPLACE FUNCTION schema . function_name ( argument_define , ) RETURN return_datatype IS AS call_spec ;

其中,为实现向外置UDF传递参数和获得返回值,对函数定义的参数和返回值应与java_method里的参数和返回值一致,此处一致的具体含义为:

  • 参数个数及顺序一致。
  • 数据类型兼容,即显式地满足YashanDB和java数据类型对应关系,或者通过隐式转换后满足对应关系,对应关系见下面描述。
syntax
LANGUAGE JAVA NAME java_string_literal_name LIBRARY schema . library_name ' library_path '

# LANGUAGE

指定外置UDF所用的语言,这里为JAVA。

# NAME

指定需引用的java函数的函数签名,java_package.java_class部分长度需小于128字节,其格式为:

java_package.java_class.java_method(argu1_datatype,argu2_datatype...) [return return_datatype]

此处引用的java函数必须为static的java方法,且该方法的参数和返回类型必须在如下列示的java数据类型范围内:

  • bool:对应YashanDB的BOOLEAN
  • byte:对应YashanDB的TINYINT
  • char:对应YashanDB的INT(取值范围[0,65535],即0-16位无符号整数)
  • short:对应YashanDB的SMALLINT
  • int:对应YashanDB的INT
  • long:对应YashanDB的BIGINT
  • float:对应YashanDB的FLOAT
  • double:对应YashanDB的DOUBLE
  • string:对应YashanDB的VARCHAR

# library_name

指定已经创建的自定义库名称。

# library_path

方式一:创建function时,指定library为已创建成功的library名称。

方式二:创建function时,指定library为通过执行DBMS_STANDARD.LOADJAVA方法加载的自定义库路径(此方式不建议使用)。

示例(单机、共享集群部署)

-- 方式一
CREATE OR REPLACE FUNCTION udf_func_java(argu INT) RETURN VARCHAR IS
LANGUAGE java
NAME 'example.UDFexample.execJdbcexample(int) return string'
LIBRARY ya_java_lib;
/

-- 方式二
call DBMS_STANDARD.LOADJAVA('/home/yasdb/example/UDFexample.class');
CREATE OR REPLACE FUNCTION udf_func_java(argu INT) RETURN VARCHAR IS
LANGUAGE java
NAME 'example.UDFexample.execJdbcexample(int) return string'
LIBRARY '/home/yasdb/example/UDFexample.class';
/

# 定义C语言的外置UDF

通过创建一个函数,实现对C语言的外置UDF的定义,语法如下:

syntax
CREATE OR REPLACE FUNCTION schema . function_name ( argument_define , ) RETURN return_datatype IS AS call_spec ;

call_spec::=

syntax
LANGUAGE C EXTERNAL NAME c_string_literal_name LIBRARY schema . library_name LIBRARY schema . library_name NAME c_string_literal_name

# language

指定外置UDF所用的语言,这里为C。

# external

指定为C语言的外置UDF(建议使用LANGUAGE指定外置UDF所使用的语言)。

# name

通过c_string_literal_name指定需引用的C函数的函数名,默认转为全大写,如需保留大小写,请使用双引号包围,如果省略NAME,默认使用外置UDF的名称。

c_string_literal_name需要为合法的标识符,其最大长度为64字节。

# library_name

指定已经创建的自定义库名称。

示例(单机、共享集群部署)

CREATE OR REPLACE FUNCTION udf_func_c(argu INT) RETURN VARCHAR IS
LANGUAGE C
NAME "udfExample"
LIBRARY ya_c_lib;
/

# 编写c语言外置udf指导

C语言外置UDF是调用C语言编写的函数,将C语言文件编译为动态库文件后,在创建为自定义库被数据库调用。

C函数有唯一参数hProc,不可修改其值。通过YashanDB提供的一系列yepGet、yepOutPut、yepReturn接口实现获取入参、设置出参、设置返回值功能。

使用yepGet接口时,需根据入参类型调用相同类型或可以隐式转换类型的yepGet接口。

使用yepOutPut接口时,需根据入参类型调用相同类型的yepOutPut接口。

使用yepReturn接口时,需根据返回值类型调用相同类型或者可以隐式转换类型的yepReturn接口,且自定义函数必须调用yepReturn接口设置返回值。

接口参数id为函数定义的参数序号,起始值为0。

接口参数lenOrInd用于获取入参长度或是否为NULL值,若其值返回YAC_NULL_DATA,则说明入参为NULL,否则返回入参的字节长度。lenOrInd可为NULL。

YashanDB提供给编写C语言动态库的接口如下:

接口名称 接口函数声明 接口使用说明
yepGetCharsetId YacResult yepGetCharsetId(YacHandle hProc, YacUint16* charsetId) 获取数据库的字符集编号,赋值给charsetId。
yepGetBool YacResult yepGetBool(YacHandle hProc, YacInt32 id, YacBool* v, YacInt32* lenOrInd) 获取第id个BOOLEAN类型入参的值,赋值给v。
yepGetInt8 YacResult yepGetInt8(YacHandle hProc, YacInt32 id, YacInt8* v, YacInt32* lenOrInd) 获取第id个TINYINT类型入参的值,赋值给v。
yepGetInt16 YacResult yepGetInt16(YacHandle hProc, YacInt32 id, YacInt16* v, YacInt32* lenOrInd) 获取第id个SMALLINT类型入参的值,赋值给v。
yepGetInt32 YacResult yepGetInt32(YacHandle hProc, YacInt32 id, YacInt32* v, YacInt32* lenOrInd) 获取第id个INT类型入参的值,赋值给v。
yepGetInt64 YacResult yepGetInt64(YacHandle hProc, YacInt32 id, YacInt64* v, YacInt32* lenOrInd) 获取第id个BIGINT类型入参的值,赋值给v。
yepGetFloat YacResult yepGetFloat(YacHandle hProc, YacInt32 id, YacFloat* v, YacInt32* lenOrInd) 获取第id个FLOAT类型入参的值,赋值给v。
yepGetDouble YacResult yepGetDouble(YacHandle hProc, YacInt32 id, YacDouble* v, YacInt32* lenOrInd) 获取第id个DOUBLE类型入参的值,赋值给v。
yepGetNumber YacResult yepGetNumber(YacHandle hProc, YacInt32 id, YacNumber* v, YacInt32* lenOrInd) 获取第id个NUMBER类型入参的值,赋值给v。
yepGetDate YacResult yepGetDate(YacHandle hProc, YacInt32 id, YacDate* v, YacInt32* lenOrInd) 获取第id个DATE类型入参的值,赋值给v。
yepGetTimestamp YacResult yepGetTimestamp(YacHandle hProc, YacInt32 id, YacTimestamp* v, YacInt32* lenOrInd) 获取第id个TIMESTAMP类型入参的值,赋值给v。
yepGetYMInterval YacResult yepGetYMInterval(YacHandle hProc, YacInt32 id, YacYMInterval* v, YacInt32* lenOrInd) 获取第id个INTERVAL YEAR TO MONTH类型入参的值,赋值给v。
yepGetDSInterval YacResult yepGetDSInterval(YacHandle hProc, YacInt32 id, YacDSInterval* v, YacInt32* lenOrInd) 获取第id个INTERVAL DAY TO SECOND类型入参的值,赋值给v。
yepGetString YacResult yepGetString(YacHandle hProc, YacInt32 id, YacChar* str, YacUint32 bufSize, YacInt32* lenOrInd) 获取第id个字符串类型(CHAR、VARCHAR)入参的值,需要提前分配内存,传入指针str和bufSize。
yepGetBytes YacResult yepGetBytes(YacHandle hProc, YacInt32 id, YacUint8* bytes, YacUint32 bufSize, YacInt32* lenOrInd) 获取第id个RAW类型入参的值,需要提前分配内存,传入指针bytes和bufSize。
yepOutputNull YacResult yepOutputNull(YacHandle hProc, YacInt32 id) 将第id个出参设置为NULL。
yepOutputBool YacResult yepOutputBool(YacHandle hProc, YacInt32 id, YacBool v) 将第id个BOOLEAN类型出参的值设置为v。
yepOutputInt8 YacResult yepOutputInt8(YacHandle hProc, YacInt32 id, YacInt8 v) 将第id个TINYINT类型出参的值设置为v。
yepOutputInt16 YacResult yepOutputInt16(YacHandle hProc, YacInt32 id, YacInt16 v) 将第id个SMALLINT类型出参的值设置为v。
yepOutputInt32 YacResult yepOutputInt32(YacHandle hProc, YacInt32 id, YacInt32 v) 将第id个INT类型出参的值设置为v。
yepOutputInt64 YacResult yepOutputInt64(YacHandle hProc, YacInt32 id, YacInt64 v) 将第id个BIGINT类型出参的值设置为v。
yepOutputFloat YacResult yepOutputFloat(YacHandle hProc, YacInt32 id, YacFloat v) 将第id个FLOAT类型出参的值设置为v。
yepOutputDouble YacResult yepOutputDouble(YacHandle hProc, YacInt32 id, YacDouble v) 将第id个DOUBLE类型出参的值设置为v。
yepOutputNumber YacResult yepOutputNumber(YacHandle hProc, YacInt32 id, YacNumber* v) 将第id个NUMBER类型出参的值设置为v。
yepOutputDate YacResult yepOutputDate(YacHandle hProc, YacInt32 id, YacDate v) 将第id个DATE类型出参的值设置为v。
yepOutputTimestamp YacResult yepOutputTimestamp(YacHandle hProc, YacInt32 id, YacTimestamp* v) 将第id个TIMESTAMP类型出参的值设置为v。
yepOutputYMInterval YacResult yepOutputYMInterval(YacHandle hProc, YacInt32 id, YacYMInterval v) 将第id个INTERVAL YEAR TO MONTH类型出参的值设置为v。
yepOutputDSInterval YacResult yepOutputDSInterval(YacHandle hProc, YacInt32 id, YacDSInterval v) 将第id个INTERVAL DAY TO SECOND类型出参的值设置为v。
yepOutputString YacResult yepOutputString(YacHandle hProc, YacInt32 id, YacChar* str) 将第id个字符串类型(CHAR、VARCHAR)类型出参的值设置为str指向的字符串。
yepOutputBytes YacResult yepOutputBytes(YacHandle hProc, YacInt32 id, YacUint8* bytes, YacUint32 size) 将第id个RAW类型出参的值设置为bytes指向的字节,大小为size。
yepReturnNull #define yepReturnNull(hProc) yepOutputNull(hProc, YEP_RETURN) 将返回值设置为NULL。
yepReturnBool #define yepReturnBool(hProc, value) yepOutputBool(hProc, YEP_RETURN, value) 将返回值设置为BOOLEAN类型,值为value。
yepReturnInt8 #define yepReturnInt8(hProc, value) yepOutputInt8(hProc, YEP_RETURN, value) 将返回值设置为TINYINT类型,值为value。
yepReturnInt16 #define yepReturnInt16(hProc, value) yepOutputInt16(hProc, YEP_RETURN, value) 将返回值设置为SMALLINT类型,值为value。
yepReturnInt32 #define yepReturnInt32(hProc, value) yepOutputInt32(hProc, YEP_RETURN, value) 将返回值设置为INT类型,值为value。
yepReturnInt64 #define yepReturnInt64(hProc, value) yepOutputInt64(hProc, YEP_RETURN, value) 将返回值设置为BIGINT类型,值为value。
yepReturnFloat #define yepReturnFloat(hProc, value) yepOutputFloat(hProc, YEP_RETURN, value) 将返回值设置为FLOAT类型,值为value。
yepReturnDouble #define yepReturnDouble(hProc, value) yepOutputDouble(hProc, YEP_RETURN, value) 将返回值设置为DOUBLE类型,值为value。
yepReturnNumber #define yepReturnNumber(hProc, value) yepOutputNumber(hProc, YEP_RETURN, value) 将返回值设置为NUMBER类型,值为value。
yepReturnDate #define yepReturnDate(hProc, value) yepOutputDate(hProc, YEP_RETURN, value) 将返回值设置为DATE类型,值为value。
yepReturnTimestamp #define yepReturnTimestamp(hProc, value) yepOutputTimestamp(hProc, YEP_RETURN, value) 将返回值设置为TIMESTAMP类型,值为value。
yepReturnYMInterval #define yepReturnYMInterval(hProc, value) yepOutputYMInterval(hProc, YEP_RETURN, value) 将返回值设置为INTERVAL YEAR TO MONTH类型,值为value。
yepReturnDSInterval #define yepReturnDSInterval(hProc, value) yepOutputDSInterval(hProc, YEP_RETURN, value) 将返回值设置为INTERVAL DAY TO SECOND类型,值为value。
yepReturnString #define yepReturnString(hProc, value) yepOutputString(hProc, YEP_RETURN, value) 将返回值设置为VARCHAR类型,值为value指向的字符串。
yepReturnBytes #define yepReturnBytes(hProc, value, size) yepOutputBytes(hProc, YEP_RETURN, value, size) 将返回值设置为RAW类型,值为bytes指向的字节,大小为size。

# 运行外置udf

对于已成功定义的外置UDF,其调用方法与函数一致。

示例(单机、共享集群部署)

SELECT udf_func_java(1) FROM dual;
UDF_FUNC_JAVA(1)                                                      
---------------------------------------------------------------- 
Hello  

SELECT udf_func_java(2) FROM dual;
UDF_FUNC_JAVA(2)                                                      
---------------------------------------------------------------- 
World 

SELECT udf_func_c(1) FROM dual;
UDF_FUNC_C(1)
----------------------------------------------------------------
Hello World, id: 1

# 删除外置udf

删除定义了某个外置UDF的函数,即实现对这个外置UDF的删除。

# 删除自定义库

确认外置UDF使用的自定义库不再使用时,可对其进行删除,使用DROP_LIBRARY语句删除自定义库。