Es gibt zwei Möglichkeiten, MySQL neue Funktionen hinzuzufügen:
CREATE FUNCTION
und DROP
FUNCTION
-Statements hinzugefügt bzw. gelöscht.
See section 10.1.1 CREATE FUNCTION / DROP FUNCTION
-Syntax.
mysqld
-Server kompiliert und stehen
dann dauerhaft zur Verfügung.
Jede Methode hat Vorteile und Nachteile:
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 FUNCTION / DROP FUNCTION
-SyntaxCREATE [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 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)
SQL-Typ | C-/C++-Typ |
STRING | char *
|
INTEGER | long long
|
REAL | double
|
xxx_init()
(optional)
xxx()
. Sie kann für folgendes
benutzt werden:
XXX()
zu prüfen.
REAL
-Funktionen) die maximale Anzahl von Dezimalstellen
anzugeben.
NULL
sein darf oder nicht.
xxx_deinit()
(optional)
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
1.34
,
1.345
und 1.3
übergeben werden, wäre der Vorgabewert 3, weil
1.345
3 Dezimalstellen hat.
unsigned int max_length
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
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
if (args->arg_count != 2) { strcpy(message,"XXX() benoetigt zwei Argumente"); return 1; }
enum Item_result *arg_type
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:
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.
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]);
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
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:
sql_functions[]
-Array definiert.
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.
Item_num_func
oder Item_str_func
erbt, je nachdem, ob Ihre
Funktion eine Zahl oder eine Zeichenkette zurückgibt.
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.
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:
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.)
::str()
-Funktion sollte die Zeichenkette zurückgeben, die das
Ergebnis enthält, oder (char*) 0
, wenn das Ergebnis NULL
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]])
Im Moment ist die einzige Dokumentation hierfür der Quelltext.
Sie finden alle Informationen über Prozeduren, wenn Sie folgende Dateien untersuchen:
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:
process_alarm()
, um
Zeitüberschreitungen auf Verbindungen zu erzwingen, die zu lange im
Leerlauf waren.
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.
--flush_time=#
-Option benutzt, wird ein dedizierter
Thread erzeugt, der alle Tabellen im angegebenen Intervall auf Platte
zurückschreibt.
INSERT DELAYED
benutzt,
erhält ihren eigenen Thread.
--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:
mysql-test/t/*.test
;
-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
).
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.
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.
--error fehler_nummer
kennzeichnen. Die Fehlernummer
kann eine Auflistung möglicher Fehlerzahlen sein, getrennt durch
','
.
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.
let $1=1000; while ($1) { # machen Sie Ihre Anfragen hier dec $1; }
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.
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 Ihre MySQL-Version die Test-Suite nicht fehlerfrei durchläuft, sollten Sie folgendes tun:
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.
mysql-test-run
beiliegt,
sowie alle Inhalte aller .reject
-Dateien im
mysql-test/r
-Verzeichnis.
cd mysql-test mysql-test-run --local test-nameWenn 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.
mysql-test-run
mit der --force
-Option
laufen zu lassen, um zu sehen, ob auch andere Tests fehlschlagen.
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.
mysql-test/var/log
-Verzeichnis nach Hinweisen untersuchen, was
schief ging.
mysql-test-run
mit den --gdb
- und / oder
--debug
-Optionen laufen lassen.
See section 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 section 3.3 Installation der Quelldistribution.
Go to the first, previous, next, last section, table of contents.