Creating the C++ file for a UDAF
To implement a UDAF, create a class object that is derived from the Uda base class. Use state values to share any data that is to be transferred across different processing phases.
The following C++ code implements a simple aggregate function called penmax, which returns the
second-largest value (that is, the penultimate maximum) of an input set. If there is not a
second-largest value, penmax returns
NULL.
#include "udxinc.h"
using namespace nz::udx_ver2;
class CPenMax: public nz::udx_ver2::Uda
{
public:
};
Each UDAF must implement the following methods in addition to its
constructor and destructor:
- instantiate()
- This method is called by the runtime engine to create the object
dynamically. The static implementation must be outside of the class
definition. The instantiate method takes one argument (UdxInit *pInit),
which enables access to the memory specification, the log setting,
and the UDX environment in the constructor (see UDX environment). It creates
an object of the derived class type by using the new operator and
returns it (as base class type Uda) to the runtime engine. The runtime
engine deletes the object when it is no longer needed. An example
follows:
class CPenMax : public nz::udx_ver2::Uda { public: CPenMax(UdxInit *pInit) : Uda(pInit) { } static nz::udx_ver2::Uda* instantiate(UdxInit *pInit); virtual void initializeState(); virtual void accumulate(); virtual void merge(); virtual ReturnValue finalResult(); }; nz::udx_ver2::Uda* CPenMax::instantiate(UdxInit *pInit) { return new CPenMax(pInit); }
- initializeState()
- This method is called to allow the implementer to initialize the
necessary state that is used in the UDAF. The state of a UDAF is one
or more values that must be valid Db2® data types. The state is automatically preserved by
the runtime engine between snippets, if necessary. To calculate the
penultimate maximum, the function must track the largest two numbers
in state variables. The initializeState() method
sets both the variables to NULL. The states are declared in the CREATE
AGGREGATE command, which is described later. An example follows:
void CPenMax::initializeState() { setStateNull(0, true); // set current max to null setStateNull(1, true); // set current penmax to null }
- accumulate()
- This method is called once per row and adds the contribution of
its arguments to the accumulator state. It updates the states to keep
the highest two values in the correct states. In addition to getting
the arguments through
int curVal = int32Arg(0);
, the method retrieves the two state variables by using the int32State(int) and isStateNull(int) functions. The accumulate method updates the states as required.void CPenMax::accumulate() { int *pCurMax = int32State(0); bool curMaxNull = isStateNull(0); int *pCurPenMax = int32State(1); bool curPenMaxNull = isStateNull(1); int curVal = int32Arg(0); bool curValNull = isArgNull(0); if ( !curValNull ) { // do nothing if argument is null - can't //affect max or penmax if ( curMaxNull ) { // if current max is null, this arg //becomes current max setStateNull(0, false); // current max no longer null *pCurMax = curVal; } else { if ( curVal > *pCurMax ) { // if arg is new max setStateNull(1, false); // then prior current max // becomes current penmax *pCurPenMax = *pCurMax; *pCurMax = curVal; // and current max gets arg } else if ( curPenMaxNull || curVal > *pCurPenMax ){ // arg might be greater than current penmax setStateNull(1, false); // it is *pCurPenMax = curVal; } } } }
- merge()
- This method is called with arguments of a second set of state
variables and merges this second state into its own state variables.
This method is necessary because the Db2 database engine merges data from
parallel units of execution into a single unit. The merge() method
merges two states, handling all the null values states correctly.
One of the states is passed in normally as in accumulate().
The second state is passed in as arguments, requiring the use of argument
retrieval functions such as int32Arg(int) and isArgNull(int) to
retrieve. For example:
void CPenMax::merge() { int *pCurMax = int32State(0); bool curMaxNull = isStateNull(0); int *pCurPenMax = int32State(1); bool curPenMaxNull = isStateNull(1); int nextMax = int32Arg(0); bool nextMaxNull = isArgNull(0); int nextPenMax = int32Arg(1); bool nextPenMaxNull = isArgNull(1); if ( !nextMaxNull ) { // if next max is null, then so is //next penmax and we do nothing if ( curMaxNull ) { setStateNull(0, false); // current max was null, // so save next max *pCurMax = nextMax; } else { if ( nextMax > *pCurMax ) { setStateNull(1, false); // next max is greater than current, so save next *pCurPenMax = *pCurMax; // and make current penmax prior current max *pCurMax = nextMax; } else if ( curPenMaxNull || nextMax > *pCurPenMax ) { // next max may be greater than current penmax setStateNull(1, false); // it is *pCurPenMax = nextMax; } } if ( !nextPenMaxNull ) { if ( isStateNull(1) ) { // can't rely on curPenMaxNull here, might have // change state var null flag above setStateNull(1, false); // first non-null penmax, // save it *pCurPenMax = nextPenMax; } else { if ( nextPenMax > *pCurPenMax ) { *pCurPenMax = nextPenMax; // next penmax greater than current, save it } } } } }
- finalResult()
- This method returns the final aggregation value from the accumulated
state. An example might be a UDAF implementation of an average aggregation,
where the finalResult() method divides the sum
by the count to produce an average. In this example, the finalResult() method
gathers one of the states and returns it by using the NZ_UDX_RETURN_INT32
macro in a similar fashion to evaluate() for a
UDSF.
ReturnValue CPenMax::finalResult() { int curPenMax = int32Arg(1); bool curPenMaxNull = isArgNull(1); if ( curPenMaxNull ) NZ_UDX_RETURN_NULL(); setReturnNull(false); NZ_UDX_RETURN_INT32(curPenMax); }
The following example shows the class header for the penmax
UDAF:
class CPenMax : public nz::udx_ver2::Uda
{
public:
static nz::udx_ver2::Uda* instantiate(UdxInit *pInit)
virtual void initializeState();
virtual void accumulate();
virtual void merge();
virtual ReturnValue finalResult();
};
nz::udx_ver2::Uda* CPenMax::instantiate(UdxInit *pInit)
{
return new CPenMax(pInit);
}