PL/Java语言函数
使用GaussDB(DWS)数据库的PL/Java函数,用户可以使用自己喜欢的Java IDE编写Java方法,并将包含这些方法的jar文件安装到GaussDB(DWS)数据库中,然后使用该方法。GaussDB(DWS) PL/Java基于开源PL/Java 1.5.5开发,所使用的JRE版本为1.8.0_322。
使用限制
Java UDF可以实现一些java逻辑计算,强烈建议不要在Java UDF中封装业务
- 强烈建议不要在Java函数中使用任何方式连接数据库,包括但不限于JDBC。
- 暂不支持的数据类型:除表1内容之外的数据类型,包括自定义类型,复杂数据类型(Java Array类及派生类)。
- 暂不支持UDAF,UDTF。
示例
使用PL/Java函数时,需要首先将Java方法的实现打包为jar包并且部署到数据库中,然后使用数据库管理员账号创建函数,考虑兼容性问题,请使用1.8.0_322版本的JRE进行编译。
-
Java方法的实现和出包可以借助IDE来实现,以下是一个通过命令行来进行编译和出包的简单的示例,通过这个简单示例可以创建出一个包含单个方法的jar包文件。
首先,编写一个Example.java文件,在此文件中实现子字符串大写转换的方法,本例中类名为Example,方法名为upperString,内容如下:
|
1 2 3 4 5 6 7| ``` public class Example { public static String upperString (String text, int beginIndex, int endIndex) { return text.substring(beginIndex, endIndex).toUpperCase(); } }| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 然后,创建manifest.txt清单文件,文件内容如下: | ``` 1 2 3 4 5 6 ``` | ``` Manifest-Version: 1.0 Main-Class: Example Specification-Title: "Example" Specification-Version: "1.0" Created-By: 1.6.0_35-b10-428-11M3811 Build-Date: 08/14/2018 10:09 AM ``` | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 其中,Manifest-Version定义了manifest文件的版本,Main-Class定义了jar文件的入口类,Specification-Title和Specification-Version属于包的扩展属性,Specification-Title定义了扩展规范的标题,Specification-Version定义了扩展规范的版本,Created-By声明了该文件的生成者,Build-Date声明了该文件构建日期。 最后,编译java文件并打包得到javaudf-example.jar | ``` 1 2 ``` | ``` javac Example.java jar cfm javaudf-example.jar manifest.txt Example.class ``` | | ----------- | ---------------------------------------------------------------------------------- | ![]() jar包的命名规则应符合JDK命名要求,如果含有非法字符,在部署或者使用函数时将出错。 -
部署jar 包。
Jar包的部署需要omm用户在命令行中调用gs_om工具完成。以操作系统用户omm登录CN所在主机,执行source ${BIGDATA_HOME}/mppdb/.mppdbgs_profile命令启动环境变量。
首先,修改jar包权限:
|
1| ``` chmod 644 javaudf-example.jar| --------- | -------------------------------------- | 然后,将jar包部署到数据库中: | ``` 1 ``` | ``` gs_om -t javaUDF -m addjar -s javaudf-example.jar ``` | | --------- | ---------------------------------------------------------- | gs_extend_library函数如何使用请参见[ 管理jar包和文件]()。函数中的AK/SK值,请用户根据实际获取值替换。region_name请用户根据实际所在的区域名称替换。 -
使用PL/Java 函数。
首先,使用拥有sysadmin权限的数据库用户(例如:omm)登录数据库并创建java_upperstring函数如下:
|
1 2 3 4| ``` CREATE FUNCTION java_upperstring(VARCHAR, INTEGER, INTEGER) RETURNS VARCHAR AS 'Example.upperString' LANGUAGE JAVA;| --------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ![]() - 函数java_upperstring中定义的数据类型为GaussDB(DWS)的数据类型。该数据类型需要和[步骤1]()中java定义的方法upperString中数据类型一一对应。GaussDB(DWS)与Java数据类型的对应关系,请参见[表1]()。 - AS子句用于指定该函数所调用的Java方法的类名和static方法名,格式为“类名.方法名”。该字段需要和[步骤1]()中java定义的类名和方法名一致。 - 使用PL/Java函数时,LANGUAGE字段应指定为JAVA。 - CREATE FUNCTION更多说明,请参见[创建函数]()。 然后,执行java_upperstring函数: | ``` 1 ``` | ``` SELECT java_upperstring('test', 0, 1); ``` | | --------- | ----------------------------------------------- | 得到预期结果为: | ``` 1 2 3 4 ``` | ``` java_upperstring --------------------- T (1 row) ``` | | --------------- | ----------------------------------------------------------- | -
授权普通用户使用 PL/Java 函数 。
创建普通用户,名称为udf_user。
|
1| ``` CREATE USER udf_user PASSWORD 'password';| --------- | -------------------------------------------------- | 授权普通用户udf_user对java_upperstring函数的使用权限。注意,此处需要把函数所在模式和函数的使用权限同时赋予给用户,用户才可以使用此函数。 | ``` 1 2 ``` | ``` GRANT ALL PRIVILEGES ON SCHEMA public TO udf_user; GRANT ALL PRIVILEGES ON FUNCTION java_upperstring(VARCHAR, INTEGER, INTEGER) TO udf_user; ``` | | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | 以普通用户udf_user登录数据库。 | ``` 1 ``` | ``` SET SESSION SESSION AUTHORIZATION udf_user PASSWORD 'password'; ``` | | --------- | ------------------------------------------------------------------------ | 执行java_upperstring函数: | ``` 1 ``` | ``` SELECT public.java_upperstring('test', 0, 1); ``` | | --------- | ------------------------------------------------------ | 得到预期结果为: | ``` 1 2 3 4 ``` | ``` java_upperstring --------------------- T (1 row) ``` | | --------------- | ----------------------------------------------------------- | -
删除函数。
如果不再使用该函数可以进行删除:
|
1| ``` DROP FUNCTION java_upperstring;| --------- | ---------------------------------------- | -
卸载jar 包。
Jar包的卸载同样需要omm用户通过命令行调用gs_om工具完成:
|
1| ``` gs_om -t javaUDF -m rmjar -d javaudf-example.jar| --------- | --------------------------------------------------------- |
gs_om -t javaUDF工具
目前可以通过两种方式进行部署,一种是使用gs_om命令行工具,该工具可以部署任意格式文件,另一种是使用SQL函数gs_extend_library进行部署,该方式功能比较有限,建议线下用户使用gs_om命令行工具完成jar包或文件的部署。
-
部署jar包或文件
用户通过gs_om –t javaUDF –m addjar [-s LocalPath] [-d RemoteRelativePath] 命令实现jar包和其他配置文件的安装部署。
-t javaUDF:操作名称。
-m addjar:表明操作类型为新增文件。
-s LocalPath:命令执行节点的绝对路径,可以是文件也可以是路径,不能为空。
-d RemoteRelativePath:相对路径,可缺省,若LocalPath为目录,该操作可以把LocalPath目录下的所有文件以及子目录及文件拷贝到集群中所有服务器的GAUSSHOME/lib/java/[RemoteRelativePath]目录下。拷贝过程中如果存在冲突,提供是否覆盖选项y/n/a,分别表示是、否、全部替换,由用户根据实际需要自行选择策略。
-
卸载jar包或文件
用户通过gs_om –t javaUDF –m rmjar [-d RemoteRelativePath] 命令实现jar和其他配置文件的卸载。
-t javaUDF:操作名称。
-m rmjar:表明操作类型为删除文件。
-d RemoteRelativePath:相对路径,可缺省,该操作可以删除集群中所有节点的指定目录$GAUSSHOME/lib/java/[RemoteRelativePath] 及其下层目录下的目录、jar包和文件。
-
查看jar 包或文件
线下用户通过gs_om -t javaUDF –m ls [-d RemoteRelativePath]命令查看已部署的jar和配置文件。并对文件一致性做校验。
-t javaUDF:操作名称。
-m ls:表明操作类型为查看文件。
-d RemoteRelativePath:相对路径,可缺省,该操作可以查看集群中所有节点的指定目录$GAUSSHOME/lib /java/[RemoteRelativePath] 及其下层目录下的目录、jar包和文件(包括每个jar包或配置文件的相对路径),以执行节点为标准,对各节点中jar包及配置文件进行一致性检查。一致性检查会检查名称和MD5码,若有不一致给以警告。
SQL定义与使用
-
拥有sysadmin权限的数据库用户可以使用gs_extend_library函数来部署、查看和删除数据库中的jar包,函数语法如下:
|
1| ``` SELECT gs_extend_library('[action]', '[operation]');| --------- | ------------------------------------------------------------- | ![]() - **action**表明操作动作,值可以为: - ls表示查看数据库中jar包,会对各节点的文件进行MD5值一致性检查。 - addjar表示将本地文件系统中的jar包部署到数据库中。 - rmjar表示将数据库中的jar包删除。 - **operation**表明操作字符串,格式为: file://[source_filepath] libraryname=[libraryname] - source_filepath: 本地文件的绝对路径,仅支持jar文件。 - libraryname: 自定义库名,此自定义命名用于GaussDB(DWS)内对jar文件的调用。当action为addjar和rmjar时,该参数不可缺省,当action为ls时,该参数可以缺省。注意,自定义库名不允许含有/|;&$<>\'{}"()[]~*?!等字符。 -
PL/Java函数通过CREATE FUNCTION语法创建,并且定义为LANGUAGE JAVA,且包含RETURNS和AS子句。
-
CREATE FUNCTION时指定所创建函数的名称,以及参数类型;
-
RETURNS子句用于指定该函数的返回类型;
-
AS子句与用于指定该函数所调用的Java方法的类名和static方法名,如果需要向Java方法传递NULL值作为入参,还需要指定该参数类型所对应的Java封装类名 (详见NULL值处理)。
-
更多语法说明,请参见CREATE FUNCTION。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18| ``` CREATE [ OR REPLACE ] FUNCTION function_name ( [ { argname [ argmode ] argtype [ { DEFAULT | := | = } expression ]} [, …] ]) [ RETURNS rettype [ DETERMINISTIC ] ] LANGAUGE JAVA [ { IMMUTABLE | STATBLE | VOLATILE } | [ NOT ] LEAKPROOF | WINDOW | { CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT |STRICT } | {[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | AUTHID DEFINER | AUTHID CURRENT_USER} | { FENCED } | COST execution_cost | ROWS result_rows | SET configuration_parameter { {TO |=} value | FROM CURRENT} ] […] { AS 'class_name.method_name' ( { argtype } [, …] ) }| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-
-
使用函数
执行时,PL/Java会根据jar包名的字母序列,在所有部署的jar包中寻找函数指定的Java类,并调用首次找到的类中函数所指定的Java方法,并返回调用结果。
-
删除函数
PL/Java函数通过DROP FUNCTION语法删除函数。更多语法说明,请参见DROP FUNCTION。
DROP FUNCTION [ IF EXISTS ] function_name [ ( [ {[ argmode ] [ argname ] argtype} [, ...] ] ) [ CASCADE | RESTRICT ] ];需要注意的是,如果所删除的函数为重载函数(详见重载函数),则删除时需要指明该函数的参数类型,如为非重载函数,则可直接指定函数名进行删除。
-
非sysadmin户无法创建PL/Java函数,sysadmin用户可以赋予其他类型用户使用函数的权限。更多语法说明,请参见GRANT。
GRANT { EXECUTE | ALL [ PRIVILEGES ] } ON { FUNCTION {function_name ( [ {[ argmode ] [ arg_name ] arg_type} [, ...] ] )} [, ...] | ALL FUNCTIONS IN SCHEMA schema_name [, ...] } TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ];
基本数据类型映射关系
| GaussDB(DWS) | Java |
|---|---|
| BOOLEAN | boolean |
| "char" | byte |
| bytea | byte[] |
| SMALLINT | short |
| INTEGER | int |
| BIGINT | long |
| FLOAT4 | float |
| FLOAT8 | double |
| CHAR | java.lang.String |
| VARCHAR | java.lang.String |
| TEXT | java.lang.String |
| name | java.lang.String |
| DATE | java.sql.Timestamp |
| TIME | java.sql.Time (stored value treated as local time) |
| TIMETZ | java.sql.Time |
| TIMESTAMP | java.sql.Timestamp |
| TIMESTAMPTZ | java.sql.Timestamp |
数组类型处理
GaussDB(DWS)支持基础数组类型的转换,只需要在创建函数时在数据类型后追加 [] 即可,例如:
CREATE FUNCTION java_arrayLength(INTEGER[])
RETURNS INTEGER
AS 'Example.getArrayLength'
LANGUAGE JAVA;
Java代码类似于:
public class Example
{
public static int getArrayLength(Integer[] intArray)
{
return intArray.length;
}
}
那么下面的调用的语句后:
SELECT java_arrayLength(ARRAY[1, 2, 3]);
得到预期结果应该如下所示:
java_arrayLength
---------------------
3
(1 row)
NULL值处理
对于默认与Java的简单类型进行映射转换的那些GaussDB(DWS)数据类型,是无法处理NULL值的,如果希望在Java方法里能够获得并处理从GaussDB(DWS)中传入的NULL值,可以使用Java的封装类,并通过以下方式在AS子句中指定该Java封装类:
CREATE FUNCTION java_countnulls(INTEGER[])
RETURNS INTEGER
AS 'Example.countNulls(java.lang.Integer[])'
LANGUAGE JAVA;
Java代码类似于:
public class Example
{
public static int countNulls(Integer[] intArray)
{
int nullCount = 0;
for (int idx = 0; idx < intArray.length; ++idx)
{
if (intArray[idx] == null)
nullCount++;
}
return nullCount;
}
}
那么下面的调用的语句后:
SELECT java_countNulls(ARRAY[null, 1, null, 2, null]);
得到的预期结果应该如下所示:
java_countNulls
--------------------
3
(1 row)
重载函数
PL/Java支持重载函数,因此可以创建同名函数,或者调用Java代码中的重载方法。步骤如下:
-
创建重载函数
例如,在Java中可以实现两个方法名相同,输入参数类型不同的方法dummy(int) 和dummy(String)
public class Example { public static int dummy(int value) { return value*2; } public static String dummy(String value) { return value; } }并在GaussDB(DWS)中创建两个同名函数分别指定为上述两个方法:
CREATE FUNCTION java_dummy(INTEGER) RETURNS INTEGER AS 'Example.dummy' LANGUAGE JAVA; CREATE FUNCTION java_dummy(VARCHAR) RETURNS VARCHAR AS 'Example.dummy' LANGUAGE JAVA; -
调用重载函数
在调用重载函数时,GaussDB(DWS)会根据输入的参数类型去调用匹配该类型的Java方法。因此上述两个函数的调用结果如下所示:
SELECT java_dummy(5); java_dummy ----------------- 10 (1 row) SELECT java_dummy('5'); java_dummy --------------- 5 (1 row)需要注意的是,由于GaussDB(DWS)对数据类型存在隐式转换的情况,因此建议在调用重载函数时,指定输入参数的类型,例如:
SELECT java_dummy(5::varchar); java_dummy ---------------- 5 (1 row)此时会优先匹配所指定的参数类型,如果不存在指定参数类型的Java方法,则会对参数进行隐式转换匹配转换后的参数类型对应的Java方法。
SELECT java_dummy(5::INTEGER); java_dummy ----------------- 10 (1 row) DROP FUNCTION java_dummy(INTEGER); SELECT java_dummy(5::INTEGER); java_dummy ---------------- 5 (1 row)隐式转换的数据类型包括:
- 可以默认转换为INTEGER类型的包括:SMALLINT
- 可以默认转换为BIGINT类型的包括:SMALLINT, INTEGER
- 可以默认转换为BOOL类型的包括:TINYINT, SMALLINT, INTEGER, BIGINT
- 可以默认转换为TEXT类型的包括:CHAR, NAME, BIGINT, INTEGER, SMALLINT, TINYINT, RAW, FLOAT4, FLOAT8, BPCHAR, VARCHAR, NVARCHAR2, DATE, TIMESTAMP, TIMESTAMPTZ, NUMERIC, SMALLDATETIME
- 可以默认转换为VARCHAR类型的包括:TEXT, CHAR, BIGINT, INTEGER, SMALLINT, TINYINT, RAW, FLOAT4, FLOAT8, BPCHAR, DATE, NVARCHAR2, TIMESTAMP, NUMERIC, SMALLDATETIME
-
删除重载函数
对于重载函数,删除时需要指定函数的参数类型,否则无法删除。
DROP FUNCTION java_dummy(INTEGER);