See: Description
Class | Description |
---|---|
BuiltIns |
Helper class to simplify access to built-in function backing methods.
|
ErrorHandler |
An error handler for custom database functions implemented in Java.
|
Functions |
A collection of static methods which provide the backing functionality for Progress-like
built-in functions, which are executed within the database server as custom, Java-language
functions.
|
Operators |
A collection of static methods which provide the backing functionality for Progress-like
comparison and math operators, which are executed within the database server as custom,
Java-language functions.
|
Annotation Type | Description |
---|---|
HQLFunction |
Annotation for
Functions and Operators methods exposed as UDF HQL
functions invoked in SQL via some kind of PL. |
Author |
Eric Faulhaber |
Date |
March 13, 2007 |
Access Control |
CONFIDENTIAL |
sqlj
), used to support
its internal operations, and defines its own set of UDFs as an API
which we use to implement additional P2J-specific, Java UDFs./usr/share/pljava
(the remaining instructions assume
this directory is used). The distribution file used by P2J at the
time of writing is pljava-i686-pc-linux-gnu-pg8.1-1.3.0.tar.gz
,
and it is part of the P2J project (in the p2j/pl
directory).pljava.so
file into PostgreSQL's lib
subdirectory (e.g., /usr/local/pgsql/lib
if PostgreSQL is
installed in /usr/local/pgsql
)..bashrc
if
using bash) for the postgres
user, to ensure the
following
environment variables are set for the postmaster
process:CLASSPATH=
LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/i386:$JAVA_HOME/jre/lib/i386/server
postgresql.conf
file (customarily located in the CUSTOMIZED OPTIONS
section):custom_variable_classes = 'pljava'
pljava.classpath = '/usr/share/pljava/pljava.jar'
cd /usr/share/pljava
psql -U <super_user> -f install.sql
<database_name>
, where:super_user
is a user with rights to modify the
target databasedatabase_name
is the target databasecd /usr/share/pljava
psql -U <super_user> -f uninstall.sql
<database_name>
, where:super_user
is a user with rights to modify the
target databasedatabase_name
is the target databasecreate
function
statement. This registers our function with the
database server backend. For example:create function myFunc(int, text,
date)
returns boolean as
'java.lang.Boolean
com.goldencode.p2j.persist.pl.Functions.myFuncImpl(
java.lang.Integer,
java.lang.String,
java.sql.Date)'
language java immutable
myFunc
for the currently
connected database. This function accepts SQL integer
,
text
, and date
values as its parameters, and
returns a boolean
value to the backend. The myFunc
database function is mapped to a static method myFuncImpl
in
the com.goldencode.p2j.persist.pl.Functions
class.
The
method accepts parameters of type java.lang.Integer
, java.lang.String
,
and java.sql.Date
and returns a value of type java.lang.Boolean
.
PL/Java manages the type conversions from SQL types to J2SE wrappers on
input, and from J2SE wrapper to SQL type on return.=
)
symbol within the Java method signature of the create function
statement, between the return type and the function name. This is
a temporary workaround for the parser defect, which has been reported
to the PL/Java developer. This symbol should be removed when a
fixed version of PL/Java becomes available.]myFuncImpl()
static method therefore might be implemented as follows:package
com.goldencode.p2j.persist.pl;
import java.sql.*;
public final class Functions
{
public static final Boolean myFuncImpl(Integer i,
String s, Date d)
{
return SomeWorkerClass.myFuncWorker(new
integer(i),
new
character(s),
new
date(d)).toJavaType();
}
...
}
create function
statements directly to the backing worker methods in the P2J runtime,
this was not possible in most cases, for several reasons:create function
statement must use data types known to the database backend. The
database schema of a P2J application uses the "primary" SQL types
(e.g., integer
, text
, boolean
,
etc.) for its data. PL/Java currently offers no way to map these
primary SQL types to custom Java classes (such as the P2J data wrapper
types). This is necessary by PL/Java's design, because type
mappings to Java classes actually are registered with the PostgreSQL
backend, which relies heavily on the integrity and stability of its
core types for internal processing. While PL/Java provides
services to map some SQL types to Java classes, the primary SQL types
are reserved; they have a hard-coded mapping to the J2SE wrapper
types (e.g., java.lang.Integer
, java.lang.String
,
java.lang.Boolean
, etc.). As such, they CAN NOT
be re-mapped to other Java classes. These primary types receive
special consideration in PL/Java's internal implementation, such that
they do not compromise the integrity of the backend.SQL
Input Type |
UDF
Type (also SQL) |
J2SE
Wrapper Type |
P2J
Wrapper Type |
integer |
integer |
java.lang.Integer |
com.goldencode.p2j.util.integer |
integer |
double precision |
java.lang.Double |
com.goldencode.p2j.util.decimal |
text |
text |
java.lang.String |
com.goldencode.p2j.util.character |
date |
date |
java.sql.Date |
com.goldencode.p2j.util.date |
boolean |
boolean |
java.lang.Boolean |
com.goldencode.p2j.util.logical |
bytea |
bytea |
byte[] |
com.goldencode.p2j.util.raw |
date
parameter as the
string representation of that date. This implementation requires
that some additional care be taken when defining functions.
Namely, it is not advisable to create polymorphic UDFs which accept a date
parameter in one case and a text
parameter in another
case, because the text
version always will be resolved in
preference to the date
version. Clearly, this can
cause unintended consequences and errors. A workaround for this
problem is to refactor the polymorphic UDF into separately named
UDFs. An example in P2J is the polymorphic com.goldencode.p2j.persist.pl.Functions.toInt()
method. One version accepts a java.lang.String
parameter, another accepts a java.sql.Date
parameter. However, the UDFs are defined as toInt(text)
and julianDayInt(date)
, respectively, to avoid the
resolution conflict.p2jpl.jar
file, which is built automatically with ant
jar
(or any ant targets dependent upon P2J's build.xml
JAR target). This jar file contains the subset of the full P2J
codebase which is necessary to support the functionality required by
the Progress-specific operator and built-in function implementations
used within where clauses. This jar file is used only by the JVMs
launched by the PostgreSQL backend process; the P2J server does
not use this file. Consequently, it is important to remember that
despite the fact that the codebase is common, there is no communication
or state-sharing between the PL/Java-enabled functions and the active
P2J server, except insofar as the former are used to restrict the
contents of database query result sets requested by the latter.p2jpl.jar
from
the file system (and that P2J UDFs also cannot access the file
system; an untrusted mode also is available, but the trusted mode
suffices for our purposes). Instead, PL/Java serializes jar files
to a byte array and stores these within a database table in its sqlj
schema. At runtime, it uses a custom ClassLoader
implementation to deserialize these byte arrays back into classes and
load them into the JVM.p2jpl.jar
, and to set the classpath to allow
the JVM to find our classes. A special deployment descriptor (p2j/pl/p2jpl.ddr
)
is embedded within the jar file, which executes SQL statements to
create all of our UDFs on install or to drop all of our UDFs on
uninstall.select
sqlj.install_jar('file:///<project_path>/p2j/build/lib/p2jpl.jar',
'p2j', true)
, where:<project_path>
is the directory in which the
P2J project is installed'p2j'
, is used in future
statements as a handle for the serialized p2jpl.jar
true
, indicates that the p2jpl.jar
contains a deployment descriptor which should be executed upon
installationselect sqlj.set_classpath('public', 'p2j')
p2jpl.jar
, and
executes its deployment descriptor to drop all of our UDFs:select sqlj.remove_jar('p2j', true)