C++ Example: Polymorphic UDx

The following example shows an implementation of a ScalarFunction that adds together two or more integers.

#include "Vertica.h"
using namespace Vertica;
using namespace std;
// Adds two or more integers together.
class AddManyInts : public Vertica::ScalarFunction
{
public:
    virtual void processBlock(Vertica::ServerInterface &srvInterface,
                              Vertica::BlockReader &arg_reader,
                              Vertica::BlockWriter &res_writer)
    {
        // Always catch exceptions to prevent causing the side process or 
        // Vertica itself from crashing.
        try  
        {
            // Find the number of arguments sent.
            size_t numCols = arg_reader.getNumCols(); 
            
            // Make sure at least 2 arguments were supplied
            if (numCols < 2)
                vt_report_error(0, "Function expects at least 2 integer parameters");
            
            // Make sure all types are ints
            const SizedColumnTypes &inTypes = arg_reader.getTypeMetaData();
            for (int param=0; param < (int)numCols; param++) {
                const VerticaType &t = inTypes.getColumnType(param);
                if (!t.isInt()) 
                {
                    string typeDesc = t.getPrettyPrintStr();
                    // Report that the user supplied a non-integer value.
                    vt_report_error(0, "Function expects all arguments to be "
                                        "INTEGER. Argument %d was %s", param+1, 
                                        typeDesc.c_str());
                }
            }
            do 
            { // total up the arguments and write out the total.
                vint total = 0;
                int x;
                // Loop over all params, adding them up.
                for (x=0; x<(int)numCols; x++) {
                    total += arg_reader.getIntRef(x);
                }
                res_writer.setInt(total);
                res_writer.next();
            } while (arg_reader.next());
        }  catch(exception& e) {
           // Standard exception. Quit.
           vt_report_error(0, "Exception while processing partition: [%s]", 
			e.what());
        }
    }
};
// Defines the AddMany function. 
class AddManyIntsFactory : public Vertica::ScalarFunctionFactory
{
    // Return the function object to process the data.
    virtual Vertica::ScalarFunction *createScalarFunction(
		Vertica::ServerInterface &srvInterface)
    { return vt_createFuncObj(srvInterface.allocator, AddManyInts); }
    // Define the number and types of arguments that this function accepts
    virtual void getPrototype(Vertica::ServerInterface &srvInterface,
                              Vertica::ColumnTypes &argTypes,
                              Vertica::ColumnTypes &returnType)
    {   
        argTypes.addAny(); // Must be only argument type.
        returnType.addInt();
    }
};
RegisterFactory(AddManyIntsFactory);

Most of the work in the example is done by the ScalarFunction.processBlock() function. It performs two checks on the arguments that have been passed in through the BlockReader object:

Once the checks are performed, the example processes the block of data by looping over the arguments and adding them together.

You assign a SQL name to your polymorphic UDx using the same statement you use to assign one to a non-polymorphic UDx. The following demonstration shows how you load and call the polymorphic function from the example.

=> CREATE LIBRARY addManyIntsLib AS '/home/dbadmin/AddManyInts.so';
CREATE LIBRARY
=> CREATE FUNCTION addManyInts AS NAME 'AddManyIntsFactory' LIBRARY addManyIntsLib FENCED;
CREATE FUNCTION
=> SELECT addManyInts(1,2);
 addManyInts
-------------
           3
(1 row)
=> SELECT addManyInts(1,2,3,40,50,60,70,80,900);
 addManyInts
-------------
        1206
(1 row)
=> SELECT addManyInts(1); -- Too few parameters
ERROR 3412:  Failure in UDx RPC call InvokeProcessBlock(): Error calling 
processBlock() in User-Defined Object [addManyInts] at 
[AddManyInts.cpp:51], error code: 0, message: Exception while processing 
partition: [Function expects at least 2 integer parameters]
=> SELECT addManyInts(1,2.232343); -- Wrong data type
ERROR 3412:  Failure in UDx RPC call InvokeProcessBlock(): Error
calling processBlock() in User-Defined Object [addManyInts] at 
[AddManyInts.cpp:51], error code: 0, message: Exception while 
processing partition: [Function expects all arguments to be INTEGER. 
Argument 2 was Numeric(7,6)]

Notice that the errors returned by last two calls to the function were generated by the processBlock() function. It is up to your UDx to ensure that the user supplies the correct number and types of arguments to your function and exit with an error if it cannot process them.