manual.de_Extending_MySQL.html
MySQL-Referenzhandbuch für Version 4.1.1-alpha - 10 MySQL erweitern
Go to the first, previous, next, last section, table of contents.
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 section 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
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 section 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 section 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. See section B Beigesteuerte Programme. 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 ... Prozeduranalyse([max elements,[max memory]])
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'
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 section 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 section 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 =1000;
while ()
{
# machen Sie Ihre Anfragen hier
dec ;
}
-
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 section 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 section E.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 section 3.3 Installation der Quelldistribution.
Go to the first, previous, next, last section, table of contents.
|