Java language scalar function
The following example shows a simple scalar function that sums a set of numbers. This example starts from the beginning to construct a simple AE. It uses the following file name: TestJavaInterface.java
Code
- Pull in the Java interface. This can typically be accomplished by using:
import org.netezza.ae.*;
- Write a main. Determine an API to use, in this case a function. Get an AE connection, either
local or remote. This example uses a local AE connection. There are two interfaces that can be used
to get the connection. One is by using
NzaeFactory
and the other is by usingNzaeApiGenerator
. This example uses the generator class as it is simpler and can be used for remote mode as well. The program should appear similar to:import org.netezza.ae.*; public class TestJavaInterface { public static final void main(String [] args) { try { mainImpl(args); } catch (Throwable t) { System.err.println(t.toString()); NzaeUtil.logException(t, "main"); } } public static final void mainImpl(String [] args) { NzaeApiGenerator helper = new NzaeApiGenerator(); final NzaeApi api = helper.getApi(NzaeApi.FUNCTION); helper.close(); } }
This retrieves a
NzaeApi
reference that contains a usableNzaeFunction
object. - When the function is available, add a stub for calling the function
logic:
import org.netezza.ae.*; public class TestJavaInterface { public static final void main(String [] args) { try { mainImpl(args); } catch (Throwable t) { System.err.println(t.toString()); NzaeUtil.logException(t, "main"); } } public static final void mainImpl(String [] args) { NzaeApiGenerator helper = new NzaeApiGenerator(); final NzaeApi api = helper.getApi(NzaeApi.FUNCTION); run(api.aeFunction); helper.close(); } public static int run(Nzae aeFunc) { return 0; } }
- Implement the function. There are two ways of implementing a function. One is to use the
Nzae
object interface directly. The other is to implement anNzaeMessageHandler
-derived class that provides a simpler interface. In this example, the message handler is used. The second method is covered in a separate example. The message handler can be used only for functions that return one row of output per input. Also, the handler covers some error handling details automatically.import org.netezza.ae.*; public class TestJavaInterface { public static final void main(String [] args) { try { mainImpl(args); } catch (Throwable t) { System.err.println(t.toString()); NzaeUtil.logException(t, "main"); } } public static final void mainImpl(String [] args) { NzaeApiGenerator helper = new NzaeApiGenerator(); final NzaeApi api = helper.getApi(NzaeApi.FUNCTION); run(api.aeFunction); helper.close(); } public static class MyHandler implements NzaeMessageHandler { public void evaluate(Nzae ae, NzaeRecord input, NzaeRecord output) { final NzaeMetadata meta = ae.getMetadata(); int op = 0; double result = 0; for (int i = 0; i < input.size(); i++) { if (input.getField(i) == null) { continue; } int dataType = meta.getInputNzType(i); if (i == 0) { if (!(dataType == NzaeDataTypes.NZUDSUDX_FIXED || dataType == NzaeDataTypes.NZUDSUDX_VARIABLE)) { ae.userError("first column must be a string"); } String opStr = input.getFieldAsString(0); if (opStr.equals("*")) { result = 1; op = OP_MULT; } else if (opStr.equals("+")) { result = 0; op = OP_ADD; } else { ae.userError("invalid operator = " + opStr); } continue; } switch (dataType) { case NzaeDataTypes.NZUDSUDX_INT32: switch (op) { case OP_ADD: result += input.getFieldAsNumber(i).doubleValue(); break; case OP_MULT: result *= input.getFieldAsNumber(i).doubleValue(); break; default: break; } break; default: break; } } // end of column for loop output.setField(0, result); } } private static final int OP_ADD = 1; private static final int OP_MULT = 2; public static int run(Nzae ae) { ae.run(new MyHandler()); return 0; } }
In this example, the run function instantiates the handler object, and then invokes the run method of the function class on the handler. The handler must implement an evaluate method, which takes a reference to the function as well as references to the input record and the output record. For normal functions, there is only one column in the output record. The evaluate can then retrieve fields from the input record and use that to set the output field. This code provides a basic, non-error-checking function that should take as the first argument either the string plus sign (+) or the string asterisk (*) and for subsequent arguments INT32 values. It then either adds or multiplies the INT32 values based on the specified argument value and returns the result as a double.
- The final code preparation step adds error checking and support for additional data
types.
import org.netezza.ae.*; public class TestJavaInterface { public static final void main(String [] args) { try { mainImpl(args); } catch (Throwable t) { System.err.println(t.toString()); NzaeUtil.logException(t, "main"); } } public static final void mainImpl(String [] args) { NzaeApiGenerator helper = new NzaeApiGenerator(); final NzaeApi api = helper.getApi(NzaeApi.FUNCTION); run(api.aeFunction); helper.close(); } public static class MyHandler implements NzaeMessageHandler { public void evaluate(Nzae ae, NzaeRecord input, NzaeRecord output) { final NzaeMetadata meta = ae.getMetadata(); int op = 0; double result = 0; if (meta.getOutputColumnCount() != 1 || meta.getOutputNzType(0) != NzaeDataTypes.NZUDSUDX_DOUBLE) { throw new NzaeException("expecting one output column of type "+ "double"); } if (meta.getInputColumnCount() < 1) { throw new NzaeException("expecting at least one input column"); } if (meta.getInputNzType(0) != NzaeDataTypes.NZUDSUDX_FIXED && meta.getInputNzType(0) != NzaeDataTypes.NZUDSUDX_VARIABLE) { throw new NzaeException("first input column expected to be "+ "a string type"); } for (int i = 0; i < input.size(); i++) { if (input.getField(i) == null) { continue; } int dataType = meta.getInputNzType(i); if (i == 0) { if (!(dataType == NzaeDataTypes.NZUDSUDX_FIXED || dataType == NzaeDataTypes.NZUDSUDX_VARIABLE)) { ae.userError("first column must be a string"); } String opStr = input.getFieldAsString(0); if (opStr.equals("*")) { result = 1; op = OP_MULT; } else if (opStr.equals("+")) { result = 0; op = OP_ADD; } else { ae.userError("invalid operator = " + opStr); } continue; } switch (dataType) { case NzaeDataTypes.NZUDSUDX_INT8: case NzaeDataTypes.NZUDSUDX_INT16: case NzaeDataTypes.NZUDSUDX_INT32: case NzaeDataTypes.NZUDSUDX_INT64: case NzaeDataTypes.NZUDSUDX_FLOAT: case NzaeDataTypes.NZUDSUDX_DOUBLE: case NzaeDataTypes.NZUDSUDX_NUMERIC32: case NzaeDataTypes.NZUDSUDX_NUMERIC64: case NzaeDataTypes.NZUDSUDX_NUMERIC128: switch (op) { case OP_ADD: result += input.getFieldAsNumber(i).doubleValue(); break; case OP_MULT: result *= input.getFieldAsNumber(i).doubleValue(); break; default: break; } break; default: break; } } // end of column for loop output.setField(0, result); } } private static final int OP_ADD = 1; private static final int OP_MULT = 2; public static int run(Nzae ae) { ae.run(new MyHandler()); return 0; } }
This code ensures the correct number and types of input arguments and the correct number and type of output arguments. It supports all of the integer types that are float, double and numeric. When the code is complete, it must be compiled and registered.
Compilation
$NZ_EXPORT_DIR/ae/utilities/bin/compile_ae --language java --template \
compile TestJavaInterface.java --version 3
The arguments specify that you are using the Java language, version 3 with the template compile and creating of the TestJavaInterface.class file from the source file TestJavaInterface.java.
Registration
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "applyop_java(varargs)" \
--return "double" --class AeUdf --language java --template udf \
--define "java_class=TestJavaInterface" --version 3
This registers the UDF as applyop_java
, taking varying arguments and returning a
double. The class is AeUdf
and the template is udf, which is true for all AE UDFs.
This is registered in database db and the java class to run is
TestJavaInterface
.
Running
SELECT applyop_java('+',1,2);
APPLYOP_JAVA
--------------
3
(1 row)
doOnce
and send an error by
using NzaeException
. The following example triggers an
error:SELECT applyop_java('-',1,2);
ERROR: invalid operator = -