Inhaltsverzeichnis
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.
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.
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-Typ | C-/C++-Typ |
STRING | char * |
INTEGER | long long |
REAL | double |
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.
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.
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).
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;
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.)
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:
Fügen Sie lex.h eine neue Zeile hinzu,
die den Funktionsnamen im
sql_functions[]-Array definiert.
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.
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.
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.
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!
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.
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]])
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
<internals@lists.mysql.com> abonnieren. Das ist eine
Liste mit relativ geringem Verkehr, verglichen mit
<mysql@lists.mysql.com>.
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.
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.
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.
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
<Interna@lists.mysql.com>. Weil die Liste
keine Dateianhänge akzeptiert, sollten Sie alle
relevanten Dateien per FTP an
ftp://support.mysql.com/pub/mysql/Incoming
schicken.
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.