Installing PL/Java
对于Greenplum数据库,PL/Java扩展可作为一个包提供。从Pivotal网络下载软件包,然后使用Greenplum软件包管理器(gppkg)进行安装。
gppkg实用程序会在群集中的所有主机上安装Greenplum数据库扩展以及任何依赖项。在系统扩展和分段恢复的情况下,它也自动在新主机上安装扩展。
有关gppkg的信息,请参阅Greenplum数据库实用程序指南。
要安装和使用PL/Java:
1.安装Greenplum数据库PL/Java扩展。
2.可选。更改PL/Java使用的Java版本。
3.为每个打算使用PL/Java的数据库启用语言。
4.将包含Java方法的用户创建的JAR文件安装到所有Greenplum数据库主机的同一目录中。
5.将JAR文件的名称添加到Greenplum数据库服务器配置参数pljava_classpath。该参数列出了已安装的JAR文件。有关该参数的信息,请参阅“Greenplum数据库参考指南”。
Installing the Greenplum PL/Java Extension
Before you install the PL/Javaextension, make sure that your Greenplum database is running, you have sourced greenplum—path.sh, and that the $MASTER_DATA_DIRECTORYand $gphome variables are set.
1. Download thePL/Java extension package from PivotalNetwork then copy it to the master host.
2. Install thesoftware extension package by running the gppkg command. This example installs the PL/Java extension package on a Linux system:
$ gppkg -i pljava-ossv1.4.0_pv1.2_gpdb4.3orca-rhel5-x86_64.gppkg
3. Reload greenplum—path.sh.
$ source $GPHOME/greenplum_path.sh
4. RestartGreenplum Database.
$ gpstop -r
Changing Java version used by PL/Java (Optional)
PL/Java扩展包与Java JRE 6u32一起提供。安装软件包时,JRE将安装在Greenplum数据库集群的每个主机上。要在PL/Java中使用更新版本的Java,请执行以下步骤来指定Java版本的位置。有关支持的Java版本的信息,请参阅您的发行版的“Greenplum数据库发行说明”。
注意:较新的Java必须安装在所有Greenplum数据库主机的相同位置,并且必须可供Greenplum数据库管理员(gpadmin)的系统用户访问。
1.在$GPHOME/greenplum_path.sh文件中,修改JAVA_HOME和LD_LIBRARY_PATH环境变量。
•将JAVA_HOME变量设置为安装Java运行时的目录。例如,对于Oracle JRE,该目录将是/usr/java/latest。对于OpenJDK,目录是/usr/lib/jvm/jre。此示例将环境变量更改为使用/usr/java/iatest。
JAVA_HOME=/usr/java/latest
•将LD_LIBRARY_PATH设置为包含具有Java服务器运行时库的目录。 PL/Java依赖于libjvm.so,共享对象应该在你的LD_LIBRARY_PATH中。默认情况下,libjvm.so在$jAVA_HoME/iib/amd64/server中可用。本示例将该目录添加到环境变量。
LD_LIBRARY_PATH=$GPHOME/lib:$GPHOME/ext/python/lib:$JAVA_HOME/lib/amd64/server:$LD_LIBRARY_PATH
2.将已升级的greenplum-path.sh文件复制到所有的Greenplum数据库主机。此示例gpscp命令将文件复制到文件gphosts_file中指定的所有主机。
$ gpscp -fgphosts_file $GPHOME/greenplum_path.sh
=:$GPHOME/greenplum_path.sh
3.重新加载greenplum-path.sh。
$ source$GPHOME/greenplum_path.sh
4.重新启动Greenplum数据库。
$ gpstop -r
Enabling PL/Java and Installing JAR Files
以Greenplum数据库管理员gpadmin的身份执行以下步骤。
1.通过在将使用PL/Java的数据库中运行SQL脚本$ GPHoME/share/postgresqi/pijava/instaii.sqi来启用PL/Java。例如,这个例子在数据库上启用了PL/Java
$ psql -d mytestdb
-f $GPHOME/share/postgresql/pljava/install.sql
脚本install.sql注册可信和不可信的PL/Java语言。
2.将您的Java归档(JAR文件)复制到所有Greenplum数据库主机上的相同目录。本示例使用Greenplum数据库gpscp实用程序将文件myclasses.jar复制到目录
$GPHOME/lib/postgresql/java/:
$ gpscp -f gphosts_file myclasses.jar
=:/usr/local/greenplum-db/lib/postgresql/java/
文件gphosts_file包含Greenplum数据库主机的列表。
3.在主postgresql.conf文件中设置pijava_ciasspath服务器配置参数。对于这个例子,参数值是一个以冒号(:)分隔的JAR文件列表。例如:
$ gpconfig -c pljava_classpath
-v \'examples.jar:myclasses.jar\'
当您使用gppkg实用程序安装PL/Java扩展包时,会安装文件examples.jar:。
注:如果将JAR文件安装在$GPHOME/lib/postgresql/java/以外的目录中,则必须指定JAR文件的绝对路径。每个JAR文件必须位于所有Greenplum数据库主机上的相同位置。有关指定JAR文件位置的更多信息,请参阅
有关Greenplum数据库参考指南中的pljava_classpath服务器配置参数的信息。
4.重新加载postgresql.conf文件。
$ gpstop -u
5.(可选)Greenplum提供了一个exampies.sqi文件,其中包含可用于测试的示例PL/Java函数。运行这个文件中的命令来创建测试函数(它们使用了Java中的类)
示例的.jar)。
$ psql -f $GPHOME/share/postgresql/pljava/examples.sql
Uninstalling PL/Java
• Remove PL/Java Support for a Database
• Uninstall the Java JAR files and SoftwarePackage
Remove PL/Java Support for a Database
对于不需要PL/Java语言的数据库,删除对PL/Java的支持。 以gpadmin用户身份运行uninstall.sql文件。 例如,该命令禁用指定数据库中的PL/Java语言。
$ psql -dmydatabase
-f$GPHOME/share/postgresql/pljava/uninstall.sql
Uninstall the Java JAR files and Software Package
如果没有数据库将PL/Java作为注册语言,则删除Java JAR文件并使用gppkg实用程序卸载GreenplumPL/Java扩展。
1. 从所有Greenplum数据库主机的postgresql.conf文件中删除pljava_classpath服务器配置参数。 例如:
$ gpconfig -r pljava_classpath
2. 从所有Greenplum数据库主机上安装的目录中删除JAR文件。 有关JAR文件安装目录的信息,请参阅启用PL/Java和安装JAR文件。.
3. 使用带有-r选项的Greenplumgppkg实用程序来卸载PL/Java扩展。 本示例卸载Linux系统上的PL/Java扩展:
$ gppkg -rpljava-ossv1.4.0_pv1.3_gpdb4.3orca
您可以使用选项-q --all运行gppkg实用程序以列出已安装的扩展及其版本。
4. 重新加载greenplum_path.sh。
$ source $GPHOME/greenplum_path.sh
5. 重新启动数据库。
$ gpstop -r
About Greenplum Database PL/Java
There are a few key differencesbetween the implementation of PL/Java in standard PostgreSQL and GreenplumDatabase.
Functions
The following functions are notsupported in Greenplum Database. The classpath is handled differently in adistributed Greenplum Database environment than in the PostgreSQL environment.
sqlj.install_jar
sqlj.install_jar
sqlj.replace_jar
sqlj.remove_jar
sqlj.get_classpath
sqlj.set_classpath
GreenplumDatabase uses the pijava_ciasspath server configuration parameter inplace of the sqlj.set_classpath function.
Server Configuration parameter
在Greenplum数据库中,PL/Java使用以下服务器配置参数。 这些参数替换标准PostgreSQLPL/Java实现中使用的pijava.*参数:
• pljava_classpath
以冒号(:)分隔的包含任何PL/Java函数中使用的Java类的jar文件列表。
jar文件必须安装在所有Greenplum数据库主机的相同位置。 使用受信任的PL/Java语言处理程序,jar文件路径必须相对于$GPHOME/lib/postgresql/java/目录。 使用不受信任的语言处理程序(javaU语言标记),路径可能相对于$GPHOME/lib/postgresql/java/或绝对路径。
•pljava_statement_cache_size
为已准备好的语句设置最近使用(MRU)缓存的大小(以KB为单位)。
•pljava_release_lingering_savepoints
如果为TRUE,则将在功能退出时释放延迟的保存点。 如果FALSE,它们将被回滚。
•pljava_vmoptions
定义Greenplum数据库Java VM的启动选项。
有关Greenplum数据库参数的信息,请参阅“Greenplum数据库参考指南”。
Writing PL/Java functions
Informationabout writing functions with PL/Java.
• SQL Declaration
• Type Mapping
• NULL Handling
• Complex Types
• Returning Complex Types
• Returning Complex Types
• Functions That Return Sets
• Returning a SETOF
• Returning a SETOF〈complex type>
SQL Declaration
一个Java函数声明一个类的名称和该类的静态方法。 该类将使用已经为声明该函数的模式定义的类路径来解析。 如果没有为该模式定义类路径,则使用公共模式。 如果在其中找不到类路径,则使用系统类加载器解析该类。
可以声明以下函数来访问java.iang.system类上的静态方法getProperty:
CREATEFUNCTION getsysprop(VARCHAR)
RETURNSVARCHAR
AS'java.lang.System.getProperty'
LANGUAGE java;
运行以下命令以返回Java user.home属性:
SELECT getsysprop('user.home');
Type Mapping
Scalar types are mapped in a straightforward way. This table lists the current mappings.
Table 142: PL/Java data type mapping
|
PostgreSQL |
Java |
date |
java.sql.Date |
time |
java.sql.Time (stored value treated as local time) |
timetz |
java.sql.Time |
timestamp |
java.sql.Timestamp (stored value treated as local time) |
timestampz |
java.sql.Timestamp |
complex |
java.sql.ResultSet |
setof complex |
java.sql.ResultSet |
|
All other types are mapped tojava.lang.String and will utilize the standard textin/textout routines registered for respectivetype.
NULL Handling
映射到Java基元的标量类型不能作为NULL值传递。 要传递NULL值,这些类型可以有一个替代映射。 您可以通过在方法引用中明确表示它来启用此映射。
CREATE FUNCTIONtruelfEvenOrNull(integer)
RETURNS bool
AS ,foo.fee.Fum.trueIfEvenorNull(java.lang.Integer)'
LANGUAGE java;
The Java code would be similar tothis:
package foo.fee; public class Fum
{
static booleantrueIfEvenOrNull(Integer value)
{
return (value == null)
? true
:(value.intValue() % 1) == 0;
}
}
The followingtwo statements both yield true:
SELECT trueIfEvenOrNull(NULL);
SELECT trueIfEvenorNull(4);
为了从Java方法返回NULL值,可以使用对应于该基元的对象类型(例如,返回java.lang.Integer而不是int)。 PL/Java解析机制无论如何都会找到方法。 由于Java不能为同名的方法使用不同的返回类型,所以不会引入任何歧义。
Complex Types
一个复合类型总是作为一个只读的java.sql.ResultSet传递一行。 ResultSet位于其行,所以不应该调用next()。 使用ResultSet的标准getter方法来检索复杂类型的值。
Example:
CREATE TYPEcomplexTest
AS(baseinteger, incbase integer, ctime timestamptz); CREATE FUNCTIONuseComplexTest(complexTest)
RETURNSVARCHAR
AS 'foo.fee.Fum.useComplexTest'
IMMUTABLE LANGUAGE java;
In the Java class Fum, we add thefollowing static method:
public staticString useComplexTest(ResultSet complexTest) throws SQLException
{
int base =complexTest.getInt(1); int incbase = complexTest.getInt(2);
Timestampctime = complexTest.getTimestamp(3); return "Base =+ base +
incbase =\"" + incbase +
"\",ctime = \"" + ctime + "\"";
}
Returning Complex Types
Java不规定任何方式来创建一个ResultSet。 因此,返回一个ResultSet不是一个选项。 SQL-2003草案建议复杂的返回值应作为IN/OUT参数处理。 PL/Java以这种方式实现了一个ResultSet。 如果声明一个返回复杂类型的函数,则需要使用带有布尔返回类型的Java方法,最后一个参数的类型为java.sql.ResultSet。 该参数将被初始化为一个空的可更新的ResultSet,它只包含一行。
假设上一节中的complexTest类型已经创建。
Assume that the complexTest type inprevious section has been created.
CREATEFUNCTION createComplexTest(int, int)
RETURNScomplexTest AS 'foo.fee.Fum.createComplexTest'
IMMUTABLE LANGUAGE java;
The PL/Java method resolve will nowfind the following method in the Fum class:
public staticboolean complexReturn(int base, int increment,
ResultSetreceiver) throws SQLException {
receiver.updateInt(1,base); receiver.updateInt(2, base + increment); receiver.updateTimestamp(3, new
Timestamp(System.currentTimeMillis()));return true;
}
The return value denotes if thereceiver should be considered as a valid tuple (true) or NULL (false).
Functions That Return Sets
返回结果集时,不要在返回结果集之前创建结果集,因为构建较大的结果集会消耗大量资源。 最好一次只产生一行。 顺便说一下,这就是Greenplum数据库后端希望用SETOF返回的功能。 您可以返回SETOF标量类型,如int,float或varchar,或者可以返回SETOF复杂类型。
Returning a SETOF
In order toreturn a set of a scalar type, you need create a Java method that returnssomething that implements the java.utii.iterator interface. Here is an exampleof a method that returns a SETOF
varchar:
CREATEFUNCTION javatest.getSystemProperties() RETURNS SETOF varchar AS'foo.fee.Bar.getNames'
IMMUTABLE LANGUAGE java;
This simple Java method returns aniterator:
packagefoo.fee;
import java.util.Iterator;
public classBar
{
public staticIterator getNames()
{
ArrayListnames = new ArrayList();
names.add("Lisa");
names.add("Bob");
names.add("Bill");
names.add("Sally");
returnnames.iterator();
}
}
Returning a SETOF
返回SETOF <复杂类型>的方法必须使用接口
org.postgresql.pljava.ResultSetProvider或org.postgresql.pljava.ResultSetHandle。该有两个接口的原因是它们迎合两个不同用例的最佳处理。 前者适用于您想要动态创建要从SETOF函数返回的每一行的情况。 后者是在你想要返回执行查询的结果的情况下。
Using theResultSetProvider Interface
这个接口有两个方法。 boolean assignRowValues(java.sql.ResultSet tupleBuilder,int rowNumber)和void close()方法。 Greenplum数据库查询评估器将重复调用assignRowValues,直到它返回false或直到评估者决定不需要更多行。 然后它关闭。
你可以用下面的方法使用这个接口:
CREATEFUNCTION javatest.listComplexTests(int, int)
RETURNS SETOFcomplexTest AS 'foo.fee.Fum.listComplexTest'
IMMUTABLE LANGUAGE java;
The functionmaps to a static java method that returns an instance that implements the
ResultSetProvider interface.
public classFum implements ResultSetProvider {
private finalint m_base; private final int m_increment; public Fum(int base, int increment)
{
m base = base;
m_increment = increment;
} _
public booleanassignRowValues(ResultSet receiver, int currentRow)
throws SQLException
{
// Stop when we reach 12 rows.
//
if(currentRow >= 12) return false;
receiver.updateInt(1, m_base);
receiver.updateInt(2, m_base +m_increment * currentRow); receiver.updateTimestamp(3, newTimestamp(System.currentTimeMillis())); return true;
}
public void close()
{
// Nothing needed in this example
}
public static ResultSetProviderlistComplexTests(int base, int increment)
throws SQLException
{
return new Fum(base, increment);
}
}
listComplextTests方法被调用一次。 如果没有可用的结果或ResultSetProvider的实例,它可能会返回NULL。 这里的Java类Fum实现了这个接口,所以它返回一个它自己的实例。 assignRowValues方法将被重复调用,直到它返回false。 那时候,关闭会被调用
Using theResultSetHandle Interface
这个接口类似于ResultSetProvider接口,它有一个close()方法,最后会调用它。 但是,不是让评估者调用一次构建一行的方法,而是使用返回ResultSet的方法。 查询评估器将遍历这个集合,并一次将RestulSet内容(一次一个元组)传递给调用者,直到对next()的调用返回false或者评估者决定不再需要行为止。
以下是使用默认连接获取的语句执行查询的示例。 适合部署描述符的SQL如下所示::
publicclass Users implements ResultSetHandle
{
privatefinal String m_filter;
privateStatement m_statement;
publicUsers(String filter)
{
m_filter= filter;
}
publicResultSet getResultSet()
throwsSQLException
{
m_statement=
DriverManager.getConnection("Jdbc:default:connection").cr
eateStatement();
return m_statement.executeQuery("SELECT * FROM pg_user
WHERE" + m_filter);
}
publicvoid close()
throwsSQLException
{
m_statement.close();
}
publicstatic ResultSetHandle listSupers()
{
returnnew Users("usesuper = true");
}
public static ResultSetHandle listNonSupers()
{
return new Users("usesuper = false");
}
Using JDBC
PL / Java包含一个映射到PostgreSQL SPI函数的JDBC驱动程序。 映射到当前事务的连接可以使用以下语句获得:
Connection conn =
DriverManager.getConnection(njdbc:default:connectionn);
获得连接后,可以准备和执行类似于其他JDBC连接的语句。 这些是对PL/Java JDBC驱动程序的限制:
•事务不能以任何方式进行管理。 因此,您不能使用连接上的方法,例如:
• commit()
• rollback()
• setAutoCommit()
• setTransactionIsolation()
•保存点有一些限制。 一个保存点不能超过它所设置的功能,并且必须通过该功能回退或释放。
•从executeQuery()返回的ResultSet始终为FETCH_FORWARD和CoNCUR_READ_ONLY。
元数据仅在PL/Java 1.1或更高版本中可用。
Exception Handling
您可以捕获并处理Greenplum数据库后端中的异常,就像其他异常一样。 后端ErrorData结构作为一个名为属性的类被公开
org.postgresql.pljava.ServerException(派生自java.sql.SQLException),Java try/catch机制与后端机制同步。
重要说明:除非您已经使用了保存点,否则直到您的函数返回并且在后端生成异常时传播了错误,您将无法继续执行后端函数。 当保存点回滚时,异常情况将被重置,您可以继续执行。
.
Savepoints
Greenplum数据库保存点使用java.sql.Connection接口公开。 两个限制适用。
•保存点必须在设置的函数中回滚或释放。
•保存点不得超过设置的功能
Logging
PL/Java使用标准的Java记录器。 因此,你可以写下如下的东西:
Logger.getAnonymousLogger().info("Timeis " + new Date(System.currentTimeMillis()));
目前,记录器使用一个处理程序,将Greenplum数据库配置设置log_min_messages的当前状态映射到有效的记录器级别,并使用Greenplum数据库后端函数elog()输出所有消息。
注意:首次执行会话中的PL/Java函数时,将从数据库中读取log_min_messages设置]。 在Java方面,在特定的会话中执行第一个PL/Java函数之后,设置不会改变,直到与PL/Java一起工作的Greenplum数据库会话重新启动。
记录器级别和Greenplum数据库后端级别之间应用以下映射。
Table 143:PL/Java Logging Levels
java.util.logging.Level |
Greenplum Database Level |
SEVERE ERROR |
ERROR |
WARNING |
WARNING |
CONFIG |
LOG |
INFO |
INFO |
FINE |
DEBUG1 |
FINER |
DEBUG2 |
FINEST |
DEBUG3 |
|
Security
• Installation
• Trusted Language
Installation
Only a database super user can installPL/Java. The PL/Java utility functions are installed using SECURITY DEFINER sothat they execute with the access permissions that where granted to the creatorof the functions.
Trusted Language
PL/Java is a trusted language. Thetrusted PL/Java language has no access to the file system as stipulated byPostgreSQL definition of a trusted language. Any database user can create andaccess functions in a trusted language.
PL/Java alsoinstalls a language handler for the language javau. This version is not trustedand only a superuser can create new functions that use it. Any user can callthe functions.
Some PL/Java Issues and Solutions
在编写PL/Java时,将JVM映射到与Greenplum数据库后端代码相同的进程空间,引发了一些关于多线程,异常处理和内存管理的担忧。 这里有简要的描述解释如何解决这些问题。
•多线程
• 异常处理
Java垃圾收集器与palloc()和堆栈分配
Multi-threading
Java本质上是多线程的。 Greenplum数据库后端不是。 没有什么能阻止开发人员在Java代码中使用多个Threads类。 呼叫到后端的终结器可能已经从后台垃圾收集线程派生。 可能使用的几个第三方Java包使用多个线程。 在同一过程中,这个模型如何与Greenplum数据库后端共存?
Solution
解决方案很简单。 PL/Java定义了一个叫做Backend.THREADLocK的特殊对象。 当PL/Java被初始化时,后端立即抓取这个对象监视器(即它将同步这个对象)。 当后端调用Java函数时,监视器被释放,然后在调用返回时立即重新获得。 所有从Java到后端代码的调用都在同一个锁上进行同步。 这确保了一次只有一个线程可以从Java调用后端,并且只有在后端正在等待Java函数调用的返回时。
Exception Handling
Java频繁使用try/catch/finally块。 Greenplum数据库有时会使用一个异常机制来调用longjmp来将控制转移到已知状态。 这样的跳转通常会有效地绕过JVM。
Solution
后端现在允许使用宏PG_TRY/PG_CATCH/PG_END_TRY捕获错误,并在catch块中使用ErrorData结构检查错误。 PL/Java实现了一个
java.sql.SQLException子类名为org.postgresql.pljava.ServerException。 ErrorData
可以从该例外中检索和检查。 一个catch处理程序被允许发出回退到一个保存点。 成功回滚后,执行可以继续。
Java Garbage Collector Versus palloc() and StackAllocation
原始类型总是按值传递。 这包括String类型(由于Java使用双字节字符,所以这是必须的)。 复杂类型通常包装在Java对象中,并通过引用传递。 例如,Java对象可以包含一个指向palloc或堆栈分配的内存的指针,并使用本机JNI调用来提取和操作数据。 一旦通话结束,这些数据将会变陈旧。 进一步访问这些数据的尝试最多会给出非常不可预知的结果,但更可能导致内存故障和崩溃。
Solution
PL/Java包含的代码可以确保当分配的MemoryContext或者堆栈超出范围时,陈旧的指针会被清除。 Java包装器对象可能存在,但任何尝试使用它们都将导致本机处理异常。.
Example
The followingsimple Java example creates a JAR file that contains a single method and runsthe method.
Note: Theexample requires Java SDK to compile the Java file.
The followingmethod returns a substring.
{
public staticString substring(String text, int beginIndex, int endIndex)
{
returntext.substring(beginIndex, endIndex);
}
}
Enter the java code in a text fileexample.class.
Contents of the file manifest.txt:
Manifest-Version:1.0 Main-Class: Example Specification-Title: "Example"
Specification-Version:"1.0"
Created-By: 1.6.0_35-b10-428-11M3811Build-Date: 01/20/2013 10:09 AM
Compile the java code:
javac *.java
Create a JAR archive namedanalytics.jar that contains the class file and the manifest file MANIFEST filein the JAR.
jar cfm analytics.jar manifest.txt*.class
Upload the jar file to the Greenplummaster host.
Run the gpscp utility to copy the jarfile to the Greenplum Java directory. Use the -f option to specify the filethat contains a list of the master and segment hosts.
gpscp -f gphosts_file analytics.jar=:/usr/local/greenplum-db/lib/postgresql/java/
Use the gpconfig utility to set theGreenplum pljava_classpath server configuration parameter. The parameter liststhe installed jar files.
gpconfig -c pljava_classpath -v\'analytics.jarV
Run the gpstop utility with the -uoption to reload the configuration files.
gpstop -u
From the psql command line, run thefollowing command to show the installed jar files.
show pljava_classpath
The following SQL commands create atable and define a Java function to test the method in the jar file:
create tabletemp (a varchar) distributed randomly;
insert intotemp values ('my string');
--Examplefunction
create orreplace function java_substring(varchar, int, int) returns varchar as'Example.substring' language java;
--Exampleexecution
select java_substring(a, 1, 5) fromtemp;
You can place the contents in a file,mysample.sql and run the command from a psql command line:
> \i mysample.sql
The output is similar to this:
java_substring
y st
(1 row)
References
The PL/Java Github wiki page -https://github.com/tada/pljava/wiki.
PL/Java 1.4.0 release - https://github.com/tada/pljava/tree/B1_4.