Kapitel 10. MySQL erweitern

Inhaltsverzeichnis

10.1. Hinzufügen neuer Funktionen zu MySQL
10.1.1. CREATE FUNCTION / DROP FUNCTION-Syntax
10.1.2. Hinzufügen einer neuen benutzerdefinierten Funktion
10.1.3. Hinzufügen einer neuen nativen Function
10.2. Hinzufügen neuer Prozeduren zu MySQL
10.2.1. PROCEDURE ANALYSE
10.2.2. Eine Prozedur schreiben
10.3. MySQL-Interna
10.3.1. MySQL-Thread
10.3.2. MySQL-Test-Suite

10.1. Hinzufügen neuer Funktionen zu MySQL

Es gibt zwei Möglichkeiten, MySQL neue Funktionen hinzuzufügen:

  • Sie können die Funktion über die benutzerdefinierbare Funktions- (UDF-) Schnittstelle hinzufügen. Benutzerdefinierbare Funktionen werden dynamisch mittels CREATE FUNCTION und DROP FUNCTION-Statements hinzugefügt bzw. gelöscht. See Abschnitt 10.1.1, „CREATE FUNCTION / DROP FUNCTION-Syntax“.

  • Sie können die Funktion als native (eingebaute) MySQL-Funktion hinzufügen. Native Funktionen werden in den mysqld-Server kompiliert und stehen dann dauerhaft zur Verfügung.

Jede Methode hat Vorteile und Nachteile:

  • Wenn Sie eine benutzerdefinierte Funktion schreiben, müssen Sie die Objekt-Datei zusätzlich zum Server selbst installieren. Wenn Sie Ihre Funktion in den Server einkompilieren, brauchen Sie das nicht zu tun.

  • Sie können der binären MySQL-Distribution benutzerdefinierte Funktionen hinzufügen. Native Funktionen erfordern, dass Sie eine Quelldistribution verändern.

  • Wenn Sie Ihre MySQL-Distribution aktualisieren, können Sie weiterhin Ihre vorher installierten benutzerdefinierten Funktionen benutzen. Bei nativen Funktionen müssen Sie Ihre Änderungen jedes Mal wiederholen, wenn Sie aktualisieren.

Gleich welche Methode Sie zum Hinzufügen neuer Funktionen verwenden, können Sie diese genau wie die nativen Funktionen, z. B. ABS() oder SOUNDEX(), benutzen.

10.1.1. CREATE FUNCTION / DROP FUNCTION-Syntax

CREATE [AGGREGATE] FUNCTION funktion RETURNS {STRING|REAL|INTEGER}
       SONAME gemeinsame_bibliothek

DROP FUNCTION funktion

Eine benutzerdefinierte Funktion (UDF) ist eine Möglichkeit, MySQL durch eine neue Funktion zu erweitern, die wie die nativen (eingebauten) MySQL-Funktionen, z. B. ABS() und CONCAT(), funktioniert.

AGGREGATE ist eine neue Option für MySQL-Version 3.23. Eine AGGREGATE-Funktion funktioniert genau wie eine native MySQL- GROUP-Funktion wie SUM oder COUNT().

CREATE FUNCTION speichert den Funktionnamen, -typ und die gemeinsam genutzte Bibliothek in der mysql.func-Systemtabelle. Sie benötigen die insert- und delete-Berechtigungen für die mysql-Datenbank, um Funktionen zu erzeugen und zu löschen.

Alle aktiven Funktionen werden jedes Mal wieder geladen, wenn der Server startet, es sei denn, Sie starten ihn mit der --skip-grant-tables-Option. In diesem Fall wird die UDF-Initialisierung übersprungen, so dass UDFs nicht verfügbar sind. (Eine aktive Funktion ist eine, die mit CREATE FUNCTION geladen und nicht mit DROP FUNCTION entfernt wurde.)

Wegen weiterer Anleitungen zum Schreiben benutzerdefinierte Funktionen siehe Abschnitt 10.1, „Hinzufügen neuer Funktionen zu MySQL“. Damit der UDF-Mechanismus funktioniert, müssen Funktionen in C oder C++ geschrieben sein. Ihr Betriebssystem muss dynamisches Laden unterstützen und Sie müssen mysqld dynamisch (nicht statisch) kompiliert haben.

Beachten Sie, dass Sie für das Funktionieren von AGGREGATE eine mysql.func-Tabelle benötigen, die die Spalte typ enthält. Wenn das nicht der Fall ist, sollten Sie das Skript mysql_fix_privilege_tables laufen lassen, um diesen Mangel zu beheben.

10.1.2. Hinzufügen einer neuen benutzerdefinierten Funktion

Damit der UDF-Mechanismus funktioniert, müssen Funktionen in C oder C++ geschrieben sein. Ihr Betriebssystem muss dynamisches Laden unterstützen und Sie müssen mysqld dynamisch (nicht statisch) kompiliert haben. Die MySQL-Quelldistribution enthält eine Datei sql/udf_example.cc, die 5 neue Funktionen definiert. Sehen Sie in dieser Datei nach, wie die UDF-Aufruf-Konventionen funktionieren.

Damit mysqld UDF-Funktionen benutzen kann, sollten Sie MySQL mit --with-mysqld-ldflags=-rdynamic konfigurieren. Der Grund liegt darin, dass Sie auf vielen Plattformen (inklusive Linux) eine dynamische Bibliothek (mit dlopen()) von einem statisch gelinkten Programm laden können, was Sie erhalten würden, wenn Sie --with-mysqld-ldflags=-all-static benutzen. Wenn Sie eine UDF benutzen wollen, die auf Symbole von mysqld zugreifen muss (wie das methaPhone-Beispiel in sql/udf_example.cc, das default_charset_info benutzt), müssen Sie das Programm mit -rdynamic benutzen (siehe man dlopen).

Für jede Funktion, die Sie in SQL-Statements benutzen wollen, sollten Sie die entsprechenden C- (oder C++-) Funktionen benutzen. In den unten stehenden Ausführungen wird ``xxx'' als Beispiel-Funktionsname benutzt. Um zwischen SQL- und C-/C++-Benutzung zu unterscheiden, kennzeichnet XXX() (Großschreibung) einen SQL-Funktionsaufruf und xxx() (Kleinschreibung) einen C-/C++-Funktionsaufruf.

The C-/C++-Funktionen, die Sie für die Implementierung der Schnittstelle für XXX() schreiben, sind:

  • xxx() (required)

    Die Hauptfunktion. Hier wird das Funktionsergebnis berechnet. Der Zusammenhang zwischen dem SQL-Typ und dem Rückgabe-Typ Ihrer C-/C++-Funktion ist unten dargestellt:

    SQL-TypC-/C++-Typ
    STRINGchar *
    INTEGERlong long
    REALdouble
  • xxx_init() (optional)

    Die Initialisierungsfunktion für xxx(). Sie kann für folgendes benutzt werden:

    • Um die Anzahl von Argumenten für XXX() zu prüfen.

    • Um zu prüfen, ob die Argumente vom erforderlichen Typ sind oder, alternativ, MySQL mitzuteilen, den Argumenttyp zu erzwingen, den Sie beim Aufruf der Hauptfunktion brauchen.

    • Um jeglichen Speicher zuzuweisen, der von der Hauptfunktion benötigt wird.

    • Um die maximale Länge des Ergebnisses anzugeben.

    • Um (für REAL-Funktionen) die maximale Anzahl von Dezimalstellen anzugeben.

    • Um festzulegen, ob das Ergebnis NULL sein darf oder nicht.

  • xxx_deinit() (optional)

    Die Deinitialisierungsfunktion für xxx(). Sie sollte jeglichen Speicher freigeben (deallozieren), der durch die Initialisierungsfunktion zugewiesen wurde.

Wenn ein SQL-Statement XXX() aufruft, ruft MySQL die Initialisierungsfunktion xxx_init() auf, damit diese die notwendige Einrichtung vornehmen kann wie Argumente prüfen oder Speicherzuweisung. Wenn xxx_init() einen Fehler zurückgibt, wird das SQL-Statement mit einer Fehlermeldung abgebrochen, die Haupt- und Deinitialisierungsfunktionen werden nicht aufgerufen. Ansonsten wird die Hauptfunktion xxx() für jede Zeile aufgerufen. Nachdem alle Zeilen abgearbeitet sind, wird die Deinitialisierungsfunktion xxx_deinit() aufgerufen, damit sie die erforderlichen Aufräumarbeiten ausführen kann.

Alle Funktionen müssen Thread-sicher sein (nicht nur die Hauptfunktion, sondern auch die Initialisierungs- und Deinitialisierungsfunktionen). Das heißt, dass Sie keinerlei globale oder statische Variablen zuweisen dürfen, die sich ändern! Wenn Sie Speicher brauchen, sollten Sie ihn in xxx_init() zuweisen und in xxx_deinit() freigeben.

10.1.2.1. UDF-Aufruf-Sequenzen

Die Hauptfunktion sollte wie unten dargestellt deklariert werden. Beachten Sie, dass sich der Rückgabetyp und der Parameter unterscheiden, abhängig davon, wie Sie die SQL-Funktion XXX() deklarieren, damit sie STRING, INTEGER oder REAL im CREATE FUNCTION-Statement zurückgibt:

Bei STRING-Funktionen:

char *xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *result, unsigned long *length,
              char *is_null, char *error);

Bei INTEGER-Funktionen:

long long xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);

Bei REAL-Funktionen:

double xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);

Die Initialisierungs- und Deinitialisierungsfunktionen werden wie folgt deklariert:

my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);

void xxx_deinit(UDF_INIT *initid);

Der initid-Parameter wird an alle drei Funktionen übergeben. Er zeigt auf eine UDF_INIT-Struktur, die benutzt wird, um Informationen zwischen den Funktionen zu übermitteln. Die UDF_INIT-Strukturmitglieder sind unten aufgelistet. Die Initialisierungsfunktion sollte alle Mitglieder ausfüllen, die sie ändern will. (Um für ein Mitglied den Vorgabewert zu verwenden, lassen Sie es unverändert.)

  • my_bool maybe_null

    xxx_init() sollte maybe_null auf 1 setzen, wenn xxx() NULL zurückgeben kann. Der Vorgabewert ist 1, wenn irgend eins der Argumente als maybe_null deklariert ist.

  • unsigned int Dezimalstellen

    Anzahl von Dezimalstellen. Der Vorgabewert ist die maximale Anzahl von Dezimalstellen in den Argumenten, die an die Hauptfunktion übergeben werden. (Wenn der Funktion beispielsweise die Argumente 1.34, 1.345 und 1.3 übergeben werden, wäre der Vorgabewert 3, weil 1.345 3 Dezimalstellen hat.

  • unsigned int max_length

    Die maximale Länge des Zeichenkettenergebnisses. Der Vorgabewert ist unterschiedlich, abhängig vom Ergebnistyp der Funktion. Bei Zeichenketten-Funktionen ist die Vorgabe die Länge des längsten Arguments. Bei Ganzzahl-Funktionen ist die Vorgabe 21 Ziffern. Bei REAL-Funktionen ist die Vorgabe 13 plus die Anzahl von Dezimalstellen, die von initid->Dezimalstellen angezeigt werden. (Bei numerischen Funktionen enthält die Länge jedes Vorzeichen- oder Dezimalpunkt-Zeichen.)

    Wenn Sie einen Blob zurückgeben wollen, können Sie diesen auf 65 KB oder 16MB setzen. Der Speicher wird nicht zugewiesen, aber dazu verwendet, um zu entscheiden, welcher Spaltentyp benutzt werden soll, falls es notwendig werden sollte, Daten temporär zu speichern.

  • char *ptr

    Ein Zeiger, den die Funktion für eigene Zwecke verwenden kann. Beispielsweise können Funktionen initid->ptr benutzen, um Informationen über den zugewiesenen Speicher zwischen den Funktionen zu kommunizieren. Beispiel, um in xxx_init() Speicher zuzuweisen und ihn diesem Zeiger zuzuordnen:

    initid->ptr = allocated_memory;
    

    In xxx() und xxx_deinit() verweisen Sie auf initid->ptr, um Speicher zu verwenden oder freizugeben.

10.1.2.2. Verarbeitung von Argumenten

Der args-Parameter zeigt auf eine UDF_ARGS-Struktur, die unten aufgelistete Mitglieder hat:

  • unsigned int arg_count

    Die Anzahl von Argumenten. Prüfen Sie diesen Wert in der Initialisierungsfunktion, wenn Sie wollen, dass Ihre Funktion mit einer bestimmten Anzahl von Argumenten aufgerufen wird. Beispiel:

    if (args->arg_count != 2)
    {
        strcpy(message,"XXX() benoetigt zwei Argumente");
        return 1;
    }
    

  • enum Item_result *arg_type

    Die Typen für jedes Argument. Die möglichen Typenwerte sind STRING_RESULT, INT_RESULT und REAL_RESULT.

    Um sicherzustellen, dass die Argumente vom angegebenen Typ sind und einen Fehler zurückgeben, falls nicht, prüfen Sie das arg_type-Array in der Initialisierungsfunktion. Beispiel:

    if (args->arg_type[0] != STRING_RESULT ||
        args->arg_type[1] != INT_RESULT)
    {
        strcpy(message,"XXX() erfordert eine Zeichenkette und eine Ganzzahl");
        return 1;
    }
    

    Als Alternative dazu, dass Ihre Funktionsargumente von bestimmten Typen sein müssen, können Sie die Initialisierungsfunktion benutzen, um die arg_type-Elemente auf die Typen zu setzen, die Sie wollen. Das veranlasst MySQL, die Typen der Argumente bei jedem Aufruf von xxx() zu erzwingen. Um beispielsweise zu erzwingen, dass die ersten zwei Argumente Zeichenkette und Ganzzahl sind, geben Sie in xxx_init() folgendes ein:

    args->arg_type[0] = STRING_RESULT;
    args->arg_type[1] = INT_RESULT;
    

  • char **args

    args->args kommuniziert der Initialisierungsfunktion Informationen über die allgemeine Natur der Argumente, mit der Ihre Funktion aufgerufen wurde. Bei einem Konstanten-Argument i zeigt args->args[i] auf den Argumentwert. (Siehe unten wegen Anleitungen, wie auf diesen Wert korrekt zugegriffen wird.) Bei einem Nicht-Konstanten-Argument ist args->args[i] 0. Ein Konstanten-Argument ist ein Ausdruck, der nur Konstanten wie 3 oder 4*7-2 oder SIN(3.14) benutzt. Ein Nicht-Konstanten-Argument ist ein Ausdruck, der auf Werte verweist, die sich von Zeile zu Zeile ändern können, wie Spaltennamen oder Funktionen, die mit Nicht-Konstanten-Argumenten aufgerufen werden.

    Bei jedem Aufruf der Hauptfunktion enthält args->args die tatsächlichen Argumente, die für die Zeile übergeben werden, die momentan verarbeitet wird.

    Funktionen können auf ein Argument i wie folgt verweisen:

    • Ein Argument des Typs STRING_RESULT wird als ein Zeichenkettenzeiger plus einer Länge angegeben, um die Handhabung von Binärdaten oder Daten beliebiger Länge zu erlauben. Die Zeichenketten-Inhalte sind als args->args[i] und die Zeichenkettenlänge als args->lengths[i] verfügbar. Sie sollten nicht davon ausgehen, dass Zeichenketten null-terminiert sind.

    • Bei einem Argument des Typs INT_RESULT müssen Sie args->args[i] zu einem long long-Wert machen (cast):

      long long int_val;
      int_val = *((long long*) args->args[i]);
      
    • Bei einem Argument des Typs REAL_RESULT müssen Sie args->args[i] zu einem double-Wert machen (cast):

      double    real_val;
      real_val = *((double*) args->args[i]);
      

  • unsigned long *lengths

    Bei der Initialisierungsfunktion gibt das lengths-Array die maximale Zeichenkettenlänge jedes Arguments an. Bei jedem Aufruf der Hauptfunktion enthält lengths die tatsächlichen Längen jeglicher Zeichenketten-Argumente, die für die momentan verarbeitete Zeile übergeben werden. Bei Argumenten des Typs INT_RESULT oder REAL_RESULT enthält lengths immer noch die maximale Länge des Arguments (wie bei der Initialisierungsfunktion).

10.1.2.3. Rückgabewerte und Fehlerbehandlung

Die Initialisierungsfunktion sollte 0 zurückgeben, wenn kein Fehler auftrat, ansonsten 1. Wenn ein Fehler auftritt, sollte xxx_init() eine null-terminierte Fehlermeldung im message-Parameter enthalten. Die Meldung wird an den Client übergeben. Der Meldungspuffer ist MYSQL_ERRMSG_SIZE Zeichen lang, aber Sie sollten versuchen, die Meldung kleiner als 80 Zeichen zu halten, damit sie auf die Anzeigebreite eines Standard-Terminals passt.

Der Rückgabewert der Hauptfunktion xxx() ist der Funktionswert, bei long long- und double-Funktionen. Eine Zeichenkettenfunktion sollte einen Zeiger auf das Ergebnis und die Länge der Zeichenkette in den length-Argumenten zurückgeben.

Setzen Sie diese auf die Inhalte und Länge des Rückgabewerts. Beispiel:

memcpy(result, "ergebnis_zeichenkette", 13);
*length = 13;

Der result-Puffer, der an die Berechnungsfunktionen übergeben wird, ist 255 Byte Groß. Wenn Ihr Ergebnis dort hinein passt, müssen Sie sich um die Speicherzuweisung für Ergebnisse nicht kümmern.

Wenn Ihre Zeichenketten-Funktion eine Zeichenkette zurückgeben muss, die länger als 255 Bytes ist, müssen Sie den Platz dafür mit malloc() in Ihrer xxx_init()-Funktion oder Ihrer xxx()-Funktion zuweisen und in Ihrer xxx_deinit()-Funktion freigeben. Sie können den zugewiesenen Speicher im ptr-Slot in der UDF_INIT-Struktur für erneute Benutzung durch zukünftige xxx()-Aufrufe speichern. See Abschnitt 10.1.2.1, „UDF-Aufruf-Sequenzen“.

Um einen Rückgabewert von NULL in der Hauptfunktion anzuzeigen, setzen Sie is_null auf 1:

*is_null = 1;

Um eine Fehlerrückgabe in der Hauptfunktion anzuzeigen, setzen Sie den error-Parameter auf 1:

*error = 1;

Wenn xxx() *error für beliebige Zeilen auf 1 setzt, ist der Funktionswert der aktuellen Zeile NULL, was auch für nachfolgende Zeilen gilt, die von dem Statement verarbeitet werden, in dem XXX() aufgerufen wurde. (xxx() wird für nachfolgende Zeilen nicht einmal aufgerufen.) HINWEIS: In MySQL-Versionen vor 3.22.10 sollten Sie sowohl *error als auch und *is_null setzen:

*error = 1;
*is_null = 1;

10.1.2.4. Kompilieren und Installieren benutzerdefinierter Funktionen

Dateien, die UDFs implementieren, müssen auf dem Host kompiliert und installiert werden, auf dem der Server läuft. Dieser Prozess wird unten am Beispiel der UDF-Datei udf_example.cc beschrieben, die in der MySQL-Quelldistribution enthalten ist. Diese Datei enthält folgende Funktionen:

  • metaphon() gibt eine metaphon-Zeichenkette des Zeichenkettenarguments zurück. Das ist etwas wie eine Soundex-Zeichenkette, nur etwas besser für englisch angepasst.

  • myfunc_double() gibt die Summe der ASCII-Werte der Zeichen in ihren Argumenten zurück, geteilt durch die Summe der Längen ihrer Argumente.

  • myfunc_int() gibt die Summe der Längen ihrer Argumente zurück.

  • sequence([const int]) gibt eine Sequenz zurück, die mit der angegebenen Zahl startet oder mit 1, wenn keine Zahl angegeben wurde.

  • lookup() gibt die IP-Nummer für einen Hostnamen zurück.

  • reverse_lookup() gibt den Hostnamen für eine IP-Nummer zurück. Die Funktion kann mit einer Zeichenkette "xxx.xxx.xxx.xxx" oder mit vier Zahlen aufgerufen werden.

Eine dynamisch ladbare Datei sollte als gemeinsam nutzbare Objektdatei kompiliert werden, etwa mit folgendem Befehl:

shell> gcc -shared -o udf_example.so myfunc.cc

Die korrekten Kompiler-Optionen für Ihr System finden Sie leicht heraus, wenn Sie diesen Befehl im sql-Verzeichnis Ihres MySQL-Quellbaums laufen lassen:

shell> make udf_example.o

Sie sollten einen Kompilierbefehl laufen lassen, der dem ähnelt, was make anzeigt, ausser dass Sie die -c-Option kurz vor dem Zeilenende entfernen und -o udf_example.so am Zeilenende hinzufügen sollten. (Auf manchen Systemen können Sie -c im Befehl lassen.)

Wenn Sie ein gemeinsam genutztes Objekt kompiliert haben, das UDFs enthält, müssen Sie es danach installieren und MySQL darüber informieren. Wenn Sie ein gemeinsam genutztes Objekt von udf_example.cc kompilieren, wird eine Datei etwa mit dem Namen udf_example.so erzeugt (der exakte Name variiert von Plattform zu Plattform). Kopieren Sie diese Datei in ein Verzeichnis, das von ld durchsucht wird, wie /usr/lib. Auf vielen Systemen können Sie die LD_LIBRARY- oder LD_LIBRARY_PATH-Umgebungsvariable so setzen, dass sie auf das Verzeichnis zeigt, wo Sie Ihre UDF-Funktionsdateien haben. Das dlopen-Handbuch sagt Ihnen, welche Variable Sie auf Ihrem System setzen sollten. Sie sollten diese auf mysql.server oder safe_mysqld setzen und mysqld neu starten.

Nachdem die Bibliothek installiert ist, unterrichten Sie mysqld über die neuen Funktionen mit diesen Befehlen:

mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
mysql> CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
mysql> CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";

Funktionen können mit DROP FUNCTION gelöscht werden:

mysql> DROP FUNCTION metaphon;
mysql> DROP FUNCTION myfunc_double;
mysql> DROP FUNCTION myfunc_int;
mysql> DROP FUNCTION lookup;
mysql> DROP FUNCTION reverse_lookup;
mysql> DROP FUNCTION avgcost;

Die CREATE FUNCTION- und DROP FUNCTION-Statements aktualisieren die Systemtabelle func in der mysql-Datenbank. Der Funktionsname, -typ und gemeinsam genutzte Bibliothek werden in der Tabelle gespeichert. Sie benötigen die insert- und delete-Berechtigungen für die mysql-Datenbank, um Funktionen zu erzeugen und zu löschen.

Sie sollten CREATE FUNCTION nicht benutzen, um eine Funktion hinzuzufügen, die bereits erzeugt wurde. Wenn Sie eine Funktion erneut installieren wollen, sollten Sie sie zuerst mit DROP FUNCTION entfernen und dann mit CREATE FUNCTION erneut installieren. Sie müssen so etwas zum Beispiel tun, wenn Sie eine neue Version Ihrer Funktion kompilieren, damit mysqld die neue Version erhält. Ansonsten würde der Server mit der alten Version weitermachen.

Aktive Funktionen werden jedes Mal neu geladen, wenn der Server startet, es sei denn, Sie starten mysqld mit der --skip-grant-tables-Option. In diesem Fall wird die UDF-Initialisierung übersprungen und UDFs sind nicht verfügbar. (Eine aktive Funktion ist eine, die mit CREATE FUNCTION geladen und nicht mit DROP FUNCTION entfernt wurde.)

10.1.3. Hinzufügen einer neuen nativen Function

Die Prozedur zum Hinzufügen einer neuen nativen Funktion wird hier beschrieben. Beachten Sie, dass Sie einer Binärdistribution keine nativen Funktionen hinzufügen können, weil die Prozedur die Änderung des MySQL-Quelltextes beinhaltet. Sie müssen MySQL selbst aus einer Quelldistribution kompilieren. Beachten Sie auch, dass Sie die Prozedur wiederholen müssen, wenn Sie auf eine andere Version von MySQL aktualisieren (beispielsweise wenn eine neue Version herauskommt).

Um eine neue native MySQL-Funktion hinzuzufügen, gehen Sie wie folgt vor:

  1. Fügen Sie lex.h eine neue Zeile hinzu, die den Funktionsnamen im sql_functions[]-Array definiert.

  2. Wenn der Funktionsprototyp einfach ist (nur keins, eins, zwei oder drei Argumente entgegennimmt), sollten Sie in lex.h SYM(FUNC_ARG#) angeben (wobei # die Anzahl von Argumenten ist), als zweites Argument im sql_functions[]-Array, und eine Funktion hinzufügen, die ein Funktionsobjekt in item_create.cc erzeugt. Sehen Sie sich als Beispiel hierfür "ABS" und create_funcs_abs() an.

    Wenn der Funktionsprototyp kompliziert ist (zum Beispiel eine variable Anzahl von Argumenten entgegennimmt), sollten Sie zwei Zeile zu sql_yacc.yy hinzufügen. Eine gibt das Präprozessorsymbol an, das yacc definieren soll (das sollte am Anfang der Datei stehen). Definieren Sie dann die Funktionsparameter und fügen Sie ein ``item'' mit diesen Parametern zur simple_expression-Parsing-Regel hinzu. Sehen Sie sich als Beispiel alle Vorkommen von ATAN in sql_yacc.yy an, um zu sehen, wie das gemacht wird.

  3. Deklarieren Sie in item_func.h eine Klasse, die von Item_num_func oder Item_str_func erbt, je nachdem, ob Ihre Funktion eine Zahl oder eine Zeichenkette zurückgibt.

  4. Fügen Sie in item_func.cc eine der folgenden Deklarationen hinzu, je nachdem, ob Sie eine numerische oder eine Zeichenketten-Funktion definieren:

    double   Item_func_newname::val()
    longlong Item_func_newname::val_int()
    String  *Item_func_newname::Str(String *str)
    

    Wenn Sie Ihr Objekt von irgend einem der Standard-Items erben (wie von Item_num_func, müssen Sie wahrscheinlich eine der oben genannten Funktionen definieren und das Elternobjekt sich um die anderen Funktionen kümmern lassen. Beispielsweise definiert die Item_str_func-Klasse eine val()-Funktion, die atof() auf dem Wert ausführt, der von ::str() zurückgegeben wurde.

  5. Sie müssen wahrscheinlich auch die folgende Objektfunktion definieren:

    void Item_func_newname::fix_length_und_dec()
    

    Diese Funktion sollte zumindest max_length basierend auf den angegebenen Argumenten berechnen. max_length ist die maximale Anzahl von Zeichen, die die Funktion zurückgeben kann. Diese Funktion sollte auch maybe_null = 0 setzen, wenn die Hauptfunktion keinen NULL-Wert zurückgeben kann. Die Funktion kann prüfen, ob irgend eins der Funktionsargumente NULL zurückgeben kann, indem die Argumente der maybe_null-Variable geprüft werden. Sehen Sie sich als typisches Beispiel, wie das gemacht wird, Item_func_mod::fix_length_and_dec an.

Alle Funktionen müssen Thread-sicher sein (mit anderen Worten: Benutzen Sie keine globalen oder statischen Variablen in den Funktionen, ohne sie mit mutexes zu schützen).

Wenn Sie von ::val(), ::val_int() oder ::str() NULL zurückgeben wollen, sollten Sie null_value auf 1 setzen und 0 zurückgeben.

Bei ::str()-Objektfunktionen gibt es einige zusätzliche Überlegungen, auf die man achten sollte:

  • Das String *str-Argument stellt einen Zeichenketten-Puffer zur Verfügung, der benutzt werden kann, um das Ergebnis zu speichern. (Weitere Informationen über den String-Typ finden Sie durch einen Blick in die sql_string.h-Datei.)

  • Die ::str()-Funktion sollte die Zeichenkette zurückgeben, die das Ergebnis enthält, oder (char*) 0, wenn das Ergebnis NULL ist.

  • Alle aktuellen Zeichenketten-Funktionen versuchen, die Zuweisung jeglichen Speichers zu vermeiden, ausser wenn das absolut notwendig ist!

10.2. Hinzufügen neuer Prozeduren zu MySQL

In MySQL können Sie eine Prozedur in C++ definieren, die auf Daten in einer Anfrage zugreifen und diese ändern kann, bevor sie an den Client geschickt werden. Die Änderung kann Zeile für Zeile oder auf GROUP BY-Ebene geschehen.

Wir haben eine Beispiel-Prozedur in MySQL-Version 3.23 erzeugt, um zu zeigen, was getan werden kann.

Zusätzlich empfehlen wir, dass Sie einen Blick auf 'mylua' werfen, das Sie im Contrib-Verzeichnis finden. Hiermit können Sie die LUA-Sprache benutzen, um eine Prozedur zur Laufzeit in mysqld zu laden.

10.2.1. PROCEDURE ANALYSE

analyse([max Elemente,[max memory]])

Diese Prozedur ist in sql/sql_analyse.cc definiert. Sie untersucht das Ergebnis Ihrer Anfrage und gibt eine Analyse des Ergebnisses zurück:

  • max elements (Vorgabe 256) ist die maximale Anzahl unterschiedlicher Werte, die analyse pro Spalte findet. Dieses wird von analyse benutzt, um zu prüfen, ob der optimale Spaltentyp vom Typ ENUM sein sollte.

  • max memory (Vorgabe 8.192) ist der maximale Speicher, den analyse pro Spalte zuweisen sollte, wenn Sie versuchen, alle unterschiedlichen (distinct) Werte zu finden.

SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max elements,[max memory]])

10.2.2. Eine Prozedur schreiben

Im Moment ist die einzige Dokumentation hierfür der Quelltext.

Sie finden alle Informationen über Prozeduren, wenn Sie folgende Dateien untersuchen:

  • sql/sql_analyse.cc

  • sql/procedure.h

  • sql/procedure.cc

  • sql/sql_select.cc

10.3. MySQL-Interna

Dieses Kapitel beschreibt viele Dinge, die Sie wissen müssen, wenn Sie am MySQL-Code arbeiten. Wenn Sie an der MySQL-Entwicklung mitarbeiten wollen, Zugriff auf den messerscharfen Code von Zwischenversionen haben wollen, oder einfach nur über die Entwicklung auf dem Laufenden bleiben wollen, folgen Sie den Anweisungen unter See Abschnitt 3.3, „Installation der Quelldistribution“. Wenn Sie an MySQL-Interna interessiert sind, sollten Sie auch abonnieren. Das ist eine Liste mit relativ geringem Verkehr, verglichen mit .

10.3.1. MySQL-Thread

Der MySQL-Server erzeugt folgenden Thread:

  • Der TCP/IP-Verbindungs-Thread erledigt alle Verbindungsanfragen und erzeugt einen neuen dedizierten Thread, um die Verarbeitung von Authentifizierung und SQL-Anfragen für jede Verbindung zu handhaben.

  • Unter Windows NT gibt es einen Named-Pipe-Handler-Thread, der dasselbe tut wie der TCP/IP-Verbindungs-Thread, auf Named-Pipe-Verbindungsanforderungen.

  • Der Signal-Thread handhabt alle Signale. Dieser Thread handhabt normalerweise auch Alarme und Aufrufe von process_alarm(), um Zeitüberschreitungen auf Verbindungen zu erzwingen, die zu lange im Leerlauf waren.

  • Wenn mysqld mit -DUSE_ALARM_THREAD kompiliert wird, wird ein dedizierter Thread erzeugt, der Alarme handhabt. Das ist nur nützlich auf manchen Systemen, auf denen es Probleme mit sigwait() gibt, oder wenn man den thr_alarm()-Code in seiner Applikation ohne einen dedizierten Signal-Handhabungs-Thread benutzen will.

  • Wenn man die --flush_time=#-Option benutzt, wird ein dedizierter Thread erzeugt, der alle Tabellen im angegebenen Intervall auf Platte zurückschreibt.

  • Jede Verbindung hat ihren eigenen Thread.

  • Jede unterschiedliche Tabelle, auf der man INSERT DELAYED benutzt, erhält ihren eigenen Thread.

  • Wenn Sie --master-host benutzen, wird ein Slave-Replikations-Thread gestartet, der Aktualisierungen vom Master liest und anwendet.

mysqladmin processlist zeigt nur die Verbindungs-, INSERT DELAYED- und Replikations-Threads.

10.3.2. MySQL-Test-Suite

Bis vor Kurzem basierte unsere vollumfängliche Haupt-Test-Suite auf proprietären Kundendaten und war deshalb nicht öffentlich verfügbar. Der einzige öffentlich verfügbare Teil unseres Testprozesses bestand aus dem Crash-me-Test, einem Perl-DBI/DBD-Benchmark, der im sql-bench-Verzeichnis liegt, und verschiedenen Tests im tests-Verzeichnis. Das Fehlen einer standardisierten, öffentlich verfügbaren Test-Suite machte es unseren Benutzern und auch Entwicklern schwer, Regressionstests auf den MySQL-Code durchzuführen. Um das Problem anzugehen, haben wir ein neues Testsystem geschaffen, das ab Version 3.23.29 den Quell- und Binärdistributionen beiliegt.

Der aktuelle Satz von Testfällen testet nicht alles in MySQL, sollte aber die offensichtlichsten Bugs im SQL-Verarbeitungscode offen legen, sowie Betriebssystem- und Bibliotheks-Probleme, und er testet recht gründlich die Replikation. Unser letztliches Ziel ist es, dass die Tests 100% des Codes abdecken. Beiträge zu unserer Test-Suite sind herzlich willkommen, besonders Tests, die die Funktionalität untersuchen, die für Ihr System kritisch ist, weil das sicherstellt, dass alle zukünftigen MySQL-Releases mit Ihren Applikationen funktionieren.

10.3.2.1. Die MySQL-Test-Suite laufen lassen

Das Testsystem besteht aus einem Test-Sprachinterpreter (mysqltest), einem Shell-Skript, um alle Tests laufen zu lassen tests(mysql-test-run), den eigentlichen Testfällen, die in einer speziellen Testsprache geschrieben sind, und ihren erwarteten Ergebnissen. Um die Test-Suite nach dem Bauen auf Ihrem System laufen zu lassen, geben Sie make test oder mysql-test/mysql-test-run von der Wurzel der Quellinstallation aus ein. Wenn Sie eine Binärdistribution installiert haben, wechseln Sie (cd) zur Wurzel der Installation (zum Beispiel /usr/local/mysql) und geben scripts/mysql-test-run ein. Alle Tests sollten erfolgreich durchlaufen. Wenn nicht, sollten Sie versuchen, den Grund herauszufinden, und das Problem zu berichten, wenn es ein Bug in MySQL ist. See Abschnitt 10.3.2.3, „Bugs in der MySQL-Test-Suite berichten“.

Wenn eine Kopie von mysqld auf Ihrer Maschine läuft, wo Sie die Test-Suite laufen lassen wollen, müssen Sie ihn nicht anhalten, solange er nicht die Ports 9306 und 9307 benutzt. Wenn einer dieser Ports belegt ist, sollten Sie mysql-test-run editieren und die Werte des Master- und / oder Slave-Ports auf verfügbare Ports ändern.

Sie können einen einzelnen Testfall mit mysql-test/mysql-test-run test_name laufen lassen.

Wenn ein Test fehlschlägt, sollten Sie versuchen, mysql-test-run mit der --force-Option laufen zu lassen, um zu prüfen, ob irgend ein weiterer Test fehlschlägt.

10.3.2.2. Die MySQL-Test-Suite erweitern

Sie können die mysqltest-Sprache benutzen, um Ihre eigenen Testfälle zu schreiben. Leider gibt es noch keine komplette Dokumentation dafür - das soll in Kürze aber der Fall sein. Sie können sich jedoch die aktuellen Testfälle ansehen und sie als Beispiel benutzen. Folgende Punkte sollen Ihnen beim Start helfen:

  • Die Tests liegen in mysql-test/t/*.test

  • Ein Testfall besteht aus ;-begrenzten Statements und ist ähnlich der Eingabe in den mysql-Kommandozeilen-Client. Ein Statement ist vorgabemäßig eine Anfrage, die an den MySQL-Server geschickt werden soll, es sei denn, es wird als interner Befehl erkannt (zum Beispiel sleep).

  • Alle Anfragen, die Ergebnisse produzieren, zum Beispiel SELECT, SHOW, EXPLAIN usw., müssen mit @/pfad/zu/ergebnis/datei beginnen. Die Datei muss die erwarteten Ergebnisse enthalten. Eine einfache Art, die Ergebnisdatei zu erzeugen, ist, mysqltest -r < t/test-case-name.test vom mysql-test-Verzeichnis aus laufen zu lassen und dann die erzeugten Ergebnisdateien zu editieren und sie - falls nötig - an die erwartete Ausgabe anzupassen. Seien Sie in diesem Fall sehr vorsichtig, keine unsichtbaren Zeichen hinzuzufügen oder zu löschen - stellen Sie sicher, dass Sie nur den Text ändern und / oder Zeilen löschen. Wenn Sie eine Zeile einfügen müssen, achten Sie darauf, dass die Felder mit einem harten Tabulator-Zeichen getrennt sind und dass es ein hartes Tabulator-Zeichen am Zeilenende gibt. Gegebenfalls sollten Sie od -c benutzen, um sich zu vergewissern, dass Ihr Texteditor beim Editieren nichts durcheinander gebracht hat. Wir hoffen natürlich, dass Sie die Ausgabe von mysqltest -r nie editieren müssen, weil das nur der Fall ist, wenn Sie einen Bug finden.

  • Um mit unserer Einrichtung konsistent zu sein, sollten Sie Ihre Ergebnisdateien ins mysql-test/r-Verzeichnis stellen und sie test_name.result nennen. Wenn der Test mehr als ein Ergebnis erzeugt, sollten Sie test_name.a.result, test_name.b.result usw. verwenden.

  • Wenn ein Statement einen Fehler zurückgibt, sollten Sie die Zeile vor dem Statement mit --error fehler_nummer kennzeichnen. Die Fehlernummer kann eine Auflistung möglicher Fehlerzahlen sein, getrennt durch ','.

  • Wenn Sie einen Replikations-Testfall schreiben, sollten Sie in die erste Zeile der Testdatei source include/master-slave.inc; schreiben. Um zwischen Master und Slave umzuschalten, benutzen Sie connection master; und connection slave;. Wenn Sie etwas auf einer abwechselnden Verbindung machen müssen, können Sie connection master1; für den Master und connection slave1; für den Slave eingeben.

  • Wenn Sie etwas in einer Schleife ausführen müssen, können Sie zum Beispiel folgendes tun:

    let $1=1000;
    while ($1)
    {
     # machen Sie Ihre Anfragen hier
     dec $1;
    }
    

  • Um zwischen Anfragen zu schlafen, benutzen Sie den sleep-Befehl. Er unterstützt Bruchteile von Sekunden, daher können Sie zum Beispiel sleep 1.3; ausführen, um 1,3 Sekunden zu schlafen.

  • Um den Slave für Ihren Testfall mit zusätzlichen Optionen laufen zu lassen, geben Sie diese im Kommandozeilenformat in mysql-test/t/test_name-slave.opt ein. Für den Master geben Sie sie in mysql-test/t/test_name-master.opt ein.

  • Wenn Sie eine Frage zur Test-Suite haben oder einen Testfall beisteuern wollen, schicken Sie eine E-Mail an . Weil die Liste keine Dateianhänge akzeptiert, sollten Sie alle relevanten Dateien per FTP an ftp://support.mysql.com/pub/mysql/Incoming schicken.

10.3.2.3. Bugs in der MySQL-Test-Suite berichten

Wenn Ihre MySQL-Version die Test-Suite nicht fehlerfrei durchläuft, sollten Sie folgendes tun:

  • Schicken Sie keinen Bug-Bericht, bevor Sie so weit wie möglich herausgefunden haben, was schief ging! Benutzen Sie für den Bug-Bericht bitte das mysqlbug-Skript, so dass wir Informationen über Ihr System und die MySQL-Version erhalten. See Abschnitt 2.6.2.3, „Wie man Bugs oder Probleme berichtet“.

  • Stellen Sie sicher, dass die Ausgabe von mysql-test-run beiliegt, sowie alle Inhalte aller .reject-Dateien im mysql-test/r-Verzeichnis.

  • Wenn ein Test in der Test-Suite fehlschlägt, prüfen Sie, ob der Test auch fehlschlägt, wenn er allein laufen gelassen wird:

    cd mysql-test
    mysql-test-run --local test-name
    

    Wenn das fehlschlägt, sollten Sie MySQL mit --with-debug konfigurieren und mysql-test-run mit der --debug-Option laufen lassen. Wenn auch das fehlschlägt, schicken Sie dei Trace-Datei var/tmp/master.trace an ftp://support.mysql.com/pub/mysql/secret, so dass wir sie untersuchen können. Denken Sie bitte daran, eine volle Beschreibung Ihres Systems beizufügen sowie die Version Ihrer mysqld-Binärdatei und wie Sie sie kompiliert haben.

  • Versuchen Sie auch, mysql-test-run mit der --force-Option laufen zu lassen, um zu sehen, ob auch andere Tests fehlschlagen.

  • Wenn Sie MySQL selbst kompiliert haben, sehen Sie im Handbuch nach, wie MySQL auf Ihrer Plattform kompiliert wird, oder benutzen Sie vorzugsweise eine der Binärdateien, die wir für Sie kompiliert haben und die Sie unter http://www.mysql.com/downloads/ finden. Alle unsere Standard-Binärdateien sollten die Test-Suite fehlerfrei durchlaufen!

  • Wenn Sie einen Fehler wie Result length mismatch oder Result content mismatch erhalten, heißt das, dass die Ausgabe des Tests nicht genau mit der erwarteten Ausgabe übereinstimmt. Das könnte ein Bug in MySQL sein, könnte aber auch heißen, dass Ihre mysqld-Version unter bestimmten Umständen leicht abweichende Ausgaben erzeugt.

    Fehlgeschlagene Testergebnisse werden in eine Datei mit demselben Namen wie die Ergebnisdatei, mit der Endung .reject, gestellt. Wenn Ihr Testfall fehlschlägt, sollten Sie ein DIFF beider Dateien vornehmen. Wenn Sie nicht erkennen können, in welcher Hinsicht sie sich unterscheiden, untersuchen Sie beide mit od -c und prüfen Sie auch ihre Längen.

  • Wenn ein Testfall völlig fehlschlägt, sollten Sie die Log-Dateien im mysql-test/var/log-Verzeichnis nach Hinweisen untersuchen, was schief ging.

  • Wenn Sie MySQL mit Debugging kompiliert haben, können Sie versuchen, das zu debuggen, indem Sie mysql-test-run mit den --gdb- und / oder --debug-Optionen laufen lassen. See Abschnitt D.1.2, „Trace-Dateien erzeugen“.

    Wenn Sie MySQL nicht für Debugging kompiliert haben, sollten Sie das besser tun. Geben Sie einfach die --with-debug-Option für configure an! See Abschnitt 3.3, „Installation der Quelldistribution“.


This is a translation of the MySQL Reference Manual that can be found at dev.mysql.com. The original Reference Manual is in English, and this translation is not necessarily as up to date as the English version.