Java 示例:重载 UDx

以下示例代码演示了创建一个将两个或三个整数相加的用户定义标量函数 (UDSF)。Add2or3ints 类已准备好处理两个或三个参数。它将检查已传入的参数个数,并将两个或三个参数全部相加。processBlock() 方法将检查对它发出的调用是否具有少于 2 个参数或具有多于 3 个参数。理论上,这不应当发生,因为如果用户的函数调用与您为函数创建的某一个工厂类上的签名相匹配,则 Vertica 仅调用 UDSF。在实践中,如果您的(或其他人的)工厂类报告函数类接受了它实际上不接受的一组参数,最好执行此健全性检查。

// You need to specify the full package when creating functions based on
// the classes in your library.
package com.mycompany.multiparamexample;
// Import the entire Vertica SDK
import com.vertica.sdk.*;
// This ScalarFunction accepts two or three integer arguments. It tests
// the number of input columns to determine whether to read two or three
// arguments as input.
public class Add2or3ints extends ScalarFunction
{
    @Override
    public void processBlock(ServerInterface srvInterface,
                             BlockReader argReader,
                             BlockWriter resWriter)
                throws UdfException, DestroyInvocation
    {
        // See how many arguments were passed in
        int numCols = argReader.getNumCols();

        // Return an error if less than two or more than 3 aerguments
        // were given. This error only occurs if a Factory class that
        // accepts the wrong number of arguments instantiates this
        // class.
        if (numCols < 2 || numCols > 3) {
            throw new UdfException(0,
                "Must supply 2 or 3 integer arguments");
        }

        // Process all of the rows of input.
        do {
            // Get the first two integer arguments from the BlockReader
            long a = argReader.getLong(0);
            long b = argReader.getLong(1);

            // Assume no third argument.
            long c = 0;

            // Get third argument value if it exists
            if (numCols == 3) {
                c = argReader.getLong(2);
            }

            // Process the arguments and come up with a result. For this
            // example, just add the three arguments together.
            long result = a+b+c;

            // Write the integer output value.
            resWriter.setLong(result);

            // Advance the output BlocKWriter to the next row.
            resWriter.next();

            // Continue processing input rows until there are no more.
        } while (argReader.next());
    }
}

Add2ints 类和 Add2or3ints 类之间的主要区别在于是否包含一个通过调用 BlockReader.getNumCols() 来获取参数个数的节。此类还会测试它从 Vertica 收到的列数,以确保该列数处于它准备好处理的范围内。仅当所创建的 ScalarFunctionFactorygetPrototype() 方法定义了接受少于两个参数或接受多于三个参数的签名时,此测试才会失败。在此简单示例中,实在没有必要执行此测试,但对于更复杂的类,最好测试 Vertica 传递到函数类的列数和数据类型。

do 循环中,如果 Vertica 向 Add2or3ints 类发送两个输入列,则此类会使用默认值(零)。否则,此类会检索第三个值,并将该值与另外两个值相加。您自己的类需要对缺失的输入列使用默认值,或者需要将其处理更改为某种其他方法以处理可变列。

您必须在单独的源文件中定义函数类,而不能将函数类定义为工厂类之一的内部类,因为 Java 不允许从内部类的所属类的外部将内部类实例化。您的工厂类必须可由多个工厂类进行实例化。

创建了一个或多个函数类之后,应为您希望函数类处理的每个签名创建工厂类。这些工厂类可以调用不同的函数类,或者也可以全部调用已准备好接受多个参数集的同一个类。

以下示例的 createScalarFunction() 方法实例化 Add2or3ints 类的成员。

// You will need to specify the full package when creating functions based on
// the classes in your library.
package com.mycompany.multiparamexample;
// Import the entire Vertica SDK
import com.vertica.sdk.*;
public class Add2intsFactory extends ScalarFunctionFactory
{
    @Override
    public void getPrototype(ServerInterface srvInterface,
                             ColumnTypes argTypes,
                             ColumnTypes returnType)
    {
        // Accept two integers as input
        argTypes.addInt();
        argTypes.addInt();
        // writes one integer as output
        returnType.addInt();
    }
    @Override
    public ScalarFunction createScalarFunction(ServerInterface srvInterface)
    {
        // Instantiate the class that can handle either 2 or 3 integers.
        return new Add2or3ints();
    }
}

以下 ScalarFunctionFactory 子类接受三个整数作为输入。此外,它还会将 Add2or3ints 类的成员实例化以处理函数调用:

// You will need to specify the full package when creating functions based on
// the classes in your library.
package com.mycompany.multiparamexample;
// Import the entire Vertica SDK
import com.vertica.sdk.*;
public class Add3intsFactory extends ScalarFunctionFactory
{
    @Override
    public void getPrototype(ServerInterface srvInterface,
                             ColumnTypes argTypes,
                             ColumnTypes returnType)
    {
        // Accepts three integers as input
        argTypes.addInt();
        argTypes.addInt();
        argTypes.addInt();
        // Returns a single integer
        returnType.addInt();
    }
    @Override
    public ScalarFunction createScalarFunction(ServerInterface srvInterface)
    {
        // Instantiates the Add2or3ints ScalarFunction class, which is able to
        // handle eitehr 2 or 3 integers as arguments.
        return new Add2or3ints();
    }
}

必须将工厂类及其调用的一个或多个函数类打包到同一个 JAR 文件中(有关详细信息,请参阅编译并打包 Java 库)。如果数据库群集中的主机上安装了 JDK,则您可以使用以下命令来编译和打包该示例:

$ cd pathToJavaProject$ javac -classpath /opt/vertica/bin/VerticaSDK.jar \
> com/mycompany/multiparamexample/*.java
$ jar -cvf Add2or3intslib.jar com/vertica/sdk/BuildInfo.class \
> com/mycompany/multiparamexample/*.class
added manifest
adding: com/vertica/sdk/BuildInfo.class(in = 1202) (out= 689)(deflated 42%)
adding: com/mycompany/multiparamexample/Add2intsFactory.class(in = 677) (out= 366)(deflated 45%)
adding: com/mycompany/multiparamexample/Add2or3ints.class(in = 919) (out= 601)(deflated 34%)
adding: com/mycompany/multiparamexample/Add3intsFactory.class(in = 685) (out= 369)(deflated 46%)

打包了已重载的 UDx 之后,应以常规 UDx 的相同方法部署已重载的 UDx,唯一的例外是应多次(每个工厂类各一次)使用 CREATE FUNCTION 语句来定义函数。

=> CREATE LIBRARY add2or3intslib as '/home/dbadmin/Add2or3intslib.jar'
-> language 'Java';
CREATE LIBRARY
=> CREATE FUNCTION add2or3ints as LANGUAGE 'Java' NAME 'com.mycompany.multiparamexample.Add2intsFactory' LIBRARY add2or3intslib;
CREATE FUNCTION
=> CREATE FUNCTION add2or3ints as LANGUAGE 'Java' NAME 'com.mycompany.multiparamexample.Add3intsFactory' LIBRARY add2or3intslib;
CREATE FUNCTION

调用已重载的函数的方法与调用任何其他函数相同。

=> SELECT add2or3ints(2,3);
 add2or3ints
-------------
           5
(1 row)
=> SELECT add2or3ints(2,3,4);
 add2or3ints
-------------
           9
(1 row)
=> SELECT add2or3ints(2,3,4,5);
ERROR 3457:  Function add2or3ints(int, int, int, int) does not exist, or permission is denied for add2or3ints(int, int, int, int)
HINT:  No function matches the given name and argument types. You may need to add explicit type casts

最后一个错误由 Vertica 生成,而非 UDx 代码。如果无法找到签名与函数调用的签名匹配的工厂类,它会返回错误。

如果您希望函数接受有限的一组潜在参数,则创建已重载的 UDx 很有用。如果要创建更灵活的参数,您可以创建多态函数。