C++-Datei für UDA erstellen

Verwenden Sie zunächst einen Texteditor Ihrer Wahl zum Erstellen der C++-Datei. Ihre C++-Datei muss die Header-Datei " udxinc.h enthalten, die die erforderlichen Deklarationen für benutzerdefinierte Aggregate und die Verarbeitung auf den SPUs enthält.

#include "udxinc.h"

Vergewissern Sie sich außerdem, dass alle standardmäßigen Headerdateien für die C++-Bibliotheken deklariert wurden, die für Ihr Aggregat möglicherweise erforderlich sind. Wenn für Ihr UDA (User-Defined Aggregate; benutzerdefiniertes Aggregat) benutzerdefinierte gemeinsam genutzte Bibliotheken benötigt werden, dann notieren Sie die Bibliotheksnamen, da Sie sie zu einem späteren Zeitpunkt zur Registrierung des UDA in der Datenbank benötigen.

Benutzerdefinierte gemeinsam genutzte Bibliotheken müssen in der Datenbank bereits vorhanden sein, um das UDA registrieren und diese Bibliotheken als Abhängigkeiten angeben zu können. Sie können das UDA registrieren, ohne Bibliotheksabhängigkeiten anzugeben. Nach dem Hinzufügen der Bibliotheken können Sie den Befehl ALTER AGGREGATE verwenden, um die UDA-Definition mit den korrekten Abhängigkeiten zu aktualisieren. Weitere Informationen zu benutzerdefinierten gemeinsamen Bibliotheken finden Sie unter Erstellen einer benutzerdefinierten gemeinsamen Bibliothek.

Die UDX-Klassen für die API-Version 2 werden in einem Namensbereich mit dem Namen nz::udx_ver2 definiert. (Die UDXs der API-Version 1 verwenden den Namensbereich nz::udx.) Ihr C++-Programm muss auf den korrekten Namensbereich verweisen. Beispiel:
#include "udxinc.h"

using namespace nz::udx_ver2;

In diesem Abschnitt wird udx_ver2 als Standardnamensbereich für die nachfolgenden Beispiele verwendet. In den Abschnitten wird auf die Unterschiede zu UDX Version 1 hingewiesen, und die Beispielreferenz für benutzerdefinierte Funktionen und Aggregate enthält Beispiele für Definitionen der Version 1 und der Version 2. Sie können weiterhin UDAs für UDXs der Version 1 und neue UDAs der Version 2 erstellen. Beide Versionen können auf Systemen mit Release 6.0.x oder höher eingesetzt werden. Die UDAs der Version 1 funktionieren jedoch auf Netezza Performance Server Release 5.0.x und späteren Systemen und sind daher möglicherweise besser für Ihre Netezza Performance Server geeignet.

Zum Implementieren eines UDA erstellen Sie ein Klassenobjekt, das von der Basisklasse 'Uda' abgeleitet wird. Fortsetzung zum PenMax-Beispiel:
#include "udxinc.h"

using namespace nz::udx_ver2;

class CPenMax: public nz::udx_ver2::Uda
{
public:
};
Jedes UDA muss zusätzlich zum eigenen Konstruktor und Destruktor die folgenden fünf Methoden implementieren. Im Folgenden ist ein Beispiel für den Klassenheader des PenMax-UDA aufgeführt:
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);
}
  • Die Methode instantiate() wird von der Laufzeitengine aufgerufen, um das Objekt dynamisch zu erstellen. Die statische Implementierung muss außerhalb der Klassendefinition angegeben werden. In UDX Version 2 benötigt die Methode instantiate ein ArgumentUdxInit *pInit), das den Zugriff auf die Speicherspezifikation, die Log-Einstellung und die UDX-Umgebung im Konstruktor ermöglicht (siehe UDX-Umgebung). Es dient zur Erstellung eines Objekts des abgeleiteten Klassentyps anhand des neuen Operators und gibt es (mit dem Basisklassentyp 'Uda') an die Laufzeitengine zurück. Die Laufzeitengine löscht das Objekt, wenn es nicht mehr benötigt wird. Beispiel:
       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);
    }
  • Die Methode initializeState() wird aufgerufen, um dem Implementierer die Initialisierung des erforderlichen Status zu ermöglichen, der im UDA verwendet wird. Der Status eines UDA besteht aus einem oder mehreren Werten, die gültige Netezza Performance Server sein müssen. Der Status wird von der Laufzeitengine automatisch snippetübergreifend gespeichert, wenn dies erforderlich ist. Zur Berechnung des vorletzten Maximums muss die Funktion die beiden höchsten numerischen Werte in den Statusvariablen überwachen. Die Methode initializeState() legt für beide Variablen NULL fest. Die Statuswerte werden im Befehl CREATE AGGREGATE deklariert, der an späterer Stelle beschrieben wird. Beispiel:
       void CPenMax::initializeState()
    {
        setStateNull(0, true); // set current max to null
        setStateNull(1, true); // set current penmax to null
    }
  • Die Methode accumulate() wird einmal pro Zeile aufgerufen und dient zum Hinzufügen der Ergänzung der zugehörigen Argumente zum Akkumulatorstatus. Sie aktualisiert die Statuswerte, um den korrekten Zustand der beiden höchsten Werte sicherzustellen. Zusätzlich zum Abruf der Argumente über int curVal = int32Arg(0); ruft die Methode die beiden Statusvariablen mithilfe der Funktionen int32State(int) und isStateNull(int) ab. Mit der Kumulierungsmethode werden die Statuswerte bei Bedarf aktualisiert.
       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;
                }
            }
        }
    }
  • Die Methode merge() wird mit Argumenten der zweiten Gruppe mit Statusvariablen aufgerufen und führt diesen zweiten Status mit den eigenen Statusvariablen zusammen. Diese Methode ist notwendig, da das Netezza Performance Server eine Parallelverarbeitungsarchitektur ist und die Aggregatzustände von allen SPUs an den Host gesendet werden, wo sie zu einem einzigen Aggregationszustand zusammengeführt werden. Die Methode merge() dient zur Zusammenführung von zwei Statuswerten, wobei alle Status von Nullwerten korrekt verarbeitet werden. Einer der Statuswerte wird normalerweise wie bei accumulate() übergeben. Der zweite Statuswert wird in Form von Argumenten übergeben. Hierzu ist die Verwendung von Argumentabruffunktionen wie beispielsweise int32Arg(int) und isArgNull(int) erforderlich. Beispiel:
       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
                    }
                }
            }
        }
    }
  • Die Methode finalResult() gibt den endgültigen Aggregationswert auf Basis des kumulierten Statuswerts zurück. Als Beispiel kann eine UDA-Implementierung einer Durchschnittsaggregation angeführt werden, bei der die Methode finalResult() die Summe durch die Anzahl dividiert, um einen Durchschnittswert zu erhalten. In diesem Beispiel erfasst die Methode finalResult() einen der Statuswerte und gibt ihn mithilfe des Makros NZ_UDX_RETURN_INT32 in ähnlicher Weise wie für evaluate() im Falle einer UDF zurück.
       ReturnValue CPenMax::finalResult()
    {
        int curPenMax = int32Arg(1);
        bool curPenMaxNull = isArgNull(1);
    
        if ( curPenMaxNull )
            NZ_UDX_RETURN_NULL();
        setReturnNull(false);
        NZ_UDX_RETURN_INT32(curPenMax);
    }

Mit dem Makro NZ_UDX_RETURN_INT32 kann auf einfache Weise überprüft werden, ob der Rückgabewert den erwarteten Typ aufweist. Eine Liste der verfügbaren Rückgabemakros finden Sie unter UDX-Rückgabemakros. Die Methode " finalResult() kann auf alle API-Aufrufe der Datentyp-Helper zugreifen und auf eine Liste von Zustandsargumenten, die in UDA-Zustandsargumenten aufgeführt sind.