Creating the C++ file for a UDTF
To implement a UDTF, you create a class object that is derived
from the Udtf base class. For example:
#include "udxinc.h"
using namespace nz::udx_ver2;
class parseNames : public Udtf {
public:
}
The parseNames UDTF takes an input table of
strings fields that are separated by spaces or commas, and returns
a table where each field of the requested string is output on its
own row. As with other UDXs, you define the variables that are required
for the UDTF algorithm at the class level. For example:
#include "udxinc.h"
using namespace nz::udx_ver2;
class parseNames : public Udtf {
private:
char value[1000];
int valuelen;
int i;
public:
}
The parseNames UDTF uses the following variables:
value
- A copy of the input parameter.
valuelen
- The length of the input string.
i
- A counter.
Each UDTF must implement the instantiate() and
constructor method and two more UDTF-specific methods: newInputRow() and nextOutputRow().
An example of the methods and their purpose follows.
- As with a UDSF, you call the instantiate() method
to create the UDTF object dynamically. The instantiate method
takes one argument (UdxInit *pInit), which enables access to the memory
specification, the log setting, and the UDX environment (see UDX environment). The constructor
must take a UdxInit object as well and pass it to the base class constructor.
An example follows:
#include "udxinc.h" using namespace nz::udx_ver2; class parseNames : public Udtf { private: char value[1000]; int valuelen; int i; public: parseNames(UdxInit *pInit) : Udtf(pInit) {} static Udtf* instantiate(UdxInit*); }; Udtf* parseNames::instantiate (UdxInit* pInit) { return new parseNames(pInit); }
- For a UDTF, you use the newInputRow() method
to perform initialization actions such as copying input arguments,
initializing class variables, and managing situations such as null
input variables. The method is called once for each input row. For
the parseNames UDTF example, the following sample
code copies the input list to the variable value, sets
valuelen
to the length of the input string, and initializes the variablei
to zero:virtual void newInputRow() { StringArg *valuesa = stringArg(0); bool valuesaNull = isArgNull(0); if (valuesaNull) valuelen = 0; else { if (valuesa->length >= 1000) throwUdxException("Input value must be less than 1000 characters."); memcpy(value, valuesa->data, valuesa->length); value[valuesa->length] = 0; valuelen = valuesa->length; } i = 0; }
- You use the nextOutputRow() method to create
and return the next output row of the table. You should also detect
whether there is more data to return and then return Done. The Db2® database engine calls this method
at least once per input row. For example:
virtual DataAvailable nextOutputRow() { if (i >= valuelen) return Done; // save starting position of name int start = i; // scan string for next comma while ((i < valuelen) && value[i] != ',') i++; // return word StringReturn *rk = stringReturnColumn(0); if (rk->size < i-start) throwUdxException("Value exceeds return size"); memcpy(rk->data, value+start, i-start); rk->size = i-start; i++; return MoreData; }
As shown in the example, you create a column by using the appropriate column return type such as stringReturnColumn() or intReturnColumn() and you specify the position of the column such as 1, 2, 3, and so on. The return MoreData syntax indicates that there is another row to process. When the counter variable
i
reaches the end of the input string, there is no more data to process and nextOutputRow() returns Done.