With the GaussDB(DWS) PL/Java functions, you can choose your favorite Java IDE to write Java methods and install the JAR files containing these methods into the GaussDB(DWS) database before invoking them. GaussDB(DWS) PL/Java is developed based on open-source PL/Java 1.5.5 and uses JDK 1.8.0_292.
Java UDF can be used for some Java logical computing. You are not advised to encapsulate services in Java UDF.
Before using PL/Java, you need to pack the implementation of Java methods into a JAR package and deploy it into the database. Then, create functions as a database administrator. For compatibility purposes, use JDK 1.8.0_262 for compilation.
Java method implementation and JAR package archiving can be achieved in an integrated development environment (IDE). The following is a simple example of compilation and archiving through command lines. You can create a JAR package that contains a single method in the similar way.
First, prepare an Example.java file that contains a method for converting substrings to uppercase. In the following example, Example is the class name and upperString is the method name:
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(); } } |
Then, create a manifest.txt file containing the following content:
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 specifies the version of the manifest file. Main-Class specifies the main class used by the .jar file. Specification-Title and Specification-Version are the extended attributes of the package. Specification-Title specifies the title of the extended specification and Specification-Version specifies the version of the extended specification. Created-By specifies the person who created the file. Build-Date specifies the date when the file was created.
Finally, archive the .java file and package it into javaudf-example.jar.
1 2 | javac Example.java jar cfm javaudf-example.jar manifest.txt Example.class |
JAR package names must comply with JDK rules. If a name contains invalid characters, an error occurs when a function is deployed or used.
First store the JAR package on an OBS server. For details, see "Uploading a File" in Object Storage Service Console Operation Guide. Then, create the access key AK/SK. For details about how to create access keys, see "Creating an Access Key (AK and SK)" in Data Warehouse Service User Guide. After that, log in to the database, run the gs_extend_library function, and import the package to GaussDB(DWS).
1 | SELECT gs_extend_library('addjar', 'obs://bucket/path/javaudf-example.jar accesskey=access_key_value_to_be_replaced secretkey=secret_access_key_value_to_be_replaced region=region_name libraryname=example'); |
For details about how to use the gs_extend_library function, see Manage JAR packages and files. Change the values of AK and SK as needed. Replace region_name with an actual region name.
Log in to the database as a user who has the sysadmin permission (for example, dbadmin) and create the java_upperstring function:
1 2 3 4 | CREATE FUNCTION java_upperstring(VARCHAR, INTEGER, INTEGER) RETURNS VARCHAR AS 'Example.upperString' LANGUAGE JAVA; |
Execute the java_upperstring function.
1 | SELECT java_upperstring('test', 0, 1); |
The expected result is as follows:
1 2 3 4 | java_upperstring --------------------- T (1 row) |
Create a common user named udf_user.
1 | CREATE USER udf_user PASSWORD 'password'; |
This command grants user udf_user the permission for the java_upperstring function. Note that the user can use this function only if it also has the permission for using the schema of the function.
1 2 | GRANT ALL PRIVILEGES ON SCHEMA public TO udf_user; GRANT ALL PRIVILEGES ON FUNCTION java_upperstring(VARCHAR, INTEGER, INTEGER) TO udf_user; |
Log in to the database as user udf_user.
1 | SET SESSION SESSION AUTHORIZATION udf_user PASSWORD 'password'; |
Execute the java_upperstring function.
1 | SELECT public.java_upperstring('test', 0, 1); |
The expected result is as follows:
1 2 3 4 | java_upperstring --------------------- T (1 row) |
1 | DROP FUNCTION java_upperstring; |
Use the gs_extend_library function to uninstall the JAR package.
1 | SELECT gs_extend_library('rmjar', 'libraryname=example'); |
A database user having the sysadmin permission can use the gs_extend_library function to deploy, view, and delete JAR packages in the database. The syntax of the function is as follows:
1 | SELECT gs_extend_library('[action]', '[operation]'); |
obs://[bucket]/[source_filepath] accesskey=[accesskey] secretkey=[secretkey] region=[region] libraryname=[libraryname]
PL/Java functions can be created using the CREATE FUNCTION syntax and are defined as LANGUAGE JAVA, including the RETURNS and AS clauses.
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 } [, ...] ) } |
During execution, PL/Java searches for the Java class specified by a function among all the deployed JAR packages, which are ranked by name in alphabetical order, invokes the Java method in the first found class, and returns results.
PL/Java functions can be deleted by using the DROP FUNCTION syntax. For details about the syntax, see DROP FUNCTION.
DROP FUNCTION [ IF EXISTS ] function_name [ ( [ {[ argmode ] [ argname ] argtype} [, ...] ] ) [ CASCADE | RESTRICT ] ];
To delete an overloaded function (for details, see Overloaded Functions), specify argtype in the function. To delete other functions, simply specify function_name.
Only user sysadmin can create PL/Java functions. It can also grant other users the permission to use the PL/Java functions. For details about the syntax, see 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) can convert basic array types. You only need to append a pair of square brackets ([]) to the data type when creating a function.
CREATE FUNCTION java_arrayLength(INTEGER[]) RETURNS INTEGER AS 'Example.getArrayLength' LANGUAGE JAVA;
Java code is similar to the following:
public class Example { public static int getArrayLength(Integer[] intArray) { return intArray.length; } }
Invoke the following statement:
SELECT java_arrayLength(ARRAY[1, 2, 3]);
The expected result is as follows:
java_arrayLength --------------------- 3 (1 row)
NULL values cannot be handled for GaussDB(DWS) data types that are mapped and can be converted to simple Java types by default. If you use a Java function to obtain and process the NULL value transferred from GaussDB(DWS), specify the Java encapsulation class in the AS clause as follows:
CREATE FUNCTION java_countnulls(INTEGER[]) RETURNS INTEGER AS 'Example.countNulls(java.lang.Integer[])' LANGUAGE JAVA;
Java code is similar to the following:
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; } }
Invoke the following statement:
SELECT java_countNulls(ARRAY[null, 1, null, 2, null]);
The expected result is as follows:
java_countNulls -------------------- 3 (1 row)
PL/Java supports overloaded functions. You can create functions with the same name or invoke overloaded functions from Java code. The procedure is as follows:
For example, create two Java methods with the same name, and specify the methods dummy(int) and dummy(String) with different parameter types.
public class Example { public static int dummy(int value) { return value*2; } public static String dummy(String value) { return value; } }
In addition, create two functions with the same names as the above two functions in 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) invokes the functions that match the specified parameter type. The results of invoking the above two functions are as follows:
SELECT java_dummy(5); java_dummy ----------------- 10 (1 row) SELECT java_dummy('5'); java_dummy --------------- 5 (1 row)
Note that GaussDB(DWS) may implicitly convert data types. Therefore, you are advised to specify the parameter type when invoking an overloaded function.
SELECT java_dummy(5::varchar); java_dummy ---------------- 5 (1 row)
In this case, the specified parameter type is preferentially used for matching. If there is no Java method matching the specified parameter type, the system implicitly converts the parameter and searches for Java methods based on the conversion result.
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)
Data types supporting implicit conversion are as follows:
To delete an overloaded function, specify the parameter type for the function. Otherwise, the function cannot be deleted.
DROP FUNCTION java_dummy(INTEGER);
A session-level GUC parameter. It is used to set JVM startup parameters.
SET pljava_vmoptions='-Xmx64m –Xms2m –XX:MaxMetaspaceSize=8m';
pljava_vmoptions supports:
You are not advised to set any parameters that contain directories because such setting may lead to unpredictable behavior.
If a user sets pljava_vmoptions to a value beyond the value range, an error will be reported during function revoking.
SET pljava_vmoptions=' illegal.option'; SET SELECT java_dummy(5::int); ERROR: UDF Error:cannot use PL/Java before successfully completing its setup.Please check if your pljava_vmoption is set correctly,since we do not ignore illegal parameters.Or check the log for more messages.
A session-level GUC parameter. It is used to specify the maximum virtual memory used by a single Fenced UDF Worker process initiated by a session.
SET FencedUDFMemoryLimit='512MB';
The value range of this parameter is (150 MB, 1G]. If the value is greater than 1G, an error will be reported immediately. If the value is less than or equal to 150 MB, an error will be reported during function invoking.
If there is an exception in a JVM, PL/Java will export JVM stack information during the exception to a client.
PL/Java uses the standard Java Logger. Therefore, you can record logs as follows:
Logger.getAnonymousLogger().config( "Time is " + new Date(System.currentTimeMillis()));
An initialized Java Logger class is set to the CONFIG level by default, corresponding to the LOG level in GaussDB(DWS). In this case, log messages generated by Java Logger are all redirected to the GaussDB(DWS) backend. Then, the log messages are written into server logs or displayed on the user interface. MPPDB server logs record information at the LOG, WARNING, and ERROR levels. The SQL user interface displays logs at the WARNING and ERROR levels. The following table lists mapping between Java Logger levels and GaussDB(DWS) log levels.
java.util.logging.Level |
GaussDB(DWS) Log Level |
---|---|
SERVER |
ERROR |
WARINING |
WARNING |
CONFIG |
LOG |
INFO |
INFO |
FINE |
DEBUG1 |
FINER |
DEBUG2 |
FINEST |
DEBUG3 |
You can change Java Logger levels. For example, if the Java Logger level is changed to SEVERE by the following Java code, log messages (msg) will not be recorded in GaussDB(DWS) logs during WARNING logging.
Logger log = Logger.getAnonymousLogger(); Log.setLevel(Level.SEVERE); log.log(Level.WARNING, msg);
In GaussDB(DWS), PL/Java is an untrusted language. Only user sysadmin can create PL/Java functions. The user can grant other users the permission for using the PL/Java functions. For details, see Authorize permissions for functions.
In addition, PL/Java controls user access to file systems, forbidding users from reading most system files, or writing, deleting, or executing any system files in Java methods.