HAWQ-1252 - pljava doc cleanup, working examples (closes #83)

Project: http://git-wip-us.apache.org/repos/asf/incubator-hawq-docs/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-hawq-docs/commit/04bf221c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-hawq-docs/tree/04bf221c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-hawq-docs/diff/04bf221c

Branch: refs/heads/release/2.1.0.0-incubating
Commit: 04bf221c21d105941951f3fefe6c0cffd860d9a2
Parents: 2c02642
Author: Lisa Owen <lo...@pivotal.io>
Authored: Wed Jan 11 16:06:21 2017 -0800
Committer: David Yozie <yo...@apache.org>
Committed: Wed Jan 11 16:06:21 2017 -0800

----------------------------------------------------------------------
 markdown/plext/using_pljava.html.md.erb | 1146 +++++++++++++++++---------
 1 file changed, 771 insertions(+), 375 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-hawq-docs/blob/04bf221c/markdown/plext/using_pljava.html.md.erb
----------------------------------------------------------------------
diff --git a/markdown/plext/using_pljava.html.md.erb 
b/markdown/plext/using_pljava.html.md.erb
index 99b5767..e463093 100644
--- a/markdown/plext/using_pljava.html.md.erb
+++ b/markdown/plext/using_pljava.html.md.erb
@@ -2,128 +2,143 @@
 title: Using PL/Java
 ---
 
-This section contains an overview of the HAWQ PL/Java language. 
+This section provides an overview of the HAWQ PL/Java procedural language 
extension. 
 
 
 ## <a id="aboutpljava"></a>About PL/Java 
 
-With the HAWQ PL/Java extension, you can write Java methods using your 
favorite Java IDE and install the JAR files that implement the methods in your 
HAWQ cluster.
+With the PL/Java extension, you can write Java methods using your favorite 
Java IDE and invoke the methods from PostgreSQL user-defined functions (UDFs). 
 
-**Note**: If building HAWQ from source, you must specify PL/Java as a build 
option when compiling HAWQ. To use PL/Java in a HAWQ deployment, you must 
explicitly enable the PL/Java extension in all desired databases.  
+The HAWQ PL/Java package is based on the open source PL/Java 1.4.0 and 
provides the following features:
 
-The HAWQ PL/Java package is based on the open source PL/Java 1.4.0. HAWQ 
PL/Java provides the following features.
-
-- Ability to execute PL/Java functions with Java 1.6 or 1.7.
-- Standardized utilities (modeled after the SQL 2003 proposal) to install and 
maintain Java code in the database.
-- Standardized mappings of parameters and result. Complex types as well as 
sets are supported.
-- An embedded, high performance, JDBC driver utilizing the internal HAWQ 
Database SPI routines.
+- PL/Java function execution with Java 1.6 or 1.7.
+- Standardized mappings of Java and PostgreSQL parameters and results. Complex 
types as well as sets are supported.
+- Two HAWQ Java languages:
+       - `java` - Tusted PL/Java language
+       - `javau` - Untrusted PL/Java language (superusers only)
+- An embedded, high performance, JDBC driver utilizing the internal HAWQ 
Database Server Programming Interface (SPI).
 - Metadata support for the JDBC driver. Both `DatabaseMetaData` and 
`ResultSetMetaData` are included.
-- The ability to return a `ResultSet` from a query as an alternative to 
building a ResultSet row by row.
+- A standard way of passing parameters and return values. Complex types and 
sets are passed using the standard JDBC `ResultSet` class.
+- The ability to return a `ResultSet` from a query as an alternative to 
building a `ResultSet` row by row.
 - Full support for savepoints and exception handling.
-- The ability to use IN, INOUT, and OUT parameters.
-- Two separate HAWQ languages:
-       - pljava, TRUSTED PL/Java language
-       - pljavau, UNTRUSTED PL/Java language
 - Transaction and Savepoint listeners enabling code execution when a 
transaction or savepoint is committed or rolled back.
 - Integration with GNU GCJ on selected platforms.
 
-A function in SQL will appoint a static method in a Java class. In order for 
the function to execute, the appointed class must available on the class path 
specified by the HAWQ server configuration parameter `pljava_classpath`. The 
PL/Java extension adds a set of functions that helps to install and maintain 
the Java classes. Classes are stored in normal Java archives, JAR files. A JAR 
file can optionally contain a deployment descriptor that in turn contains SQL 
commands to be executed when the JAR is deployed or undeployed. The functions 
are modeled after the standards proposed for SQL 2003.
-
-PL/Java implements a standard way of passing parameters and return values. 
Complex types and sets are passed using the standard JDBC ResultSet class.
-
-A JDBC driver is included in PL/Java. This driver calls HAWQ internal SPI 
routines. The driver is essential since it is common for functions to make 
calls back to the database to fetch data. When PL/Java functions fetch data, 
they must use the same transactional boundaries that are used by the main 
function that entered PL/Java execution context.
+PL/Java is optimized for performance. The Java virtual machine executes within 
the same process as the backend, minimizing call overhead. PL/Java brings the 
power of Java to the database itself, enabling the database-intensive business 
logic to execute as close to the actual data as possible.
 
-PL/Java is optimized for performance. The Java virtual machine executes within 
the same process as the backend to minimize call overhead. PL/Java is designed 
with the objective to enable the power of Java to the database itself so that 
database intensive business logic can execute as close to the actual data as 
possible.
-
-The standard Java Native Interface (JNI) is used when bridging calls between 
the backend and the Java VM.
+Calls between the HAWQ backend and the Java VM are bridged using the standard 
Java Native Interface (JNI).
 
 
 ## <a id="abouthawqpljava"></a>About HAWQ PL/Java 
 
+PL/Java is embedded in your HAWQ product distribution or within your HAWQ 
build if you chose to enable it as a build option. To use PL/Java in a HAWQ 
deployment, you must explicitly enable the PL/Java extension in all desired 
databases.
+
 There are a few key differences between the implementation of PL/Java in 
standard PostgreSQL and HAWQ.
 
-### <a id="pljavafunctions"></a>Functions 
+### <a id="pljavafunctions"></a>Unsupported Functions
 
-The following functions are not supported in HAWQ. The classpath is handled 
differently in a distributed HAWQ environment than in the PostgreSQL 
environment.
+The following functions are not supported in HAWQ:
 
-- sqlj.install_jar
-- sqlj.install_jar
-- sqlj.replace_jar
-- sqlj.remove_jar
-- sqlj.get_classpath
-- sqlj.set_classpath
+- sqlj.install_jar()
+- sqlj.install_jar()
+- sqlj.replace_jar()
+- sqlj.remove_jar()
+- sqlj.get_classpath()
+- sqlj.set_classpath()
 
-HAWQ uses the `pljava_classpath` server configuration parameter in place of 
the `sqlj.set_classpath` function.
+The PL/Java classpath is handled differently in a distributed HAWQ environment 
than that of the PostgreSQL environment. HAWQ uses the `pljava_classpath` 
server configuration parameter in place of the `sqlj.set_classpath()` function.
 
 ### <a id="serverconfigparams"></a>Server Configuration Parameters 
 
-PL/Java uses server configuration parameters to configure classpath, Java VM, 
and other options. Refer to the [Server Configuration Parameter 
Reference](../reference/HAWQSiteConfig.html) for general information about HAWQ 
server configuration parameters.
+PL/Java uses server configuration parameters to configure the classpath, Java 
Virtual Machine (VM), and other options. Refer to the [Server Configuration 
Parameter Reference](../reference/HAWQSiteConfig.html) for general information 
about HAWQ server configuration parameters.
 
-The following server configuration parameters are used by PL/Java in HAWQ. 
These parameters replace the `pljava.*` parameters that are used in the 
standard PostgreSQL PL/Java implementation.
+The following server configuration parameters are used by HAWQ PL/Java. These 
parameters replace the `pljava.*` configuration parameters that are used in the 
standard PostgreSQL PL/Java implementation.
 
 #### pljava\_classpath
 
-A colon (:) separated list of the jar files containing the Java classes used 
in any PL/Java functions. The jar files must be installed in the same locations 
on all HAWQ hosts. With the trusted PL/Java language handler, jar file paths 
must be relative to the `$GPHOME/lib/postgresql/java/` directory. With the 
untrusted language handler (javaU language tag), paths may be relative to 
`$GPHOME/lib/postgresql/java/` or absolute.
+A PL/Java user-defined function will appoint a static method in a Java class. 
In order for the function to execute, the appointed class must be available on 
the class path specified by the 
[`pljava_classpath`](../reference/guc/parameter_definitions.html#pljava_classpath)
 HAWQ server configuration parameter.
 
-#### pljava\_statement\_cache\_size
-
-Sets the size in KB of the Most Recently Used (MRU) cache for prepared 
statements.
+`pljava_classpath` is a colon (:) separated list of the JAR files implementing 
the Java classes referenced in any PL/Java user-defined functions. The JAR 
files must be installed in the same locations on all HAWQ hosts. When using the 
trusted PL/Java language handler (`java` language tag), JAR file paths must be 
relative to the `$GPHOME/lib/postgresql/java/` directory. Relative or absolute 
JAR file paths may be used with the untrusted PL/Java language handler (`javau` 
language tag).
 
 #### pljava\_release\_lingering\_savepoints
 
-If TRUE, lingering savepoints will be released on function exit. If FALSE, 
they will be rolled back.
+[`pljava_release_lingering_savepoints`](../reference/guc/parameter_definitions.html#pljava_release_lingering_savepoints)
 identifies the action to perform when lingering savepoints exist after a 
PL/Java user-defined function exits. If TRUE, lingering savepoints are released 
on function exit. If FALSE, lingering savepoints are rolled back.
+
+#### pljava\_statement\_cache\_size
+
+[`pljava_statement_cache_size`](../reference/guc/parameter_definitions.html#pljava_statement_cache_size)
 sets the size (in KB) of the Most Recently Used (MRU) cache for prepared 
statements.
 
 #### pljava\_vmoptions
 
-Defines the start up options for the Java VM.
+[`pljava_vmoptions`](../reference/guc/parameter_definitions.html#pljava_vmoptions)
 defines the set of space-delimited start-up options for the Java VM.
 
 ### <a id="setting_serverconfigparams"></a>Setting PL/Java Configuration 
Parameters 
 
-You can set PL/Java server configuration parameters at the session level, or 
globally across your whole cluster. Your HAWQ cluster configuration must be 
reloaded after setting a server configuration value globally.
+You can set PL/Java server configuration parameters at the session level, or 
globally across your whole cluster.
 
 #### <a id="setsrvrcfg_global"></a>Cluster Level
 
 You will perform different procedures to set a PL/Java server configuration 
parameter for your whole HAWQ cluster depending upon whether you manage your 
cluster from the command line or use Ambari. If you use Ambari to manage your 
HAWQ cluster, you must ensure that you update server configuration parameters 
only via the Ambari Web UI. If you manage your HAWQ cluster from the command 
line, you will use the `hawq config` command line utility to set PL/Java server 
configuration parameters.
 
+You must reload your HAWQ cluster configuration after you set a server 
configuration value globally.
+
 The following examples add a JAR file named `myclasses.jar` to the 
`pljava_classpath` server configuration parameter for the entire HAWQ cluster.
 
 If you use Ambari to manage your HAWQ cluster:
 
-1. Set the `pljava_classpath` configuration property to include 
`myclasses.jar` via the HAWQ service **Configs > Advanced > Custom hawq-site** 
drop down. 
+1. Set the `pljava_classpath` configuration property **Key** **Value** to 
include `myclasses.jar` via the HAWQ service **Configs > Advanced > Custom 
hawq-site** drop down. 
 2. Select **Service Actions > Restart All** to load the updated configuration.
 
 If you manage your HAWQ cluster from the command line:
 
-1.  Log in to the HAWQ master host as a HAWQ administrator and source the file 
`/usr/local/hawq/greenplum_path.sh`.
+1.  Log in to the HAWQ master node as a HAWQ administrator and set up your 
HAWQ environment.
 
     ``` shell
-    $ source /usr/local/hawq/greenplum_path.sh
+    $ ssh gpadmin@master
+    gpadmin@master$ source /usr/local/hawq/greenplum_path.sh
     ```
 
 1. Use the `hawq config` utility to set `pljava_classpath`:
 
     ``` shell
-    $ hawq config -c pljava_classpath -v \'myclasses.jar\'
+    gpadmin@master$ hawq config -c pljava_classpath -v 'myclasses.jar'
     ```
+    
+    The `-c` option identifies the name of the server configuration parameter, 
`-v` identifies the new value.
+    
 2. Reload the HAWQ configuration:
 
     ``` shell
-    $ hawq stop cluster -u
+    gpadmin@master$ hawq stop cluster -u
     ```
 
 #### <a id="setsrvrcfg_session"></a>Session Level 
 
-To set a PL/Java server configuration parameter for only the *current* 
database session, set the parameter within the `psql` subsystem. For example, 
to set `pljava_classpath`:
+To set a PL/Java server configuration parameter only for the *current* 
database session, set the parameter within the `psql` subsystem. For example:
        
 ``` sql
 => SET pljava_classpath='myclasses.jar';
 ```
 
+## <a id="security"></a>Security 
 
-## <a id="enablepljava"></a>Enabling and Removing PL/Java Support 
+This section describes specific security considerations when using PL/Java.
+
+### <a id="installation"></a>Enable/Disable 
+
+Only a database superuser can enable and disable PL/Java. You install the 
PL/Java utility functions with `SECURITY DEFINER` privileges; they execute with 
the access permissions granted to the creator of the functions.
+
+### <a id="trustedlang"></a>Trusted/Untrusted Language 
 
-The PL/Java extension must be explicitly enabled on each database in which it 
will be used.
+PL/Java includes trusted and untrusted language variants.
 
+The PL/Java trusted language is named `java`. The trusted PL/Java language 
cannot access the file system as stipulated by the PostgreSQL definition of a 
trusted language. Any database user can create and execute functions in the 
trusted `java` language.
+
+The PL/Java untrusted language is named `javau`. You cannot restrict operation 
of functions you create with the `javau` untrusted language. Only database 
superusers have privileges to create untrusted PL/Java UDFs. Only database 
superusers and other database users explicitly `GRANT`ed the permissions can 
execute untrusted PL/Java UDFs.
+
+## <a id="enablepljava"></a>Enabling and Removing PL/Java Support 
+
+The PL/Java extension must be explicitly enabled on each database in which it 
will be used. To remove PL/Java support, the extension must be explicitly 
removed from each database in which it was used.
 
 ### <a id="pljavaprereq"></a>Prerequisites 
 
@@ -131,76 +146,79 @@ Before you enable PL/Java:
 
 1. Ensure that you have installed a supported Java runtime environment and 
that the `$JAVA_HOME` variable is set to the same path on the master and all 
segment nodes.
 
-2. Perform the following step on all machines to set up `ldconfig` for the 
installed JDK:
+2. Perform the following step on all nodes in your HAWQ cluster to set up the 
shared library cache for the installed JDK:
 
        ``` shell
-       $ echo "$JAVA_HOME/jre/lib/amd64/server" > /etc/ld.so.conf.d/libjdk.conf
-       $ ldconfig
+       root@hawq-node$ echo "$JAVA_HOME/jre/lib/amd64/server" > 
/etc/ld.so.conf.d/libjdk.conf
+       root@hawq-node$ ldconfig
        ```
 4. Make sure that your HAWQ cluster is running, you have sourced 
`greenplum_path.sh` and that your `$GPHOME` environment variable is set.
 
+    ``` shell
+       $ ssh gpadmin@master
+       gpadmin@master$ source /usr/local/hawq/greenplum_path.sh
+       gpadmin@master$ hawq state
+       ```
 
 ### <a id="enablepljava"></a>Enable PL/Java and Install JAR Files 
 
-To use PL/Java:
+To use PL/Java, you must:
 
 1. Enable the language for each database.
-1. Install user-created JAR files on all HAWQ hosts.
-1. Add the names of the JAR files to the HAWQ `pljava_classpath` server 
configuration parameter. This parameter value should identify a list of the 
installed JAR files.
+1. Install custom JAR files on all HAWQ hosts.
+1. Add the names of the JAR files to the HAWQ `pljava_classpath` server 
configuration parameter. This parameter value should identify a list of all 
installed JAR files.
 
 #### <a id="enablepljava"></a>Enable PL/Java and Install JAR Files 
 
 Perform the following steps as the `gpadmin` user:
 
-1. Enable PL/Java by running the `$GPHOME/share/postgresql/pljava/install.sql` 
SQL script in the databases that will use PL/Java. The `install.sql` script 
registers both the trusted and untrusted PL/Java languages. For example, the 
following command enables PL/Java on a database named `testdb`:
+1. Enable PL/Java by running the `$GPHOME/share/postgresql/pljava/install.sql` 
SQL script on the databases that will use PL/Java. The `install.sql` script 
registers both the trusted (`java`) and untrusted PL/Java (`javau`) languages. 
For example, the following command enables the PL/Java languages on a database 
named `testdb`:
 
        ``` shell
-       $ psql -d testdb -f $GPHOME/share/postgresql/pljava/install.sql
+       gpadmin@master$ psql -d testdb -f 
$GPHOME/share/postgresql/pljava/install.sql
        ```
        
        To enable the PL/Java extension in all new HAWQ databases, run the 
script on the `template1` database: 
 
     ``` shell
-    $ psql -d template1 -f $GPHOME/share/postgresql/pljava/install.sql
+    gpadmin@master$ psql -d template1 -f 
$GPHOME/share/postgresql/pljava/install.sql
     ```
 
-    Use this option *only* if you are certain you want to enable PL/Java in 
all new databases.
+    Use this option *only* if you are certain you want to enable both the 
trusted and untrusted versions of PL/Java in all newly-created databases. After 
you invoke this command, PL/Java will be enabled in each new database that a 
user creates.
        
-2. Copy your Java archives (JAR files) to `$GPHOME/lib/postgresql/java/` on 
all HAWQ hosts. This example uses the `hawq scp` utility to copy the 
`myclasses.jar` file located in the current directory:
+2. Copy all custom Java JAR files to `$GPHOME/lib/postgresql/java/` on all 
HAWQ nodes. This example uses the `hawq scp` utility to copy the 
`myclasses.jar` file  to the list of hosts identified in the `hawq_hosts` file:
 
        ``` shell
-       $ hawq scp -f hawq_hosts myclasses.jar =:$GPHOME/lib/postgresql/java/
+       gpadmin@master$ hawq scp -f ./hawq_hosts ./myclasses.jar 
=:/usr/local/hawq/lib/postgresql/java/
        ```
-       The `hawq_hosts` file contains a list of the HAWQ hosts.
 
-3. Add the JAR files to the `pljava_classpath` configuration parameter. Refer 
to [Setting PL/Java Configuration Parameters](#setting_serverconfigparams) for 
the specific procedure.
+3. Add all JAR files to the `pljava_classpath` configuration parameter. Refer 
to [Setting PL/Java Configuration Parameters](#setting_serverconfigparams) for 
the specific procedure.
 
-5. (Optional) Your HAWQ installation includes an `examples.sql` file.  This 
script contains sample PL/Java functions that you can use for testing. Run the 
commands in this file to create and run test functions that use the Java 
classes in `examples.jar`:
+5. (Optional) Your HAWQ installation includes an `examples.sql` file. This 
script registers sample PL/Java functions that you can use to test the PL/Java 
installation. Run the commands in this file to create and execute PL/Java 
user-defined functions that use the Java classes implemented in `examples.jar`. 
The database on which you run the script must have PL/Java enabled:
 
        ``` shell
-       $ psql -f $GPHOME/share/postgresql/pljava/examples.sql
+       gpadmin@master$ psql -d <dbname> -f 
$GPHOME/share/postgresql/pljava/examples.sql
        ```
 
 #### Configuring PL/Java VM Options
 
-PL/Java JVM options can be configured via the `pljava_vmoptions` server 
configuration parameter. For example, `pljava_vmoptions=-Xmx512M` sets the 
maximum heap size of the JVM. The default `-Xmx` value is `64M`.
+Configure PL/Java JVM options via the `pljava_vmoptions` server configuration 
parameter. For example, `pljava_vmoptions=-Xmx512M` sets the maximum heap size 
of the JVM. (The default `-Xmx` value is `64M`.)
 
 Refer to [Setting PL/Java Configuration 
Parameters](#setting_serverconfigparams) for the specific procedure to set 
PL/Java server configuration parameters.
-
        
 ### <a id="uninstallpljava"></a>Disable PL/Java 
 
 To disable PL/Java, you should:
 
 1. Remove PL/Java support from each database in which it was added.
-2. Uninstall the Java JAR files.
+2. Uninstall/remove the Java JAR files.
 
 #### <a id="uninstallpljavasupport"></a>Remove PL/Java Support from Databases 
 
-For a database that no longer requires the PL/Java language, remove support 
for PL/Java by running the `uninstall.sql` script as the `gpadmin` user. For 
example, the following command disables the PL/Java language in the specified 
database:
+Remove support for PL/Java by running the `uninstall.sql` script as the 
`gpadmin` user. For example, the following command disables the PL/Java 
languages in the specified database:
 
 ``` shell
-$ psql -d <dbname> -f $GPHOME/share/postgresql/pljava/uninstall.sql
+gpadmin@master$ psql -d <dbname> -f 
$GPHOME/share/postgresql/pljava/uninstall.sql
 ```
 
 Replace \<dbname\> with the name of the target database.
@@ -214,7 +232,7 @@ If you use Ambari to manage your cluster:
 
 1. Remove the `pljava_classpath` configuration property via the HAWQ service 
**Configs > Advanced > Custom hawq-site** drop down.
 
-2. Remove the JAR files from the `$GPHOME/lib/postgresql/java/` directory of 
each HAWQ host.
+2. Remove the JAR files from the `$GPHOME/lib/postgresql/java/` directory on 
each HAWQ host.
 
 3. Select **Service Actions > Restart All** to restart your HAWQ cluster.
 
@@ -224,21 +242,21 @@ If you manage your cluster from the command line:
 1.  Log in to the HAWQ master host as a HAWQ administrator and source the file 
`/usr/local/hawq/greenplum_path.sh`.
 
     ``` shell
-    $ source /usr/local/hawq/greenplum_path.sh
+    gpadmin@master$ source /usr/local/hawq/greenplum_path.sh
     ```
 
-1. Use the `hawq config` utility to remove `pljava_classpath`:
+1. Use the `hawq config` utility to remove the `pljava_classpath` setting:
 
     ``` shell
-    $ hawq config -r pljava_classpath
+    gpadmin@master$ hawq config -r pljava_classpath
     ```
     
-2. Remove the JAR files from the `$GPHOME/lib/postgresql/java/` directory of 
each HAWQ host.
+2. Remove the JAR files from the `$GPHOME/lib/postgresql/java/` directory on 
each HAWQ host.
 
-3. If you manage your cluster from the command line, run:
+3. Restart your HAWQ cluster:
 
     ``` shell
-    $ hawq restart cluster
+    gpadmin@master$ hawq restart cluster
     ```
 
 
@@ -255,33 +273,48 @@ This section provides information about writing functions 
with PL/Java.
 - [Returning a SETOF \<scalar type\>](#returnsetofscalar)
 - [Returning a SETOF \<complex type\>](#returnsetofcomplex)
 
+You must have a Java SDK installed to write Java methods.
 
 ### <a id="sqldeclaration"></a>SQL Declaration 
+PL/Java functions are defined using the standard SQL [CREATE 
FUNCTION](../reference/sql/CREATE-FUNCTION.html) syntax. A PL/Java user-defined 
function specifies `LANGUAGE java` (trusted) or `LANGUAGE javau` (untrusted). 
The function must also include `RETURNS` and `AS` clauses.
 
-A Java function is declared with the name of a class and a static method on 
that class. The class will be resolved using the classpath that has been 
defined for the schema where the function is declared. If no classpath has been 
defined for that schema, the public schema is used. If no classpath is found 
there either, the class is resolved using the system classloader.
+The `RETURNS` clause identifies the type of the function's return value. 
 
-The following function can be declared to access the static method getProperty 
on `java.lang.System` class:
+Use the `AS` clause to identify the name of a Java class and the static method 
on that class that HAWQ invokes when it executes the function. Any arguments to 
the UDF are also passed to the Java method.
 
-```sql
-=> CREATE FUNCTION getsysprop(VARCHAR)
+The Java class is resolved using the classpath that has been defined for the 
schema in which the function is declared. If no classpath has been defined for 
that schema, the `public` schema is used. If no classpath is found in the 
`public` schema, an attempt is made to resolve the class using the Java system 
class loader.
+
+Example: Create a UDF wrapper around the `java.lang.System` class 
`getProperty()` method.  `getProperty()` takes a property name string as input 
and returns the string value of that property:
+
+``` sql
+=> CREATE FUNCTION getsysprop_udf(VARCHAR)
      RETURNS VARCHAR
      AS 'java.lang.System.getProperty'
    LANGUAGE java;
 ```
 
-Run the following command to return the Java `user.home` property:
+Execute the `getsysprop_udf()` PL/Java UDF, passing as input the property name 
 `user.home`:
 
 ```sql
-=> SELECT getsysprop('user.home');
+=> SELECT getsysprop_udf('user.home');
 ```
 
+``` 
+ getsysprop_udf   
+----------------
+ /home/gpadmin
+(1 row)
+```
+
+`getsyprop_udf()` returns the value `/home/gpadmin`, the current user's 
(`gpadmin`) home directory.
+
 ### <a id="typemapping"></a>Type Mapping 
 
-Scalar types are mapped in a straightforward way. This table lists the current 
mappings.
+Scalar types are mapped between HAWQ and Java in a straightforward way. The 
current HAWQ to Java type mappings are listed in the table below.
 
-***Table 1: PL/Java data type mappings***
+**Table 1: PL/Java Data Type Mappings**
 
-| PostgreSQL | Java |
+| HAWQ | Java |
 |------------|------|
 | bool | boolean |
 | char | byte |
@@ -299,306 +332,629 @@ Scalar types are mapped in a straightforward way. This 
table lists the current m
 | complex |    java.sql.ResultSet |
 | setof complex        | java.sql.ResultSet |
 
-All other types are mapped to `java.lang.String` and will utilize the standard 
textin/textout routines registered for respective type.
+All other types are mapped to `java.lang.String` and will utilize the standard 
textin/textout routines registered for the respective type.
 
 ### <a id="nullhandling"></a>NULL Handling 
 
-The scalar types that map to Java primitives can not be passed as NULL values. 
To pass NULL values, those types can have an alternative mapping. You enable 
this mapping by explicitly denoting it in the method reference.
+The scalar types that map to Java primitives can not be passed as NULL values 
to Java methods. To pass NULL values, those types should be mapped to the Java 
object wrapper class that corresponds with the primitive, and must be 
explicitly denoted in the method reference. For example, the object wrapper 
class for the `integer` primitive type is `java.lang.Integer`.
 
-```sql
-=> CREATE FUNCTION trueIfEvenOrNull(integer)
-     RETURNS bool
-     AS 'foo.fee.Fum.trueIfEvenOrNull(java.lang.Integer)'
-   LANGUAGE java;
-```
+Example: Handling Null Inputs
 
-The Java code would be similar to this:
-
-```java
-package foo.fee;
-public class Fum
-{
-  static boolean trueIfEvenOrNull(Integer value)
-  {
-    return (value == null)
-      ? true
-      : (value.intValue() % 1) == 0;
-  }
-}
-```
+1. Create a work area for the example:
 
-The following two statements both yield true:
+    ``` shell
+    $ mkdir pljava_work
+    $ cd pljava_work
+    $ export PLJAVAWORK=`pwd`
+    $ mkdir -p pljex/foo/fee
+    $ cd pljex/foo/fee
+    ```
 
-```sql
-=> SELECT trueIfEvenOrNull(NULL);
-=> SELECT trueIfEvenOrNull(4);
-```
+2. Create a new file named `Fum.java`, adding the following text to create a 
class named `Fum` with a single method named `trueIfEvenOrNull()`. This method 
takes an integer as input and returns true if the integer is even or NULL, 
false otherwise:
 
-In order to return NULL values from a Java method, you use the object type 
that corresponds to the primitive (for example, you return `java.lang.Integer` 
instead of `int`). The PL/Java resolve mechanism finds the method regardless. 
Since Java cannot have different return types for methods with the same name, 
this does not introduce any ambiguity.
+    ``` shell
+    $ vi Fum.java
+    ```
+
+    ``` java
+    package foo.fee;
+    public class Fum
+    {
+      static boolean trueIfEvenOrNull(Integer value)
+      {
+        return (value == null)
+          ? true
+          : (value.intValue() % 1) == 0;
+      }
+    }
+    ```
+
+3. Compile the `Fum` class and create a JAR file for this class named 
`pljex.jar`:
+
+    ``` shell
+    $ javac Fum.java
+    $ cd ../..
+    $ jar cf pljex.jar foo
+    ```
+
+4. Copy the JAR file to the default PL/Java classpath directory:
+
+    ``` shell
+    $ cp pljex.jar /usr/local/hawq/lib/postgresql/java/
+    ```
+ 
+5. Start the `psql` subsystem:
+
+    ``` shell
+    $ psql -d testdb
+    ```
+
+6. Add the JAR file to the session-level classpath:
+
+    ``` sql
+    => SET pljava_classpath='pljex.jar';
+    ```
+
+7. Create a trusted PL/Java UDF that invokes the `Fum` class 
`trueIfEvenOrNull()` method:
+
+    ``` sql
+    => CREATE FUNCTION isEvenOrNull(integer)
+         RETURNS bool
+         AS 'foo.fee.Fum.trueIfEvenOrNull(java.lang.Integer)'
+       LANGUAGE java;
+    ```
+    
+    Notice that the UDF input type is `integer`, while the 
`trueIfEvenOrNull()` Java method input is a `java.lang.Integer` object.
+
+8. Execute the UDF twice, once with a NULL and once with an even input:
+
+    ``` sql
+    => SELECT isEvenOrNull(NULL);
+    => SELECT isEvenOrNull(4);
+    ```
+    
+    Both statements should return true.
 
 ### <a id="complextypes"></a>Complex Types 
 
-A complex type will always be passed as a read-only `java.sql.ResultSet` with 
exactly one row. The `ResultSet` is positioned on its row so a call to `next()` 
should not be made. The values of the complex type are retrieved using the 
standard getter methods of the `ResultSet`.
+PL/Java supports complex types. Use the `CREATE TYPE` SQL command to create 
the complex type. Use the `CREATE FUNCTION` SQL command to define a PL/Java UDF 
whose input argument is the new (complex) type.
 
-Example:
+A complex type is always passed to a Java method as a read-only 
`java.sql.ResultSet` with exactly one row. The values of the specific fields in 
the complex type are retrieved using the standard getter method associated with 
the data type of each field present in the `ResultSet`.
 
-```sql
-=> CREATE TYPE complexTest
-     AS(base integer, incbase integer, ctime timestamptz);
-=> CREATE FUNCTION useComplexTest(complexTest)
-     RETURNS VARCHAR
-     AS 'foo.fee.Fum.useComplexTest'
-   IMMUTABLE LANGUAGE java;
-```
+Example: Complex Input Types
 
-In the Java class `Fum`, we add the following static method:
-
-```java
-public static String useComplexTest(ResultSet complexTest)
-throws SQLException
-{
-  int base = complexTest.getInt(1);
-  int incbase = complexTest.getInt(2);
-  Timestamp ctime = complexTest.getTimestamp(3);
-  return "Base = \"" + base +
-    "\", incbase = \"" + incbase +
-    "\", ctime = \"" + ctime + "\"";
-}
-```
+1. Add the following definitions and static method to the Java `Fum` class you 
created in an earlier exercise. This method outputs the components of the 
complex type comprised of two integer fields and a timestamp field:
+
+    ``` shell
+    $ cd $PLJAVAWORK/pljex/foo/fee
+    $ vi Fum.java
+    ```
+
+    ``` java
+    import java.sql.ResultSet;
+    import java.sql.SQLException;
+    import java.sql.Timestamp;
+    ```
+    
+    ``` java
+    public static String useComplexTest(ResultSet complexTest)
+    throws SQLException
+    {
+      int base = complexTest.getInt(1);
+      int incbase = complexTest.getInt(2);
+      Timestamp ctime = complexTest.getTimestamp(3);
+      return "Base = \"" + base +
+        "\", incbase = \"" + incbase +
+        "\", ctime = \"" + ctime + "\"";
+    }
+    ```
+    
+    Add the `imports` under the `package` definition. And be sure to include 
the `useComplexTypes()` method within the `Fum` class `{}`s. 
+    
+    Notice that the `ResultSet` object is immediately referenced; a call to 
`next()` is not required.
+
+2. Compile the `Fum` class, create the JAR file, and copy the JAR file to the 
default PL/Java classpath directory:
+
+    ``` shell
+    $ javac Fum.java
+    $ cd ../..
+    $ jar cf pljex.jar foo
+    $ cp pljex.jar /usr/local/hawq/lib/postgresql/java/
+    ```
+ 
+5. Start the `psql` subsystem:
+
+    ``` shell
+    $ psql -d testdb
+    ```
+
+6. Add the JAR file to the session-level classpath:
+
+    ``` sql
+    => SET pljava_classpath='pljex.jar';
+    ```
+
+7. Create a custom complex type with 2 integers and a single timestamp 
component:
+
+    ``` sql
+    => CREATE TYPE complexTestType
+         AS(base integer, incbase integer, ctime timestamptz);
+    ```
+
+7. Create a PL/Java UDF that invokes the `Fum` class `useComplexTest()` 
method, specifying a `complexTestType` as input:
+
+    ```sql
+    => CREATE FUNCTION useComplexTest(complexTestType)
+         RETURNS VARCHAR
+         AS 'foo.fee.Fum.useComplexTest'
+       IMMUTABLE LANGUAGE java;
+    ```
+
+8. Execute the `useComplexTest()` UDF, providing 2 integers and a timestamp as 
input:
+
+    ```sql
+    => SELECT useComplexTest( '(1,2,20170101010203)' );
+    ```
+
+    ```
+                           usecomplextest                       
+    ------------------------------------------------------------
+     Base = "1", incbase = "2", ctime = "2017-01-01 01:02:03.0"
+    (1 row)
+    ```
+    
+    Running the UDF displays the fields and values comprising the complex type.
 
 ### <a id="returningcomplextypes"></a>Returning Complex Types 
 
-Java does not stipulate any way to create a `ResultSet`. Hence, returning a 
ResultSet is not an option. The SQL-2003 draft suggests that a complex return 
value should be handled as an IN/OUT parameter. PL/Java implements a 
`ResultSet` that way. If you declare a function that returns a complex type, 
you will need to use a Java method with boolean return type with a last 
parameter of type `java.sql.ResultSet`. The parameter will be initialized to an 
empty updateable ResultSet that contains exactly one row.
+As Java does not define any way to create a `ResultSet`, returning a 
`ResultSet` from a Java method is not an option. The SQL-2003 draft suggests 
that a complex return value should be handled as an input/output argument. 
Conveniently, PL/Java implements a `ResultSet` that way. To declare a function 
that returns a complex type, you must implement a Java method with a `boolean` 
return type and a last input argument of type `java.sql.ResultSet`. This 
input/output argument will be initialized to an empty updateable `ResultSet` 
that contains exactly one row.
 
-Assume that the complexTest type in previous section has been created.
+Example: Complex Return Types
 
-```sql
-=> CREATE FUNCTION createComplexTest(int, int)
-     RETURNS complexTest
-     AS 'foo.fee.Fum.createComplexTest'
-   IMMUTABLE LANGUAGE java;
-```
+1. Create the `complexTestType` type definition if you did not yet create it:
 
-The PL/Java method resolve will now find the following method in the `Fum` 
class:
-
-```java
-public static boolean complexReturn(int base, int increment, 
-  ResultSet receiver)
-throws SQLException
-{
-  receiver.updateInt(1, base);
-  receiver.updateInt(2, base + increment);
-  receiver.updateTimestamp(3, new 
-    Timestamp(System.currentTimeMillis()));
-  return true;
-}
-```
+    ``` sql
+    => CREATE TYPE complexTestType
+         AS(base integer, incbase integer, ctime timestamptz);
+    ```
+
+2. Add the following static method to the Java class `Fum` you created in an 
earlier exercise. This method takes two integers as input, returning a complex 
type consisting of the first input integer, first input integer added to the 
second input integer, and a current timestamp:
+
+    ``` shell
+    $ cd $PLJAVAWORK/pljex/foo/fee
+    $ vi Fum.java
+    ```
+
+    ``` java
+    public static boolean complexReturn(int base, int increment, 
+      ResultSet receiver)
+    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 the `receiver` should be considered as a valid 
tuple (true) or NULL (false).
 
-The return value denotes if the receiver should be considered as a valid tuple 
(true) or NULL (false).
+3. Compile the `Fum` class, create the JAR file, and copy the JAR file to the 
default PL/Java classpath directory:
 
-### <a id="functionreturnsets"></a>Functions that Return Sets 
+    ``` shell
+    $ javac Fum.java
+    $ cd ../..
+    $ jar cf pljex.jar foo
+    $ cp pljex.jar /usr/local/hawq/lib/postgresql/java/
+    ```
+ 
+5. Start the `psql` subsystem:
 
-When returning result set, you should not build a result set before returning 
it, because building a large result set would consume a large amount of 
resources. It is better to produce one row at a time. Incidentally, that is 
what the HAWQ backend expects a function with SETOF return to do. You can 
return a SETOF a scalar type such as an int, float or varchar, or you can 
return a SETOF a complex type.
+    ``` shell
+    $ psql -d testdb
+    ```
 
-### <a id="returnsetofscalar"></a>Returning a SETOF \<scalar type\> 
+6. Add the JAR file to the session-level classpath:
 
-In order to return a set of a scalar type, you need create a Java method that 
returns something that implements the `java.util.Iterator` interface. Here is 
an example of a method that returns a SETOF varchar:
+    ``` sql
+    => SET pljava_classpath='pljex.jar';
+    ```
 
-```sql
-=> CREATE FUNCTION javatest.getSystemProperties()
-     RETURNS SETOF varchar
-     AS 'foo.fee.Bar.getNames'
-   IMMUTABLE LANGUAGE java;
-```
+7. Create a PL/Java UDF that invokes the `Fum` class `createComplexType()` 
method, taking two integers as input:
 
-This simple Java method returns an iterator:
+    ```sql
+    => CREATE FUNCTION createComplexTest(int, int)
+         RETURNS complexTestType
+         AS 'foo.fee.Fum.complexReturn'
+       IMMUTABLE LANGUAGE java;
+    ```
+
+8. Execute the `createComplexTest()` UDF, providing 2 integers  as input:
+
+    ```sql
+    => SELECT createComplexTest(11,22);
+    ```
+
+    ```
+              createcomplextest           
+    --------------------------------------
+     (11,33,"2016-12-31 23:04:09.388-08")
+    (1 row)
+    ```
+    
+    As described, the UDF, when executed, returns the first input integer, the 
sum of the first and second input integers, and the current timestamp.
+
+### <a id="functionreturnsets"></a>Functions that Return Sets
+
+PL/Java supports user-defined functions that return sets of both scalar and 
complex types. 
+
+The HAWQ backend expects a function that `RETURNS` a `SETOF` to return one row 
at a time.
+
+### <a id="returnsetofscalar"></a>Returning a SETOF &lt;Scalar Type&gt; 
+
+To return a set of a scalar type, the Java method must return an object that 
implements the `java.util.Iterator` interface. 
+
+Example: Function that returns a SETOF varchar:
+
+1. Create a new file named `Bar.java`, adding the following text to create a 
class named `Bar` with a single method named `getNames()`. This method uses an 
`Iterator` to collect and return a list of string names:
+
+    ``` shell
+    $ cd $PLJAVAWORK/pljex/foo/fee
+    $ vi Bar.java
+    ```
 
-```java
-package foo.fee;
-import java.util.Iterator;
+    ``` java
+    package foo.fee;
+    import java.util.ArrayList;
+    import java.util.Iterator;
 
-public class Bar
-{
-    public static Iterator getNames()
+    public class Bar
     {
-        ArrayList names = new ArrayList();
-        names.add("Lisa");
-        names.add("Bob");
-        names.add("Bill");
-        names.add("Sally");
-        return names.iterator();
+        public static Iterator getNames()
+        {
+            ArrayList names = new ArrayList();
+            names.add("Lisa");
+            names.add("Bob");
+            names.add("Bill");
+            names.add("Sally");
+            return names.iterator();
+        }
     }
-}
-```
+    ```
+
+
+3. Compile the `Bar` class (ignore warnings), create the JAR file, and copy 
the JAR file to the default PL/Java classpath directory:
+
+    ``` shell
+    $ javac *.java
+    $ cd ../..
+    $ jar cf pljex.jar foo
+    $ cp pljex.jar /usr/local/hawq/lib/postgresql/java/
+    ```
+ 
+5. Start the `psql` subsystem:
 
-### <a id="returnsetofcomplex"></a>Returning a SETOF \<complex type\> 
+    ``` shell
+    $ psql -d testdb
+    ```
 
-A method returning a SETOF <complex type> must use either the interface 
`org.postgresql.pljava.ResultSetProvider` or 
`org.postgresql.pljava.ResultSetHandle`. The reason for having two interfaces 
is that they cater for optimal handling of two distinct use cases. The former 
is for cases when you want to dynamically create each row that is to be 
returned from the SETOF function. The latter makes is in cases where you want 
to return the result of an executed query.
+6. Add the JAR file to the session-level classpath:
+
+    ``` sql
+    => SET pljava_classpath='pljex.jar';
+    ```
+
+7. Create a PL/Java UDF that invokes the `Bar` class `getNames()` method:
+
+    ``` sql
+    => CREATE FUNCTION getListOfNames()
+         RETURNS SETOF varchar
+         AS 'foo.fee.Bar.getNames'
+       IMMUTABLE LANGUAGE java;
+    ```
+
+8. Execute the UDF:
+
+    ``` sql
+    => SELECT getListOfNames();
+    ```
+    
+    ```
+     getlistofnames 
+    ----------------
+     Lisa
+     Bob
+     Bill
+     Sally
+    (4 rows)
+    ```
+    
+    The UDF returns a list of four string names.
+
+### <a id="returnsetofcomplex"></a>Returning a SETOF &lt;Complex Type&gt; 
+
+A method returning a set of \<complex type\> must implement either the 
`org.postgresql.pljava.ResultSetProvider` interface or the 
`org.postgresql.pljava.ResultSetHandle` interface. The interfaces provide 
optimal handling for distinct use cases. Use 
`org.postgresql.pljava.ResultSetProvider` when you want to dynamically create 
each row the function returns. Use `org.postgresql.pljava.ResultSetHandle` in 
cases where you want to return the result of an executed query.
 
 #### Using the ResultSetProvider Interface
 
-This interface has two methods. The boolean 
`assignRowValues(java.sql.ResultSet tupleBuilder, int rowNumber)` and the `void 
close()` method. The HAWQ query evaluator will call the `assignRowValues` 
repeatedly until it returns false or until the evaluator decides that it does 
not need any more rows. Then it calls close.
+The `ResultSetProvider` interface has two methods:
 
-You can use this interface the following way:
+- `boolean assignRowValues(java.sql.ResultSet tupleBuilder, int rowNumber)`
+- `void close()` 
 
-```sql
-=> CREATE FUNCTION javatest.listComplexTests(int, int)
-     RETURNS SETOF complexTest
-     AS 'foo.fee.Fum.listComplexTest'
-   IMMUTABLE LANGUAGE java;
-```
+The HAWQ query evaluator calls `assignRowValues()` repeatedly until it returns 
false or until the evaluator decides that it does not need any more rows. At 
that point, it will call `close()`.
+
+Example: Using the `ResultSetProvider` Interface
+
+1. Create a new file named `FumSetOfComplex.java`, adding the following text 
to create a class named `FumSetOfComplex` that implements the 
`ResultSetProvider` interface:
+
+    ``` shell
+    $ cd $PLJAVAWORK/pljex/foo/fee
+    $ vi FumSetOfComplex.java
+    ```
+
+    ``` java
+    package foo.fee;
+    import java.sql.ResultSet;
+    import java.sql.SQLException;
+    import java.sql.Timestamp;
+    import org.postgresql.pljava.ResultSetProvider;
 
-The function maps to a static java method that returns an instance that 
implements the `ResultSetProvider` interface.
-
-```java
-public class Fum implements ResultSetProvider
-{
-  private final int m_base;
-  private final int m_increment;
-  public Fum(int base, int increment)
-  {
-    m_base = base;
-    m_increment = increment;
-  }
-  public boolean assignRowValues(ResultSet receiver, int 
+    public class FumSetOfComplex implements ResultSetProvider
+    {
+      private final int m_base;
+      private final int m_increment;
+      public FumSetOfComplex(int base, int increment)
+      {
+        m_base = base;
+        m_increment = increment;
+      }
+      public boolean assignRowValues(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, new 
+      throws SQLException
+      {
+        if(currentRow >= 12)
+          return false;
+        receiver.updateInt(1, m_base);
+        receiver.updateInt(2, m_base + m_increment * currentRow);
+        receiver.updateTimestamp(3, new 
 Timestamp(System.currentTimeMillis()));
-    return true;
-  }
-  public void close()
-  {
-   // Nothing needed in this example
-  }
-  public static ResultSetProvider listComplexTests(int base, 
+        return true;
+      }
+      public void close()
+      {
+         /* Nothing needed in this example */
+      }
+      public static ResultSetProvider listComplex(int base, 
 int increment)
-  throws SQLException
-  {
-    return new Fum(base, increment);
-  }
-}
-```
+      throws SQLException
+      {
+        return new FumSetOfComplex(base, increment);
+      }
+    }
+    ```
+
+    The `listComplex()` method is called once. It will return NULL if no 
results are available. If results are available, `listComplex()` will return an 
instance of a `ResultSetProvider` interface. The Java class `FumSetOfComplex` 
implements this interface to return an instance of itself. `assignRowValues()` 
is called repeatedly until it returns false. At that time, `close()` is called.
+
+3. Compile the `FumSetOfComplex` class, create the JAR file, and copy the JAR 
file to the default PL/Java classpath directory:
+
+    ``` shell
+    $ javac -classpath /usr/local/hawq/lib/postgresql/pljava.jar 
FumSetOfComplex.java
+    $ cd ../..
+    $ jar cf pljex.jar foo
+    $ cp pljex.jar /usr/local/hawq/lib/postgresql/java/
+    ```
+ 
+5. Start the `psql` subsystem:
+
+    ``` shell
+    $ psql -d testdb
+    ```
+
+6. Add the JAR file to the session-level classpath:
+
+    ``` sql
+    => SET pljava_classpath='pljex.jar';
+    ```
+
+7. Create a PL/Java UDF that invokes the `FumSetOfComplex` class 
`listComplexTest()` method:
+
+    ```sql
+    => CREATE FUNCTION listComplexTest(int, int)
+         RETURNS SETOF complexTestType
+         AS 'foo.fee.FumSetOfComplex.listComplex'
+       IMMUTABLE LANGUAGE java;
+    ```
+
+8. Execute the UDF:
+
+    ``` sql
+    => SELECT listComplexTest(1,2);
+    ```
+    
+    ```
+    testdb=# SELECT listComplexTest(1,2);
+           listcomplextest           
+    -------------------------------------
+     (1,1,"2017-01-01 01:20:32.888-08")
+     (1,3,"2017-01-01 01:20:32.888-08")
+     (1,5,"2017-01-01 01:20:32.888-08")
+     (1,7,"2017-01-01 01:20:32.888-08")
+     (1,9,"2017-01-01 01:20:32.888-08")
+    ...
+    (12 rows)
+    ```
 
-The `listComplextTests` method is called once. It may return NULL if no 
results are available or an instance of the `ResultSetProvider`. Here the Java 
class `Fum` implements this interface so it returns an instance of itself. The 
method `assignRowValues` will then be called repeatedly until it returns false. 
At that time, close will be called.
 
 #### Using the ResultSetHandle Interface
 
-This interface is similar to the `ResultSetProvider` interface in that it has 
a `close()` method that will be called at the end. But instead of having the 
evaluator call a method that builds one row at a time, this method has a method 
that returns a `ResultSet`. The query evaluator will iterate over this set and 
deliver the `ResultSet` contents, one tuple at a time, to the caller until a 
call to `next()` returns false or the evaluator decides that no more rows are 
needed.
+Classes implementing the `ResultSetHandle` interface must include a method to 
return a `ResultSet` named `getResultSet()`. The query evaluator will iterate 
over this set and deliver the `ResultSet` contents, one tuple at a time, to the 
caller until a call to `next()` returns false or the evaluator decides that no 
more rows are needed.
 
-Here is an example that executes a query using a statement that it obtained 
using the default connection. The SQL suitable for the deployment descriptor 
looks like this:
+Example: Using the `ResultSetHandle` Interface
 
-```sql
-=> CREATE FUNCTION javatest.listSupers()
-     RETURNS SETOF pg_user
-     AS 'org.postgresql.pljava.example.Users.listSupers'
-   LANGUAGE java;
-=> CREATE FUNCTION javatest.listNonSupers()
-     RETURNS SETOF pg_user
-     AS 'org.postgresql.pljava.example.Users.listNonSupers'
-   LANGUAGE java;
-```
+1. A `Users` class is defined in the Java example package 
`org.postgresql.example` (refer to 
[`example`](https://github.com/apache/incubator-hawq/blob/master/src/pl/pljava/src/java/examples/org/postgresql/example)
 for example source files):
 
-And in the Java package `org.postgresql.pljava.example` a class `Users` is 
added:
-
-```java
-public class Users implements ResultSetHandle
-{
-  private final String m_filter;
-  private Statement m_statement;
-  public Users(String filter)
-  {
-    m_filter = filter;
-  }
-  public ResultSet getResultSet()
-  throws SQLException
-  {
-    m_statement = 
-      DriverManager.getConnection("jdbc:default:connection").cr
-eateStatement();
-    return m_statement.executeQuery("SELECT * FROM pg_user 
-       WHERE " + m_filter);
-  }
-
-  public void close()
-  throws SQLException
-  {
-    m_statement.close();
-  }
-
-  public static ResultSetHandle listSupers()
-  {
-    return new Users("usesuper = true");
-  }
-
-  public static ResultSetHandle listNonSupers()
-  {
-    return new Users("usesuper = false");
-  }
-}
-```
+    ``` java
+    package org.postgresql.example;
+
+    import java.sql.DriverManager;
+    import java.sql.ResultSet;
+    import java.sql.SQLException;
+    import java.sql.Statement;
+
+    import org.postgresql.pljava.ResultSetHandle;
+    
+    public class Users implements ResultSetHandle
+    {
+      private final String m_filter;
+      private Statement m_statement;
+      public Users(String filter)
+      {
+        m_filter = filter;
+      }
+      public ResultSet getResultSet()
+      throws SQLException
+      {
+        m_statement = 
+          
DriverManager.getConnection("jdbc:default:connection").createStatement();
+        return m_statement.executeQuery("SELECT * FROM pg_user 
+           WHERE " + m_filter);
+      }
+
+      public void close()
+      throws SQLException
+      {
+        m_statement.close();
+      }
+
+      public static ResultSetHandle listSupers()
+      {
+        return new Users("usesuper = true");
+      }
+
+      public static ResultSetHandle listNonSupers()
+      {
+        return new Users("usesuper = false");
+      }
+    }
+    ```
+    
+    The `listSupers()` and `listNonSupers()` methods each execute a query to 
return a `ResultSetHandle` containing a list of those users with and without 
superuser privileges, respectively. 
+
+2. Copy the `examples.jar` file to the default PL/Java classpath directory:
+
+    ``` shell
+    $ cp /usr/local/hawq/share/postgresql/pljava/examples.jar  
/usr/local/hawq/lib/postgresql/java/
+    ```
+    
+2. Start the `psql` subsystem:
+
+    ``` shell
+    $ psql -d testdb
+    ```
+
+6. Add the `examples.jar` JAR file to the session-level classpath:
+
+    ``` sql
+    => SET pljava_classpath='examples.jar';
+    ```
+
+7. Create PL/Java UDFs that execute the `listSupers()` and `listNonSupers()` 
methods in the `org.postgresql.example.Users` class:
+
+    ``` sql
+    => CREATE FUNCTION plistSupers()
+         RETURNS SETOF pg_user
+         AS 'org.postgresql.example.Users.listSupers'
+       LANGUAGE java;
+    => CREATE FUNCTION plistNonSupers()
+         RETURNS SETOF pg_user
+         AS 'org.postgresql.example.Users.listNonSupers'
+       LANGUAGE java;
+    ```
+
+8. Execute the UDFs:
+
+    ``` sql
+    => SELECT plistSupers();
+    ```
+    
+    ```
+              plistsupers           
+    -------------------------------
+     (gpadmin,10,t,t,t,********,,)
+    (1 row)
+    ```
+    
+    ``` sql
+    => SELECT plistNonSupers();
+    ```
+    
+    ```
+     plistnonsupers 
+    ---------------
+    (0 rows)
+    ```
+
+    
 ## <a id="usingjdbc"></a>Using JDBC 
 
-PL/Java contains a JDBC driver that maps to the PostgreSQL SPI functions. A 
connection that maps to the current transaction can be obtained using the 
following statement:
+PL/Java includes a JDBC driver. This driver invokes HAWQ internal SPI 
routines. The driver is essential; it is common for functions to make calls 
back to the database to fetch data. When PL/Java user-defined functions fetch 
data, they must use the same transactional boundaries that are used by the main 
function that entered the PL/Java execution context.
+
+You can obtain a PL/Java JDBC driver connection mapping to the current 
transaction with the following Java statement:
 
-```java
+``` java
 Connection conn = 
   DriverManager.getConnection("jdbc:default:connection"); 
 ```
 
-After obtaining a connection, you can prepare and execute statements similar 
to other JDBC connections. These are limitations for the PL/Java JDBC driver:
+After obtaining a connection, you can prepare and execute statements similar 
to other JDBC connections. Refer to the `Users` class Java source code from the 
example above. 
+
+Limitations of the PL/Java JDBC driver include the following:
 
-- The transaction cannot be managed in any way. Thus, you cannot use methods 
on the connection such as:
+- A transaction cannot be managed in any way. As such, you cannot use the 
following methods on the JDBC connection:
    - `commit()`
    - `rollback()`
    - `setAutoCommit()`
    - `setTransactionIsolation()`
-- Savepoints are available with some restrictions. A savepoint cannot outlive 
the function in which it was set and it must be rolled back or released by that 
same function.
-- A ResultSet returned from `executeQuery()` are always `FETCH_FORWARD` and 
`CONCUR_READ_ONLY`.
-- Meta-data is only available in PL/Java 1.1 or higher.
+- Savepoints are available, with some restrictions. A savepoint cannot outlive 
the function in which it was set, and it must be rolled back or released by 
that same function.
+- A `ResultSet` returned from `executeQuery()` is always `FETCH_FORWARD` and 
`CONCUR_READ_ONLY`.
 - `CallableStatement` (for stored procedures) is not implemented.
-- The types `Clob` or `Blob` are not completely implemented, they need more 
work. The types `byte[]` and `String` can be used for `bytea` and `text` 
respectively.
+- The types `Clob` and `Blob` are not completely implemented. Use the types 
`byte[]` and `String` for `bytea` and `text`, respectively.
 
 ## <a id="exceptionhandling"></a>Exception Handling 
 
-You can catch and handle an exception in the HAWQ backend just like any other 
exception. The backend `ErrorData` structure is exposed as a property in a 
class called `org.postgresql.pljava.ServerException` (derived from 
`java.sql.SQLException`) and the Java try/catch mechanism is synchronized with 
the backend mechanism.
+You can catch and handle an exception in the HAWQ backend just like any other 
exception. The backend `ErrorData` structure is exposed as a property in the 
`org.postgresql.pljava.internal.ServerException` class (derived from 
`java.sql.SQLException`), and the Java `try/catch` construct is synchronized 
with the backend mechanism.
 
-**Important:** You will not be able to continue executing backend functions 
until your function has returned and the error has been propagated when the 
backend has generated an exception unless you have used a savepoint. When a 
savepoint is rolled back, the exceptional condition is reset and you can 
continue your execution.
+**Important:** If the backend generates an exception and you have set a 
savepoint, the exception condition is reset when the savepoint is rolled back, 
allowing you to continue your execution. If the backend has generated an 
exception and you have *not* used a savepoint, then you cannot execute backend 
functions until your function propagates the error and returns.
 
 ## <a id="savepoints"></a>Savepoints 
 
-HAWQ savepoints are exposed using the `java.sql.Connection` interface. Two 
restrictions apply.
+HAWQ exposes savepoints using the `java.sql.Connection` interface. Two 
restrictions apply:
 
-- A savepoint must be rolled back or released in the function where it was set.
-- A savepoint must not outlive the function where it was set.
+- A savepoint must be rolled back or released in the function in which it was 
set.
+- A savepoint must not outlive the function from which it was set.
 
 ## <a id="logging"></a>Logging 
 
-PL/Java uses the standard Java Logger. Hence, you can write things like:
+PL/Java uses the standard Java Logger. For example:
 
-```java
+``` java
 Logger.getAnonymousLogger().info( "Time is " + new 
-Date(System.currentTimeMillis()));
+    Date(System.currentTimeMillis()));
 ```
 
-At present, the logger uses a handler that maps the current state of the HAWQ 
configuration setting `log_min_messages` to a valid Logger level and that 
outputs all messages using the HAWQ backend function `elog()`.
+The logger uses a handler that maps the current setting of the HAWQ 
`log_min_messages` server configuration parameter to a valid Java logging 
level, using the HAWQ backend function `elog()` to output all messages.
 
-**Note:** The `log_min_messages` setting is read from the database the first 
time a PL/Java function in a session is executed. On the Java side, the setting 
does not change after the first PL/Java function execution in a specific 
session until the HAWQ session that is working with PL/Java is restarted.
+**Note:** The `log_min_messages` setting is read from the database the first 
time a PL/Java function in a session is executed and can only be changed by 
restarting the HAWQ session.
 
-The following mapping apply between the Logger levels and the HAWQ backend 
levels.
+The table below identifies the Java to HAWQ log level mapping:
 
-***Table 2: PL/Java Logging Levels Mappings***
+**Table 2: PL/Java Logging Levels Mappings**
 
 | java.util.logging.Level | HAWQ Level |
 |-------------------------|------------|
-| SEVERE ERROR | ERROR |
+| SEVERE | ERROR |
 | WARNING |    WARNING |
 | CONFIG |     LOG |
 | INFO | INFO |
@@ -606,104 +962,144 @@ The following mapping apply between the Logger levels 
and the HAWQ backend level
 | FINER | DEBUG2 |
 | FINEST | DEBUG3 |
 
-## <a id="security"></a>Security 
-
-This section describes security aspects of using PL/Java.
 
-### <a id="installation"></a>Installation 
-
-Only a database super user can install PL/Java. The PL/Java utility functions 
are installed using SECURITY DEFINER so that they execute with the access 
permissions that where granted to the creator of the functions.
-
-### <a id="trustedlang"></a>Trusted Language 
+## <a id="pljavaexample"></a>Example 
 
-PL/Java is a trusted language. The trusted PL/Java language has no access to 
the file system as stipulated by PostgreSQL definition of a trusted language. 
Any database user can create and access functions in a trusted language.
+The following example:
 
-PL/Java also installs a language handler for the language `javau`. This 
version is not trusted and only a superuser can create new functions that use 
it. Any user can call the functions.
+1. Creates a Java class with a single method.
+2. Creates a PL/Java UDF that invokes the method.
+3. Executes the PL/Java UDF on a simple table.
 
+**Note**: This example requires a Java SDK be installed on your development 
system.
 
-## <a id="pljavaexample"></a>Example 
+1. Create a work area for the example:
 
-The following simple Java example creates a JAR file that contains a single 
method and runs the method.
+    ``` shell
+    $ mkdir -p $PLJAVAWORK/example/com/exco/app
+    $ cd $PLJAVAWORK/example/com/exco/app
+    ```
 
-<p class="note"><b>Note:</b> The example requires Java SDK to compile the Java 
file.</p>
+2. Create a new file named `SubStr.java`, adding the following text to create 
a class named `SubStr` with a single method named `substring()`. This method 
takes a text string and two index integers as input and returns the string 
bounded by the indexes:
 
-The following method returns a substring.
+    ``` shell
+    $ vi SubStr.java
+    ```
 
-```java
-{
-public static String substring(String text, int beginIndex,
-  int endIndex)
+    ``` java
+    package com.exco.app;
+    import java.util.logging.Logger;
+    
+    public class SubStr
     {
-    return text.substring(beginIndex, endIndex);
+      public static String substring(String text, int beginIndex, int endIndex)
+        {
+          return text.substring(beginIndex, endIndex);
+        }
     }
-}
-```
-
-Enter the Java code in a text file `example.class`.
+    ```
 
-Contents of the file `manifest.txt`:
+3. Compile the `SubStr` class, create a JAR file named `exsubstr.jar`, and 
copy this JAR file to the default PL/Java classpath directory:
 
-```plaintext
-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: 01../2013 10:09 AM
-```
-
-Compile the Java code:
+    ``` shell
+    $ javac *.java
+    $ cd ../../..
+    $ jar cf exsubstr.jar com
+    $ cp exsubstr.jar /usr/local/hawq/lib/postgresql/java/
+    ```
+ 
+5. Start the `psql` subsystem:
 
-```shell
-$ javac *.java
-```
+    ``` shell
+    $ psql -d testdb
+    ```
 
-Create a JAR archive named `analytics.jar` that contains the class file and 
the manifest file in the JAR:
+6. Add the JAR file to the session-level classpath:
 
-```shell
-$ jar cfm analytics.jar manifest.txt *.class
-```
+    ``` sql
+    => SET pljava_classpath='exsubstr.jar';
+    ```
 
-Upload the JAR file to the HAWQ master host.
+7. Create a HAWQ internal table named `extblwstrings`:
 
-Run the `hawq scp` utility to copy the jar file to the HAWQ Java directory. 
Use the `-f` option to specify the file that contains a list of the master and 
segment hosts:
+    ``` sql
+    => CREATE TABLE extblwstrings (a varchar) DISTRIBUTED randomly;
+    ```
 
-```shell
-$ hawq scp -f hawq_hosts analytics.jar =:/usr/local/hawq/lib/postgresql/java/
-```
+8. Insert some string data into the `extblwstrings` table:
 
-Add the `analytics.jar` JAR file to the `pljava_classpath` configuration 
parameter. Refer to [Setting PL/Java Configuration 
Parameters](#setting_serverconfigparams) for the specific procedure.
+    ``` sql
+    => INSERT INTO extblwstrings VALUES ('my string');
+    => INSERT INTO extblwstrings VALUES ('secondstring');
+    => INSERT INTO extblwstrings VALUES ('abcdefghij');
+    ```
 
-From the `psql` subsystem, run the following command to show the installed JAR 
files:
+9. Create a PL/Java UDF that invokes the `com.exco.app.SubStr` class 
`substring()` method defined earlier:
 
-``` sql
-=> SHOW pljava_classpath
-```
+    ``` sql
+    => CREATE OR REPLACE FUNCTION ex_getsubstring(varchar, int, int) 
+         RETURNS varchar AS 'com.exco.app.SubStr.substring' 
+       LANGUAGE java;
+    ```
 
-The following SQL commands create a table and define a Java function to test 
the method in the JAR file:
+8. Execute the UDF:
 
-```sql
-=> CREATE TABLE temp (a varchar) DISTRIBUTED randomly; 
-=> INSERT INTO temp values ('my string'); 
---Example function 
-=> CREATE OR REPLACE FUNCTION java_substring(varchar, int, int) 
-     RETURNS varchar AS 'Example.substring' 
-   LANGUAGE java; 
---Example execution 
-=> SELECT java_substring(a, 1, 5) FROM temp;
-```
+    ``` sql
+    => SELECT ex_getsubstring( a, 3, 7) FROM extblwstrings;
+    ```
+    
+    ```
+     ex_getsubstring 
+    -----------------
+     stri
+     onds
+     defg
+    (3 rows)
+    ```
 
-If you add these SQL commands to a file named `mysample.sql`, you can run the 
commands from the `psql` subsystem using the `\i` meta-command:
+10. If you add each of the above SQL commands to a file, you can run the 
commands from the command line or the `psql` subsystem. Add the following text 
to a file named `/tmp/myexample.sql`:
 
-``` sql
-=> \i mysample.sql 
-```
+    ``` shell
+    $ vi /tmp/myexample.sql
+    ```
+    
+    ```
+    DROP TABLE IF EXISTS extblwstrings;
+    SET pljava_classpath='exsubstr.jar';
+    CREATE TABLE extblwstrings (a varchar) DISTRIBUTED randomly;
+    INSERT INTO extblwstrings VALUES ('my string');
+    INSERT INTO extblwstrings VALUES ('secondstring');
+    INSERT INTO extblwstrings VALUES ('abcdefghij');
+    CREATE OR REPLACE FUNCTION ex_getsubstring(varchar, int, int) 
+      RETURNS varchar AS 'com.exco.app.SubStr.substring' 
+    LANGUAGE java;
+    SELECT ex_getsubstring( a, 3, 7) FROM extblwstrings;
+    ```
 
-The output is similar to this:
+11. Run the `myexample.sql` script file:
 
-```shell
-java_substring
-----------------
- y st
-(1 row)
-```
\ No newline at end of file
+    ``` shell
+    $ psql -d testdb -f /tmp/myexample.sql 
+    DROP TABLE
+    SET
+    CREATE TABLE
+    INSERT 0 1
+    INSERT 0 1
+    INSERT 0 1
+    CREATE FUNCTION
+     ex_getsubstring 
+    -----------------
+     stri
+     onds
+     defg
+    (3 rows)
+    ```
+    
+    ``` shell
+    $ psql -d testdb
+    ```
+    
+    ``` sql
+    => \i /tmp/myexample.sql 
+    ...
+    ```

Reply via email to