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:
- Ensures there are at least two arguments
- Checks the data type of all arguments to ensure they are all integers
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.