Inhaltsverzeichnis
In diesem Kapitel wird vieles beschrieben, was Sie wissen müssen,
wenn Sie am Code von MySQL arbeiten möchten. Wenn Sie selbst zu
der Entwicklung von MySQL beitragen wollen, Zugriff auf den
allerneuesten Entwicklungsstand des Codes zwischen zwei Versionen
benötigen oder einfach nur die Entwicklung mitverfolgen möchten,
befolgen Sie bitte die Anleitungen unter
Abschnitt 2.8.3, „Installation vom Entwicklungs-Source-Tree“. Wenn Sie sich für die
Interna von MySQL interessieren, sollten Sie auch unsere
internals
-Mailingliste abonnieren. Auf dieser
Liste herrscht relativ wenig Verkehr. Wie Sie sie abonnieren
können, erfahren Sie unter Abschnitt 1.7.1, „Die MySQL-Mailinglisten“. Alle
Entwickler bei MySQL AB sind auf der
internals
-List und wir helfen gerne auch
anderen weiter, die am MySQL-Code arbeiten. Bitte nutzen Sie diese
Liste, um Fragen über den Code zu stellen und Patches zu
übermitteln, falls Sie gerne selbst zu dem MySQL-Projekt
beitragen möchten!
Der MySQL Server erzeugt folgende Threads:
Der TCP/IP-Verbindungs-Thread behandelt alle Verbindungsanfragen und erzeugt für jede Verbindung einen neuen dedizierten Thread zur Authentifizierung und Verarbeitung von SQL-Anfragen.
Auf Windows NT gibt es einen Handler-Thread für die Named Pipe, der für Named-Pipe-Verbindungsanfragen dieselbe Arbeit wie der TCP/IP-Verbindungs-Thread leistet.
Der Signal-Thread kümmert sich um Signale und normalerweise
auch um Alarmsignale und
process_alarm()
-Aufrufe, um Verbindungen,
die zu lange ungenutzt waren, per Timeout zu beenden.
Wenn mysqld mit
-DUSE_ALARM_THREAD
kompiliert wird, wird
ein spezieller Thread für den Umgang mit Alarmen erzeugt.
Das wird aber nur bei Systemen getan, auf denen es Probleme
mit sigwait()
gibt, oder wenn Sie den
Code der Funktion thr_alarm()
in Ihrer
Anwendung ohne einen dedizierten Thread zur Signalbehandlung
einsetzen möchten.
Wenn die Option
--flush_time=
gesetzt ist, wird ein dedizierter Thread erzeugt, der in den
gegebenen Abständen alle Tabellen auf die Platte
zurückschreibt.
val
Jede Verbindung besitzt ihren eigenen Thread.
Jede Tabelle, für die INSERT DELAYED
gilt, bekommt ihren eigenen Thread.
Wenn Sie die Option --master-host
setzen,
wird ein Thread für die Slave-Replikation gestartet, um
Updates vom Master zu lesen und anzuwenden.
Der Befehl mysqladmin processlist zeigt nur
den Verbindungs-Thread, den INSERT
DELAYED
-Thread und den Replikations-Thread an.
Mit dem in den Unix-Quell- und Binärdistributionen enthaltenen Testsystem können Nutzer und Entwickler Regressionstests mit dem MySQL-Code durchführen. Diese Tests kann man auf Unix, aber zurzeit noch nicht in einer nativen Windows-Umgebung laufen lassen.
Die momentan vorhandenen Testfälle testen nicht alles in MySQL, sollten aber die offensichtlichsten Fehler im Code zur SQL-Verarbeitung sowie OS/Bibliothek-Probleme finden, und leisten ziemlich gründliche Arbeit beim Testen von Replikation. Unser Ziel ist es, letztlich 100 % des Codes mit unseren Tests abzudecken. Wir freuen uns über Beiträge zu unserer Testreihe. Insbesondere können Sie uns Tests für systemkritische Funktionalitäten schicken, um zu gewährleisten, dass alle zukünftigen MySQL-Releases auch mit Ihren Anwendungen harmonieren.
Das Testsystem besteht aus einem Testsprachen-Interpreter
(mysqltest), einem Shell-Skript, um alle
Tests auszuführen (mysql-test-run), den
eigentlichen Testfällen, die in einer speziellen Testsprache
geschrieben sind, sowie aus den erwarteten Ergebnissen. Um die
Testreihe nach einem Build auf Ihrem System auszuführen,
geben Sie make test oder
mysql-test/mysql-test-run im
Wurzelverzeichnis des Quellcodes ein. Wenn Sie eine
Binärdistribution installiert haben, wechseln Sie in das
Wurzelverzeichnis der Installation (beispielsweise
/usr/local/mysql
) und führen
scripts/mysql-test-run aus. Alle Tests
sollten erfolgreich laufen. Ist das nicht der Fall, müssen
Sie die Gründe herausfinden und einen Bugreport übermitteln,
wenn es sich um einen Fehler in MySQL handelt. Siehe
Abschnitt 26.1.2.3, „Berichten von Bugs in der MySQL-Test-Suite“.
Wenn eine Instanz von mysqld auf dem
Computer läuft, auf dem Sie die Testreihe ausführen
möchten, müssen Sie diese nur dann herunterfahren, wenn sie
den Port 9306
oder 9307
belegt. Wenn einer dieser Ports nicht frei ist, müssen Sie in
mysql-test-run die Werte des Master- oder
Slave-Ports umändern und einen Port einsetzen, der frei ist.
Sie können einen einzelnen Testfall mit mysql-test/mysql-test-run test_name ausführen.
Wenn ein Test scheitert, führen Sie
mysql-test-run mit der Option
--force
aus, um zu prüfen, ob auch andere
Tests fehlschlagen.
Mit der Sprache mysqltest können Sie auch Ihre eigenen Testfälle schreiben. Allerdings haben wir leider die Dokumentation dieser Sprache noch nicht ganz fertig gestellt. Sie können sich jedoch unsere aktuellen Testfälle anschauen und als Beispiele heranziehen. Die folgenden Hinweise müssten Ihnen bei den ersten Schritten eine Hilfe sein:
Sie finden die Tests in
mysql-test/t/*.test
.
Ein Testfall besteht aus Anweisungen, die mit
;
abgeschlossen werden, und ähnelt der
Eingabe für den
mysql-Kommandozeilen-Client. Eine
Anweisung ist standardmäßig eine SQL-Anweisung, die an
den MySQL Server gesandt wird, es sei denn, sie wird als
interner Befehl erkannt (wie beispielsweise
sleep).
Vor allen Anfragen, die Ergebnisse produzieren, wie
beispielsweise SELECT
,
SHOW
oder EXPLAIN
,
muss ein @/path/to/result/file
stehen. Diese Datei muss die erwarteten Ergebnisse
enthalten. Ein einfaches Mittel, um die Ergebnisdatei zu
generieren, besteht darin, mysqltest -r <
t/test-case-name.test im Verzeichnis
mysql-test
auszuführen und dann die
generierten Ergebnisdateien wenn nötig zu bearbeiten, um
sie an die erwartete Ausgabe anzupassen. In diesem Fall
müssen Sie sehr genau darauf achten, keine unsichtbaren
Zeichen zu löschen oder hinzuzufügen. Sie dürfen nur
den Text ändern oder Zeilen löschen. Wenn Sie eine Zeile
einfügen müssen, achten Sie darauf, dass die Felder
durch einen Tabulator getrennt werden, und setzen Sie auch
am Ende einen Tabulator. Mit od -c
können Sie sich vergewissern, dass Ihr Editor bei der
Bearbeitung keinen Unsinn gemacht hat. Wir hoffen, dass
Sie niemals die Ausgabe von mysqltest
-r bearbeiten müssen, da dies nur nötig wird,
wenn Sie einen Fehler finden.
Um Ihr System wie unseres einzurichten, legen Sie die
Ergebnisdateien in das Verzeichnis
mysql-test/r
und nennen sie
test_name.result
. Wenn der Test mehr
als ein Ergebnis produziert, verwenden Sie die Namen
test_name.a.result
,
test_name.b.result
und so weiter.
Wenn eine Anweisung einen Fehler zurückgibt, sollten Sie
diesen mit --error error-number
auf der
Zeile vor dieser Anweisung angeben. Die Fehlernummer kann
auch eine kommagetrennte Liste von Fehlernummern sein.
Wenn Sie einen Replikationstestfall schreiben, setzen Sie
in die erste Zeile der Testdatei den Text source
include/master-slave.inc;
. Mit
connection master;
und
connection slave;
schalten Sie zwischen
Master und Slave um. Wenn Sie auf einer anderen Verbindung
arbeiten müssen, können Sie für den Master
connection master1;
und für den Slave
connection slave1;
eingeben.
Falls Sie eine Schleifenverarbeitung ausführen müssen, können Sie Folgendes tun:
let $1=1000; while ($1) { # Hier Anfragen einfügen dec $1; }
Mit sleep schläft das System zwischen zwei Anfragen. Da in diesem Befehl auch Sekundenbruchteile eingestellt werden können, bedeutet beispielsweise sleep 1.3;, dass das System 1,3 Sekunden schläft.
Um den Slave mit Zusatzoptionen für den Testfall
auszuführen, setzen Sie die Optionen in einem
Kommandozeilenformat in die Datei
mysql-test/t/test_name-slave.opt
bzw.
für den Master in die Datei
mysql-test/t/test_name-master.opt
.
Wenn Sie Fragen zu der Testreihe haben oder selbst einen
Test beisteuern möchten, schicken Sie eine E-Mail an die
MySQL-Mailingliste internals
(siehe
Abschnitt 1.7.1, „Die MySQL-Mailinglisten“). Da diese Liste keine
Anhänge akzeptiert, laden Sie alle relevanten Dateien per
FTP auf folgende Adresse hoch:
ftp://ftp.mysql.com/pub/mysql/upload/.
Wenn Ihre MySQL-Version die Testreihe nicht besteht, tun Sie Folgendes:
Senden Sie uns bitte erst dann einen Bugreport, wenn Sie so viel wie möglich über das Problem herausgefunden haben! Schauen Sie bitte auch in die Anleitungen unter Abschnitt 1.8, „Wie man Bugs oder Probleme meldet“.
Speichern Sie die Ausgabe von
mysql-test-run sowie die Inhalte
sämtlicher .reject
-Dateien im
Verzeichnis mysql-test/r
.
Wenn ein Test in der Testreihe fehlschlägt, überprüfen Sie, ob der Test auch dann scheitert, wenn er isoliert ausgeführt wird:
cd mysql-test mysql-test-run --local test-name
Wenn dies scheitert, sollten Sie MySQL mit der Option
--with-debug
konfigurieren und
mysql-test-run mit der Option
--debug
ausführen. Wenn auch das
fehlschlägt, senden Sie die Trace-Datei
var/tmp/master.trace
an
ftp://ftp.mysql.com/pub/mysql/upload/,
damit wir sie untersuchen können. Bitte senden Sie uns
außerdem eine vollständige Beschreibung Ihres Systems,
die Version Ihrer mysqld-Binary sowie
Informationen, wie Sie sie kompiliert haben.
Lassen Sie mysql-test-run mit der
--force
-Option laufen, um zu erfahren, ob
auch ein anderer Test scheitert.
Wenn Sie MySQL selbst kompiliert haben, schlagen Sie in unserem Handbuch nach, wie MySQL auf Ihrer Plattform kompiliert wird, oder – noch besser – verwenden Sie eine der Binaries, die wir für Sie bereits vokompiliert haben (siehe http://dev.mysql.com/downloads/). Alle unsere Standardbinaries dürften die Testreihe überstehen!
Wenn Sie eine Fehlermeldung wie Result length
mismatch
oder Result content
mismatch
bekommen, so bedeutet dies, dass die
Testausgabe nicht mit der erwarteten Ausgabe
übereinstimmt. Dies könnte ein Bug in MySQL sein oder
aber daran liegen, dass Ihre
mysqld-Version unter bestimmten
Umständen abweichende Ergebnisse bringt.
Die Ergebnisse von gescheiterten Tests werden in eine
Datei gespeichert, die genau wie die Ergebnisdatei heißt,
aber die Erweiterung .reject
hat. Wenn
Ihr Testfall scheitert, lassen Sie einen Diff-Befehl auf
den beiden Dateien laufen. Ist nicht zu erkennen, worin
sie sich unterscheiden, so untersuchen Sie beide mit
od -c
und vergleichen auch ihre
Längen.
Scheitert ein Test vollständig, suchen Sie in den
Logdateien im Verzeichnis
mysql-test/var/log
nach Gründen.
Haben Sie MySQL mit Debugging kompiliert, so können Sie
versuchen, mysql-test-run mit der
Option --gdb
und/oder
--debug
auszuführen. Siehe auch
Abschnitt E.1.2, „Trace-Dateien erzeugen“.
Wurde MySQL nicht mit Debugging kompiliert, so sollten Sie
dies nun nachholen. Dazu müssen Sie lediglich
configure mit den
--with-debug
-Optionen ausführen. Siehe
Abschnitt 2.8, „Installation der Quelldistribution“.
MySQL 5.1 und höher unterstützt eine Plug-In-API, mit der Serverkomponenten zur Laufzeit ge- oder entladen werden können, ohne den Server neu zu starten. Zurzeit unterstützt diese API die Erstellung von Volltext-Parser-Plug-Ins. Durch ein solches Plug-In lässt sich der eingebaute Volltext-Parser ersetzen oder verbessern. So kann beispielsweise ein Plug-In andere Regeln als der eingebaute Parser verwenden, um Text in Wörter zu parsen. Das kann nützlich sein, wenn Sie Text parsen möchten, der andere Merkmale hat, als sie der eingebaute Parser erwartet.
Die Plug-In-API soll Nachfolger der älteren Schnittstelle für benutzerdefinierte Funktionen (UDFs) sein. Im Endausbau wird sie auch eine API für die Erstellung von UDFs enthalten und die ältere, nicht als Plug-In geschriebene UDF-API ersetzen. Von da an wird es möglich sein, UDFs so zu überarbeiten, dass sie als Plug-In-UDFs benutzt werden können und somit auch von den besseren Sicherheits- und Versionierungsfähigkeiten der Plug-In-API profitieren. Im Anschluss daran wird der Support für die alte UDF-API auslaufen.
Die Plug-In-API benötigt die plugin
-Tabelle in
der mysql
-Datenbank. Diese Tabelle wird im
Installationsprozess von MySQL angelegt. Wenn Sie von einer
älteren Version auf MySQL 5.1 aufrüsten, erstellen Sie diese
Tabelle mit dem Befehl
mysql_fix_privilege_tables. Siehe
Abschnitt 5.6, „mysql_fix_privilege_tables — Upgrade von MySQL-Systemtabellen“.
In mancher Hinsicht ähnelt die Plug-In-API der älteren UDF-API, die sie ersetzen soll, aber sie bietet eine Reihe von Vorteilen gegenüber ihrer Vorläuferin:
Das Plug-In-Framework ist erweiterbar, sodass es verschiedene Arten von Plug-Ins aufnehmen kann.
Manche Aspekte hat die Plug-In-API mit allen Plug-Ins gemeinsam, aber sie gestattet darüber hinaus auch typspezifische Schnittstellenelemente, sodass unterschiedliche Arten von Plug-Ins erstellt werden können. Ein Plug-In, das einem bestimmten Zweck dient, kann somit die für seine Erfordernisse geeignetste Schnittstelle haben, anstatt sich an den Bedürfnissen eines anderen Plug-In-Typs zu orientieren.
Auch wenn zurzeit nur die Schnittstelle für Volltext-Parser-Plug-Ins implementiert ist, können noch andere hinzukommen, wie beispielsweise eine Schnittsstelle für UDF-Plug-Ins.
Zur Plug-In-API gehören auch Versionsinformationen.
Durch die Versionsinformationen der Plug-In-API kann sich eine Plug-In-Bibliothek sowie jedes in ihr enthaltene Plug-In selbst identifizieren, und zwar mithilfe der API-Version, die zur Erstellung der Bibliothek benutzt wurde. Wenn sich die API mit der Zeit ändert, ändern sich auch die Versionsnummern, aber der Server kann immer anhand der Versionsinformationen einer konkreten Plug-In-Bibliothek herausfinden, ob er die Plug-Ins dieser Bibliothek unterstützt.
Es gibt zwei Arten von Versionsnummern. Die erste ist die Version des allgemeinen Plug-In-Frameworks selbst. Jede Plug-In-Bibliothek enthält diese Art von Versionsnummer. Die zweite ist die Nummer des einzelnen Plug-Ins. Jeder spezifische Typ von Plug-In in einer Bibliothek hat für seine Schnittstelle eine Versionsnummer, sodass jedes Plug-In in einer Bibliothek eine typspezifische Versionsnummer besitzt. So hat beispielsweise eine Bibliothek, in der ein Volltext-Parser-Plug-In liegt, eine allgemeine Plug-In-API-Versionsnummer und das einzelne Plug-In hat seinerseits eine Versionsnummer, die für ebendiese Volltext-Plug-In-Schnittstelle spezifisch ist.
Die Sicherheit der Plug-Ins wurde gegenüber der alten UDF-Schnittstelle verbessert.
Die ältere Schnittstelle zur Erstellung von UDFs ohne Plug-In ermöglichte es, Bibliotheken aus jedem Verzeichnis zu laden, das der dynamische Linker des Systems durchsuchte, und die Symbole zur Identifikation der UDF-Bibliothek waren relativ unspezifisch. Die neueren Regeln sind strenger. Eine Plug-In-Bibliothek muss in einem speziellen dedizierten Verzeichnis installiert sein, dessen Speicherort vom Server kontrolliert wird und zur Laufzeit nicht geändert werden kann. Außerdem muss die Bibliothek bestimmte Symbole enthalten, die sie als Plug-In-Bibliothek kennzeichnen. Der Server wird nichts als Plug-In laden, das nicht als Plug-In gebaut wurde.
Die neuere Plug-In-Schnittstelle löst die Sicherheitsprobleme der älteren UDF-Schnittstelle. Wenn ein UDF-Plug-In-Typ implementiert wird, können Nicht-Plug-In-UDFs in das neue Framework überführt werden und die alte Schnittstelle kann auslaufen.
Die Plug-In-Implementierung besteht aus folgenden Komponenten:
Quelldateien (die angegebenen Speicherorte gelten für eine MySQL-Quelldistribution):
include/plugin.h
stellt die
öffentliche Plug-In-API zur Verfügung. Jeder, der eine
Plug-In-Bibliothek schreiben möchte, sollte sich diese
Datei anschauen.
sql/sql_plugin.h
und
sql/sql_plugin.cc
enthalten die interne
Plug-In-Implementierung. Diese Dateien müssen Verfasser von
Plug-Ins sich nicht anschauen. Nur wenn Sie mehr darüber
erfahren möchten, wie der Server mit Plug-Ins umgeht, sind
diese Dateien für Sie interessant.
Systemtabelle:
Die plugin
-Tabelle in der
mysql
-Datenbank listet alle installierten
Plug-Ins auf und ist für die Nutzung von Plug-Ins
erforderlich. Neuere MySQL-Versionen legen diese Tabelle bei
der Installation an. Wenn Sie von einer älteren Version als
MySQL 5.1 aufrüsten, sollten Sie mit
mysql_fix_privilege_tables Ihre
Systemtabellen aktualisieren und die
plugin
-Tabelle anlegen.
SQL-Anweisungen:
INSTALL PLUGIN
registriert ein Plug-In in
der plugin
-Tabelle und lädt den
Plug-In-Code.
UNINSTALL PLUGIN
deregistriert ein
Plug-In bei der plugin
-Tabelle und und
entlädt den Plug-In-Code.
Die WITH PARSER
-Klausel für die
Erstellung von Volltextindizes verbindet ein
Volltext-Parser-Plug-In mit einem gegebenen
FULLTEXT
-Index.
SHOW PLUGIN
zeigt Informationen über
bekannte Plug-Ins an. Die PLUGINS
-Tabelle
in INFORMATION_SCHEMA
liefert ebenfalls
Informationen über Plug-Ins.
Systemvariable:
plugin_dir
zeigt an, wo im Verzeichnis
alle Plug-Ins installiert werden müssen. Den Wert dieser
Variablen können Sie beim Serverstart mit der Option
--plugin_dir=
angeben.
path
MySQL verfügt über einen eingebauten Parser, der nach Voreinstellung für Volltextoperationen eingesetzt wird (Text parsen, der indiziert werden soll, oder einen Anfrage-String parsen, um die Suchbegriffe herauszufinden). In der Volltextverarbeitung bedeutet „Parsen“, dass Wörter aus einem Text oder Anfrage-String anhand von Regeln extrahiert werden, die definieren, aus welcher Zeichenfolge ein Wort besteht und wo die Wortgrenzen liegen.
Beim Parsen für Indizierungszwecke übergibt der Parser jedes Wort an den Server, und dieser fügt das Wort einem Volltextindex hinzu. Beim Parsen eines Anfrage-Strings übergibt der Parser ebenfalls jedes Wort an den Server, und dieser sammelt die Wörter, um sie für eine Suchoperation zusammenzustellen.
Die Parsing-Eigenschaften des eingebauten Volltext-Parsers
werden in Abschnitt 12.7, „MySQL-Volltextsuche“, beschrieben. Hierzu
gehören auch die Regeln, nach denen Wörter aus einem Text
herausgezogen werden. Der Parser wird von bestimmten
Systemvariablen wie ft_min_word_len
und
ft_max_word_len
beeinflusst, die kürzere
oder längere Wörter ausschließen können, und durch die Liste
der Stoppwörter (diese sind häufig vorkommende Wörter, die
übergangen werden).
Durch die Plug-In-API sind Sie in der Lage, einen eigenen Volltext-Parser zu verwenden und somit die Grundaufgaben eines Parsers unter Kontrolle zu haben. Ein Parser-Plug-In kann zwei Rollen spielen:
Es kann den eingebauten Parser ersetzen. In dieser Rolle liest das Plug-In die zu parsende Eingabe, zerlegt sie in Wörter und übergibt diese Wörter an den Server (entweder zum Indizieren oder zum Sammeln von Wörtern).
Sie könnten einen Parser auf diese Weise einsetzen, wenn Sie die Eingabe nach anderen Regeln als der eingebaute Parser in Wörter zerlegen möchten. Für den eingebauten Parser besteht beispielsweise der Text „case-sensitive“ aus zwei Wörtern, nämlich „case“ und „sensitive“, während eine Anwendung diesen Text möglicherweise als ein einziges Wort ansehen sollte.
Das Plug-In kann auch mit dem eingebauten Parser
zusammenarbeiten, indem es als Frontend für diesen dient.
In dieser Rolle extrahiert das Plug-In Text aus der Eingabe
und übergibt ihn an den Parser, der seinerseits den Text
nach seinen normalen Parsing-Regeln in Wörter zerlegt.
Diese Art von Parsing wird besonders von den Systemvariablen
ft_
und der
Stoppwörterliste beeinflusst.
xxx
Auf diese Weise könnten Sie einen Parser einsetzen, wenn
Sie beispielsweise PDF- oder XML-Dokumente oder
.doc
-Dateien indizieren müssen. Der
eingebaute Parser ist nicht für diese Arten von Dokumenten
gedacht, aber ein Plug-In-Parser kann Text aus diesen
Quellen extrahieren und an den eingebauten Parser
übergeben.
Ein Parser-Plug-In kann auch in beiden Rollen aktiv sein. Es kann Text aus einer Eingabe holen, die kein einfaches Textdokument ist (die Frontend-Rolle), und diesen Text auch in Wörter zerlegen (also den eingebauten Parser ersetzen).
Ein Volltext-Plug-In ist mit Volltextindizes indexweise
verbunden: Wenn Sie ein Parser-Plug-In installieren, wird es
deswegen noch nicht für Volltextoperationen benutzt, sondern
steht zunächst einmal nur zur Verfügung. Ein Volltext-Parser
kann dann beispielsweise in einer WITH
PARSER
-Klausel bei der Erstellung einzelner
FULLTEXT
-Indizes angegeben werden. Um einen
solchen Index gleichzeitig mit der Tabelle zu erstellen, tun Sie
Folgendes:
CREATE TABLE t ( doc CHAR(255), FULLTEXT INDEX (doc) WITH PARSER my_parser );
Oder Sie fügen den Index nach der Erstellung der Tabelle hinzu:
ALTER TABLE t ADD FULLTEXT INDEX (doc) WITH PARSER my_parser;
Um den Parser mit dem Index zu verbinden, müssen Sie lediglich
die WITH PARSER
-Klausel in die SQL-Anfrage
einfügen. Suchoperationen werden wie immer formuliert, ohne die
Anfragen in irgendeiner Weise zu ändern.
Wenn Sie ein Parser-Plug-In mit einem
FULLTEXT
-Index verbinden, ist dieses Plug-In
erforderlich, um den Index nutzen zu können. Wird es gelöscht,
wird jeder mit ihm verbundene Index unbenutzbar. Jeder Versuch,
ihn in einer Tabelle zu verwenden, für die kein Plug-In zur
Verfügung steht, löst einen Fehler aus. Nur DROP
TABLE
ist nach wie vor möglich.
INSTALL PLUGINplugin_name
SONAME 'plugin_library
'
Mit dieser Anweisung wird ein Plug-In installiert.
plugin_name
ist der Name des
Plug-Ins, wie er in der Plug-In-Deklarationsstruktur in der
Bibliotheksdatei festgelegt ist. Ob in Plug-In-Namen die Groß-
und Kleinschreibung beachtet wird, hängt von der
Dateinamensemantik des Hostsystems ab.
plugin_library
ist der Name der
Shared Library, die den Plug-In-Code enthält. Dieser Name
umfasst auch die Dateinamenserweiterung (beispielsweise
libmyplugin.so
oder
libmyplugin.dylib
).
Die Shared Library muss im Plug-In-Verzeichnis liegen (also in
dem Verzeichnis, das die Systemvariable
plugin_dir
angibt). Die Bibliothek muss in
dem Plug-In-Verzeichnis selbst und nicht in einem
Unterverzeichnis liegen. Nach Voreinstellung ist
plugin_dir
das Verzeichnis, welches die
Konfigurationsvariable pkglibdir
vorgibt,
aber es kann auch durch Einstellen des Werts von
plugin_dir
beim Serverstart geändert werden.
Das folgende Beispiel zeigt, wie der Wert in einer
my.cnf
-Datei gesetzt wird:
[mysqld]
plugin_dir=/path/to/plugin/directory
Ist der Wert von plugin_dir
ein relativer
Pfadname, wird er mit Bezug auf das MySQL-Basisverzeichnis
interpretiert (dieses ist in der Systemvariablen
basedir
festgelegt).
INSTALL PLUGIN
fügt der
mysql.plugin
-Tabelle eine Zeile mit einer
Beschreibung des plugin
s hinzu. Diese Tabelle
enthält den Namen des Plug-Ins und der Bibliotheksdatei.
INSTALL PLUGIN
lädt und initialisiert auch
den Plug-In-Code, um das Plug-In nutzbar zu machen. Um ein
Plug-In zu initialisieren, wird seine Initialisierungsfunktion
ausgeführt, die alle Einstellungen vornimmt, welche zur
Benutzung des Plug-Ins erforderlich sind.
Für INSTALL PLUGIN
benötigen Sie das
INSERT
-Recht für die
mysql.plugin
-Tabelle.
Beim Serverstart lädt und initialisiert der Server alle in der
mysql.plugin
-Tabelle aufgelisteten Plug-Ins.
Dies bedeutet, dass ein Plug-In mit INSTALL
PLUGIN
nur einmalig installiert wird und nicht bei
jedem Serverstart. Das Laden von Plug-Ins beim Hochfahren des
Servers funktioniert nicht, wenn der Server mit der Option
--skip-grant-tables
gestartet wird.
Wenn der Server herunterfährt, führt er die Deinitialisierungsfunktion für jedes geladene Plug-In aus, damit dieses Gelegenheit zu eventuellen Aufräumarbeiten bekommt.
Um ein Plug-In vollständig zu entfernen, führen Sie die
UNINSTALL PLUGIN
-Anweisung aus.
Die SHOW PLUGIN
-Anweisung verrät Ihnen,
welche Plug-Ins installiert sind.
Wenn Sie eine Plug-In-Bibliothek neu kompilieren und neu installieren müssen, können Sie aus folgenden Methoden wählen:
Sie deinstallieren alle Plug-Ins der Bibliothek mit
UNINSTALL PLUGIN
, installieren die neue
Plug-In-Bibliotheksdatei in das Plug-In-Verzeichnis und
installieren dann mit INSTALL PLUGIN
alle
Plug-Ins in der Bibliothek. Diese Vorgehensweise hat den
Vorteil, dass man den Server nicht anhalten muss. Wenn
jedoch die Plug-In-Bibliothek viele Plug-Ins enthält,
müssen Sie viele INSTALL PLUGIN
- und
UNINSTALL PLUGIN
-Anweisungen geben.
Alternativ können Sie den Server anhalten, die neue Plug-In-Bibliotheksdatei in das Plug-In-Verzeichnis installieren und dann den Server neu starten.
UNINSTALL PLUGIN plugin_name
Diese Anweisung entfernt ein installiertes Plug-In. Sie können ein Plug-In nicht entfernen, wenn es von einer Tabelle, die offen ist, noch gebraucht wird.
Der plugin_name
muss der Name eines
in der mysql.plugin
-Tabelle aufgeführten
Plug-Ins sein. Der Server führt die Deinitialisierungsfunktion
des Plug-Ins aus und entfernt die Zeile für dieses Plug-In aus
der mysql.plugin
-Tabelle, damit es künftig
beim Hochfahren des Servers nicht mehr geladen und initialisiert
wird. UNINSTALL PLUGIN
entfernt allerdings
nicht die Dateien des Plug-Ins aus der Shared Library.
Um UNINSTALL PLUGIN
benutzen zu können,
benötigen Sie das DELETE
-Recht für die
mysql.plugin
-Tabelle.
Die Entfernung von Plug-Ins hat Folgen für die Benutzung von
Tabellen, die mit diesem Plug-In verbunden sind. Wenn
beispielsweise ein Volltext-Parser-Plug-In mit einem
FULLTEXT
-Index auf der Tabelle verbunden ist,
wird die Tabelle durch Deinstallation des Plug-Ins unbenutzbar.
Jeder Versuch, auf sie zuzugreifen, löst einen Fehler aus. Sie
kann noch nicht einmal mehr geöffnet werden, um den Index zu
entfernen, der das Plug-In benutzt. Also sollten Sie bei der
Deinstallation von Plug-Ins vorsichtig sein, wenn Ihnen Ihre
Tabelleninhalte wichtig sind. Wenn Sie ein Plug-In
deinstallieren, das Sie nicht wieder benutzen möchten, aber
sich noch für den Inhalt der zugehörigen Tabelle
interessieren, sollten Sie die Tabelle mit
mysqldump kopieren und die WITH
PARSER
-Klausel aus der gespeicherten CREATE
TABLE
-Anweisung entfernen, damit Sie die Tabelle
später erneut laden können. Wenn Ihnen die Tabelle egal ist,
können Sie sie mit DROP TABLE
auch dann noch
löschen, wenn Ihre Plug-Ins nicht mehr vorhanden sind.
Dieser Abschnitt beschreibt die allgemeinen und typspezifischen
Teile der Plug-In-API. Außerdem wird Schritt für Schritt
erklärt, wie man eine Plug-In-Bibliothek erstellt. Ein Beispiel
für Plug-In-Quellcode finden Sie im
plugin/fulltext
-Verzeichnis einer
MySQL-Quelldistribution.
Sie können Plug-Ins in C oder C++ schreiben. Da die Plug-Ins dynamisch ge- und entladen werden, muss Ihr Betriebssystem dynamisches Laden unterstützen und auch mysqld dynamisch (nicht statisch) kompiliert worden sein.
Ein Plug-In enthält Code, der Teil des laufenden Servers wird.
Daher gelten für das Schreiben von Plug-Ins dieselben
Einschränkungen wie für Servercode. Sie bekommen
beispielsweise Schwierigkeiten, wenn Sie versuchen, Funktionen
aus der libstdc++
-Bibliothek zu benutzen.
Beachten Sie, dass sich diese Einschränkungen in zukünftigen
Versionen des Servers noch ändern können, sodass bei einem
Server-Upgrade unter Umständen auch Plug-Ins überarbeitet
werden müssen, die ursprünglich für ältere Server erstellt
wurden. Informationen über diese Einschränkungen finden Sie in
Abschnitt 2.8.2, „Typische configure-Optionen“, und
Abschnitt 2.8.4, „Probleme beim Kompilieren?“.
Jedes Plug-In muss eine allgemeine Plug-In-Deklaration
besitzen. Die Deklaration entspricht der
st_mysql_plugin
-Struktur in der
plugin.h
-Datei:
struct st_mysql_plugin { int type; /* Der Plug-In-Typ (ein MYSQL_XXX_PLUGIN-Wert) */ void *info; /* Zeiger auf den typspezifischen Plug-In-Deskriptor */ const char *name; /* Plug-In-Name */ const char *author; /* Plug-In-Autor (für SHOW PLUGINS) */ const char *descr; /* Allgemeine Beschreibung (für SHOW PLUGINS ) */ int (*init)(void); /* Die beim Laden des Plug-Ins aufgerufene Funktion */ int (*deinit)(void); /* Die beim Entladen des Plug-Ins aufgerufene Funktion */ };
Die st_mysql_plugin
-Struktur ist allen
Plug-In-Typen gemein. Ihre Bestandteile sollten
folgendermaßen ausgefüllt werden:
type
Der Plug-In-Typ. Dieser muss einer der Plug-In-Typwerte
aus plugin.h
sein. Für ein
Volltext-Parser-Plug-In ist der
type
-Wert
MYSQL_FTPARSER_PLUGIN
.
info
Ein Zeiger auf den Deskriptor für das Plug-In. Anders als die allgemeine Plug-In-Deklarationsstruktur hängt die Struktur dieses Deskriptors von dem konkreten Plug-In-Typ ab. Jeder Deskriptor hat eine Versionsnummer, die auf die API-Version für diesen Plug-In-Typ verweist, sowie andere erforderliche Bestandteile. Der Deskriptor für Volltext-Plug-Ins ist in Abschnitt 26.2.5.2, „Typspezifische Plug-In-Strukturen und -Funktionen“, beschrieben.
name
Der Name des Plug-Ins. Dieser wird in der
plugin
-Tabelle aufgeführt und in
SQL-Anweisungen wie INSTALL PLUGIN
und
UNINSTALL PLUGIN
zur Benennung des
Plug-Ins verwendet.
author
Der Autor des Plug-Ins. Kann alles sein, was Sie wollen.
desc
Eine allgemeine Beschreibung des Plug-Ins. Kann alles sein, was Sie wollen.
init
Eine nur einmalig benutzte Initialisierungsfunktion. Diese
wird ausgeführt, wenn das Plug-In geladen wird, was bei
INSTALL PLUGIN
oder für Plug-Ins aus
der plugin
-Tabelle beim Serverstart
geschieht. Diese Funktion hat keine Argumente und gibt bei
Erfolg null und bei Misserfolg einen von null
verschiedenen Wert zurück.
deinit
Eine nur einmalig benutzte Deinitialisierungsfunktion.
Diese wird ausgeführt, wenn das Plug-In entladen wird,
was bei UNINSTALL PLUGIN
oder für
Plug-Ins aus der plugin
-Tabelle beim
Server-Shutdown geschieht. Diese Funktion hat keine
Argumente und gibt bei Erfolg null und bei Misserfolg
einen von null verschiedenen Wert zurück
Die Funktionen init
und
deinit
in der allgemeinen
Plug-In-Deklaration werden nur beim Laden und Entladen des
Plug-Ins aufgerufen. Sie haben nichts mit einer Benutzung des
Plug-Ins zu tun, wie sie vorliegt, wenn eine SQL-Anweisung das
Plug-In aufruft.
Wenn eine init
- oder
deinit
-Funktion für ein Plug-In nicht
notwendig ist, kann sie in der
st_mysql_plugin
-Struktur mit 0 angegeben
werden.
In der st_mysql_plugin
-Struktur, die eine
allgemeine Plug-In-Deklaration definiert, verweist der
Bestandteil info
auf einen typspezifischen
Plug-In-Deskriptor. Bei einem Volltext-Parser-Plug-In
entspricht dieser Deskriptor der
st_mysql_ftparser
-Struktur in der
plugin.h
-Datei:
struct st_mysql_ftparser { int interface_version; int (*parse)(MYSQL_FTPARSER_PARAM *param); int (*init)(MYSQL_FTPARSER_PARAM *param); int (*deinit)(MYSQL_FTPARSER_PARAM *param); };
Wie die Strukturdefinition zeigt, hat der Deskriptor eine
Versionsnummer
(MYSQL_FTPARSER_INTERFACE_VERSION
für
Volltext-Parser-Plug-Ins) und enthält Zeiger auf drei
Funktionen. Die Bestandteile init
und
deinit
sollten auf eine Funktion verweisen
oder auf 0 gesetzt werden, wenn die Funktion nicht gebraucht
wird. Der Bestandteil parse
muss auf die
Parse-Funktion verweisen.
Ein Volltext-Parser-Plug-In wird für zwei Dinge verwendet: Indizierung und Suchoperationen. In beiden Fällen ruft der Server die Initialisierungs- und Deinitialisierungsfunktion am Anfang und am Ende der Verarbeitung jeder SQL-Anweisung auf, in der das Plug-In benutzt wird. Während der Verarbeitung der Anweisung ruft der Server die Parsing-Hauptfunktion jedoch kontextabhängig auf:
Zum Indizieren ruft der Server den Parser für jeden zu indizierenden Spaltenwert auf.
In Suchoperationen ruft der Server den Parser auf, um den
Such-String zu parsen. Ebenso kann der Parser für Zeilen
aufgerufen werden, die von der Anweisung verarbeitet
werden. Im Modus für natürliche Sprache braucht der
Server den Parser nicht aufzurufen. Ini Phrasensuchen im
booleschen Modus oder Suchen in natürlicher Sprache mit
Abfrageerweiterung (Query Expansion) wird der Parser
eingesetzt, um in den Spaltenwerten Informationen zu
parsen, die nicht im Index vorliegen. Wenn eine Suche nach
einer Spalte mit FULLTEXT
-Index im
booleschen Modus durchgeführt wird, wird ebenfalls der
eingebaute Parser aufgerufen. (Plug-Ins sind mit konkreten
Indizes verbunden. Ist kein Index vorhanden, wird auch
kein Plug-In verwendet.)
Beachten Sie, dass die Plug-In-Deklaration im Plug-In-Bibliotheksdeskriptor Initialisierungs- und Deinitialisierungsfunktionen hat, ebenso wie der Plug-In-Deskriptor, auf den sie verweist. Diese Funktionspaare dienen unterschiedlichen Zwecken und werden aus unterschiedlichen Gründen aufgerufen:
Für die Plug-In-Deklaration im Plug-In-Bibliotheksdeskriptor werden die Initialisierungs- und Deinitialisierungsfunktionen beim Laden und Entladen des Plug-Ins aufgerufen.
Für den Plug-In-Deskriptor werden die Initialisierungs- und Deinitialisierungsfunktionen für jede SQL-Anweisung aufgerufen, in der das Plug-In benutzt wird.
Jede API-Funktion, die im Plug-In-Deskriptor genannt ist,
sollte null bei einem Erfolg und einen von null verschiedenen
Wert bei einem Misserfolg zurückgeben, und jede sollte ein
Argument entgegennehmen, das auf eine
MYSQL_FTPARSER_PARAM
-Struktur verweist, die
den Parsing-Kontext enthält. Die Struktur ist wie folgt
definiert:
typedef struct st_mysql_ftparser_param { int (*mysql_parse)(void *param, byte *doc, uint doc_len); int (*mysql_add_word)(void *param, byte *word, uint word_len, MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info); void *ftparser_state; void *mysql_ftparam; CHARSET_INFO *cs; byte *doc; uint length; int mode; } MYSQL_FTPARSER_PARAM;
Die Bestandteile der Struktur werden folgendermaßen benutzt:
mysql_parse
Ein Zeiger auf eine Callback-Funktion, die den eingebauten
Parser des Servers aufruft. Diesen Callback verwenden Sie,
wenn das Plug-In als Frontend des eingebauten Parsers
fungiert. Wenn also die Plug-In-Parsing-Funktion
aufgerufen wird, soll sie die Eingabe verarbeiten, um den
Inhalt zu extrahieren, und diesen dann an den
mysql_parse
-Callback übergeben.
Der erste Parameter dieser Callback-Funktion sollte der
mysql_ftparam
-Teil der Struktur des
Parsing-Kontextes sein. Das heißt: Wenn
param
auf die Struktur verweist, wird
der Callback folgendermaßen aufgerufen:
param->mysql_parse(param->mysql_ftparam, ...);
Ein Frontend-Plug-In kann Text extrahieren und komplett oder häppchenweise an den eingebauten Parser übergeben. Im zweiten Fall behandelt der eingebaute Parser die Textstücke, als befänden sich implizite Wortgrenzen zwischen ihnen.
mysql_add_word
Ein Zeiger auf eine Callback-Funktion, die einem
Volltextindex oder der Liste der Suchbegriffe ein Wort
hinzufügt. Diesen Callback verwenden Sie, wenn das
Parser-Plug-In den eingebauten Parser ersetzen soll. Das
bedeutet: Wenn die Parsing-Funktion des Plug-Ins
aufgerufen wird, soll sie die Eingabe in Wörter parsen
und für jedes Wort den
mysql_add_word
-Callback aufrufen.
Der erste Parameter dieser Callback-Funktion soll der
mysql_ftparam
-Teil der Struktur des
Parsing-Kontexts sein. Das heißt: Wenn
param
auf die Struktur verweist, wird
der Callback wie folgt aufgerufen:
param->mysql_add_word(param->mysql_ftparam, ...);
ftparser_state
Dies ist ein generischer Zeiger. Das Plug-In kann ihn auf die Informationen verweisen lassen, die es intern für seine eigenen Zwecke benutzt.
mysql_ftparam
Wird durch den Server eingestellt und als erstes Argument
an den mysql_parse
- oder
mysql_add_word
-Callback übergeben.
cs
Verweist auf den Zeichensatz für den Text, oder auf 0, wenn diese Information nicht zur Verfügung steht.
doc
Ein Zeiger auf den zu parsenden Text.
length
Die Länge des zu parsenden Texts in Bytes.
mode
Der Parsing-Modus. Dieser Wert ist eine der folgenden Konstanten:
MYSQL_FTPARSER_SIMPLE_MODE
Parsen im schnellen, einfachen Modus. Dies wird für Indizes und für Anfragen mit natürlichen Sprachen verwendet. Der Parser sollte an den Server nur die zu indizierenden Wörter übergeben. Wenn er Längenbeschränkungen oder eine Liste mit zu ignorierenden Stoppwörtern verwendet, soll er die hierdurch ausgeschlossenen Wörter nicht an den Server übergeben.
MYSQL_FTPARSER_WITH_STOPWORDS
Parsen im Stoppwortmodus. Dies wird bei booleschen Suchoperationen für den Abgleich von Begriffen verwendet. Der Parser sollte alle Wörter an den Server übergeben, auch Stoppwörter oder Wörter, die die normalen Längenbeschränkungen übersteigen.
MYSQL_FTPARSER_FULL_BOOLEAN_INFO
Parsen im booleschen Modus. Wird zum Parsen von
booleschen Anfrage-Strings genutzt. Der Parser soll
nicht nur Wörter, sondern auch Operatoren für den
booleschen Modus erkennen, und diese als Token mit dem
mysql_add_word
-Callback an den
Server übergeben. Um dem Server zu sagen, welche Art
von Token übergeben wird, muss das Plug-In eine
MYSQL_FTPARSER_BOOLEAN_INFO
-Struktur
ausfüllen und einen Zeiger auf diese Struktur mit
übergeben.
Wenn der Parser im booleschen Modus aufgerufen wird, hat
param->mode
den Wert
MYSQL_FTPARSER_FULL_BOOLEAN_INFO
. Die
MYSQL_FTPARSER_BOOLEAN_INFO
-Struktur, die
der Parser zur Übergabe von Token-Informationen an den Server
benutzt, sieht folgendermaßen aus:
typedef struct st_mysql_ftparser_boolean_info { enum enum_ft_token_type type; int yesno; int weight_adjust; bool wasign; bool trunc; /* Diese sind im Parser-Zustand und müssen entfernt werden. */ byte prev; byte *quot; } MYSQL_FTPARSER_BOOLEAN_INFO;
Der Parser sollte die Bestandteile der Struktur folgendermaßen ausfüllen:
type
Der Token-Typ. Dies sollte einer der Werte der folgenden Tabelle sein:
Typ | Bedeutung |
FT_TOKEN_EOF | Ende der Daten |
FT_TOKEN_WORD | Ein normales Wort |
FT_TOKEN_LEFT_PAREN | Beginn einer Gruppe oder eines Teilausdrucks |
FT_TOKEN_RIGHT_PAREN | Ende einer Gruppe oder eines Teilausdrucks |
FT_TOKEN_STOPWORD | Ein Stoppwort |
yesno
Gibt an, ob das Wort vorhanden sein muss, damit eine Übereinstimmung festgestellt wird. 0 bedeutet, dass das Wort optional ist, sein Vorhandensein jedoch die Relevanz der Übereinstimmung erhöht. Werte größer 0 bedeuten, dass das Wort obligatorisch ist, und Werte kleiner 0, dass es nicht vorhanden sein darf.
weight_adjust
Ein Gewichtsfaktor, der festlegt, wie viel eine
Übereinstimmung für das Wort zählt. Indem man diesen
Faktor herauf- oder heruntersetzt, beeinflusst man die
Bedeutung, die dieses Wort in Relevanzberechnungen hat.
Ist der Wert null, so erfolgt keine Anpassung der
Gewichtung. Werte größer oder kleiner null bedeuten ein
höheres oder geringeres Gewicht. Die Beispiele unter
Abschnitt 12.7.1, „Boolesche Volltextsuche“, die
<
- und
>
-Operatoren verwenden, zeigen, wie
Gewichtung funktioniert.
wasign
Das Vorzeichen des Gewichtungsfaktors. Ein negativer Wert
verhält sich wie der boolesche Suchoperator
~
, der dafür sorgt, dass das Wort in
negativer Weise zur Relevanz beiträgt.
trunc
Zeigt an, ob der Abgleich in derselben Weise durchgeführt
wird, als ob der Kappungsoperator *
im
booleschen Modus angegeben worden wäre.
Plug-Ins sollten nicht die Bestandteile
prev
und quot
der
MYSQL_FTPARSER_BOOLEAN_INFO
-Struktur
benutzen.
Dieser Abschnitt erklärt die Erstellung einer
Plug-In-Bibliothek Schritt für Schritt. Er zeigt, wie man
eine Bibliothek entwickelt, die ein Volltext-Parsing-Plug-In
namens simple_parser
enthält. Dieses
Plug-In wendet einfachere Regeln als der eingebaute
Volltext-Parser von MySQL an: Wörter sind nichtleere Folgen
von Whitespace-Zeichen.
Jede Plug-In-Bibliothek enthält Folgendes:
Ein Plug-In-Bibliotheksdeskriptor mit der Versionsnummer der allgemeinen Plug-In-API der Bibliothek und einer allgemeinen Deklaration für jedes Plug-In in der Bibliothek.
Jede allgemeine Plug-In-Deklaration enthält Informationen, die allen Arten von Plug-Ins gemeinsam sind: einen Wert, der den Plug-In-Typ anzeigt, den Namen, den Autor und die Beschreibung des Plug-Ins sowie Zeiger auf die Initialisierungs- und die Deinitialisierungfunktionen, die der Server beim Laden und Entladen des Plug-Ins aufrufen muss.
Außerdem enthält die allgemeine Plug-In-Deklaration einen Zeiger auf einen typspezifischen Plug-In-Deskriptor. Die Struktur dieser Deskriptoren kann je nach Plug-In-Typ unterschiedlich sein, da jede Art von Plug-In ihre eigene API haben kann. Ein Plug-In-Deskriptor enthält eine typspezifische API-Versionsnummer und Zeiger auf die Funktionen, die zur Implementierung dieses Plug-In-Typs erforderlich sind. So hat beispielsweise ein Volltext-Parser-Plug-In Initialisierungs- und Deinitialisierungsfunktionen und eine Parsing-Hauptfunktion. Der Server ruft diese Funktionen auf, wenn er das Plug-In zum Parsen von Text einsetzt.
Die Plug-In-Bibliothek enthält die Schnittstellenfunktionen, auf die der Bibliotheksdeskriptor und die Plug-In-Deskriptoren verweisen.
Eine Plug-In-Bibliothek wird folgendermaßen angelegt:
Zuerst binden Sie die von der Plug-In-Bibliothek
benötigten Header-Dateien ein. Die Datei
plugin.h
ist auf jeden Fall
notwendig, allerdings kann die Bibliothek auch noch andere
Dateien erfordern. Zum Beispiel:
#include <my_global.h> #include <m_string.h> #include <m_ctype.h> #include <plugin.h>
Dann richten Sie den Deskriptor für die Plug-In-Bibliotheksdatei ein.
Jede Plug-In-Bibliothek muss einen Bibliotheksdeskriptor besitzen, der zwei Symbole definiert:
_mysql_plugin_interface_version_
ist die Versionsnummer des allgemeinen
Plug-In-Frameworks. Sie wird durch das Symbol
MYSQL_PLUGIN_INTERFACE_VERSION
angegeben, das in der Datei
plugin.h
definiert ist.
_mysql_plugin_declarations_
definiert ein Array von Plug-In-Deklarationen, wobei
am Ende eine Deklaration steht, in der alle
Bestandteile auf 0 gesetzt sind. Jede Deklaration ist
eine Instanz der Struktur
st_mysql_plugin
(ebenfalls in
plugin.h
definiert). Für jedes
Plug-In in der Bibliothek muss eine solche Deklaration
vorhanden sein.
Wenn der Server diese beiden Symbole nicht in einer Bibliothek vorfindet, akzeptiert er sie nicht als gültige Plug-In-Bibliothek und weist sie mit einer Fehlermeldung zurück. So wird verhindert, dass eine Bibliothek für Plug-In-Zwecke benutzt wird, die nicht speziell als Plug-In-Bibliothek ausgelegt wurde.
Die üblichste (und bequemste) Art, die beiden notwendigen
Symbole zu definieren, bieten die beiden Makros
mysql_declare_plugin
und
mysql_declare_plugin_end
aus der Datei
plugin.h
:
mysql_declare_plugin
... eine oder mehr Plug-In-Deklarationen ...
mysql_declare_plugin_end;
Der Bibliotheksdeskriptor für eine Bibliothek mit einem
einzigen Plug-In namens simple_parser
sähe beispielsweise folgendermaßen aus:
mysql_declare_plugin { MYSQL_FTPARSER_PLUGIN, /* Typ */ &simple_parser_descriptor, /* Deskriptor */ "simple_parser", /* Name */ "MySQL AB", /* Autor */ "Simple Full-Text Parser", /* Beschreibung */ simple_parser_plugin_init, /* Initialisierungsfunktion */ simple_parser_plugin_deinit /* Deinitialisierungsfunktion */ } mysql_declare_plugin_end;
Der Typ eines Volltext-Parser-Plug-Ins müsste
MYSQL_FTPARSER_PLUGIN
sein. Dieser Wert
kennzeichnet das Plug-In als zulässig zur Benutzung in
einer WITH PARSER
-Klausel, wenn ein
FULLTEXT
-Index angelegt werden soll.
(Kein anderer Plug-In-Typ ist für diese Klausel erlaubt.)
Die Makros mysql_declare_plugin
und
mysql_declare_plugin_end
sind in
plugin.h
folgendermaßen definiert:
#define mysql_declare_plugin \ int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION; \ struct st_mysql_plugin _mysql_plugin_declarations_[]= { #define mysql_declare_plugin_end ,{0,0,0,0,0,0,0}}
In der oben gezeigten Verwendung werden die Makros zu
folgendem Code expandiert, der beide erforderlichen
Symbole definiert
(_mysql_plugin_interface_version_
und
_mysql_plugin_declarations_
):
int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION; struct st_mysql_plugin _mysql_plugin_declarations_[]= { { MYSQL_FTPARSER_PLUGIN, /* Typ */ &simple_parser_descriptor, /* Deskriptor */ "simple_parser", /* Name */ "MySQL AB", /* Autor */ "Simple Full-Text Parser", /* Beschreibung */ simple_parser_plugin_init, /* Initialisierungsfunktion */ simple_parser_plugin_deinit /* Deinitialisierungsfunktion */ } ,{0,0,0,0,0,0,0} };
Im obigen Beispiel wird nur ein einzelnes Plug-In im
Bibliotheksdeskriptor deklariert, aber es ist ebenso gut
möglich, mehrere Plug-Ins zu deklarieren. Hierzu führen
Sie die Deklarationen
– durch Kommata getrennt – in
mysql_declare_plugin
und
mysql_declare_plugin_end
auf.
Nun richten Sie den Plug-In-Deskriptor ein.
Jede Plug-In-Deklaration im Bibliotheksdeskriptor verweist
auf einen typspezifischen Deskriptor für das zugehörige
Plug-In. In der Deklaration von
simple_parser
wird dieser Deskriptor
von &simple_parser_descriptor
angezeigt. Der Deskriptor gibt die Versionsnummer für die
Volltext-Plug-In-Schnittstelle (wie sie in
MYSQL_FTPARSER_INTERFACE_VERSION
steht)
sowie die Parsing-, Initialisierungs- und
Deinitialisierungsfunktionen des Plug-Ins an:
static struct st_mysql_ftparser simple_parser_descriptor= { MYSQL_FTPARSER_INTERFACE_VERSION, /* Schnittstellenversion */ simple_parser_parse, /* Parsing-Funktion */ simple_parser_init, /* Parser-Initialisierungsfunktion */ simple_parser_deinit /* Parser-Deinitialisierungsfunktion */ };
Jetzt richten Sie die Plug-In-Schnittstellenfunktionen ein.
In der allgemeinen Plug-In-Deklaration des
Bibliotheksdeskriptors werden die Initialisierungs- und
Deinitialisierungsfunktionen angegeben, die der Server zum
Laden und Entladen des Plug-Ins aufrufen muss. Für den
simple_parser
geben diese Funktionen
lediglich null zurück, um anzuzeigen, dass sie Erfolg
hatten:
static int simple_parser_plugin_init(void) { return(0); } static int simple_parser_plugin_deinit(void) { return(0); }
Da diese Funktionen eigentlich nichts tun, könnten Sie sie ebenso gut weglassen und in der Plug-In-Deklaration jeweils durch 0 ersetzen.
Der typspezifische Plug-In-Deskriptor für den
simple_parser
gibt die
Initialisierungs-, Deinitialisierungs- und
Parsing-Funktionen an, die der Server bei Benutzung dieses
Plug-Ins aufrufen muss. Beim
simple_parser
tun die Initialisierungs-
und Deinitialisierungsfunktionen gar nichts:
static int simple_parser_init(MYSQL_FTPARSER_PARAM *param) { return(0); } static int simple_parser_deinit(MYSQL_FTPARSER_PARAM *param) { return(0); }
Da diese Funktionen nichts tun, könnte man sie hier ebenfalls weglassen und im Plug-In-Deskriptor 0 für sie angeben.
Da die Parsing-Hauptfunktion
simple_parser_parse()
den eingebauten
Volltext-Parser ersetzen soll, muss sie Text in Wörter
zerlegen und diese Wörter an den Server übergeben. Das
erste Argument der Parsing-Funktion ist ein Zeiger auf
eine Struktur, die den Parsing-Kontext enthält. Diese
Struktur besitzt einen doc
-Bestandteil,
der auf den zu parsenden Text verweist, und einen
length
-Bestandteil, der die Länge
dieses Texts anzeigt. Da das Plug-In ganz einfache
Parsing-Regeln verwendet, wonach nichtleere Folgen von
Whitespace-Zeichen als Wörter betrachtet werden, erkennt
es Wörter folgendermaßen:
static int simple_parser_parse(MYSQL_FTPARSER_PARAM *param) { char *end, *start, *docend= param->doc + param->length; for (end= start= param->doc;; end++) { if (end == docend) { if (end > start) add_word(param, start, end - start); break; } else if (isspace(*end)) { if (end > start) add_word(param, start, end - start); start= end + 1; } } return(0); }
Während der Parser ein Wort nach dem anderen erkennt,
ruft er die Funktion add_word()
auf, um
es an den Server zu übergeben.
add_word()
ist nur eine Hilfsfunktion,
die nicht zur Plug-In-Schnittstelle gehört. Der Parser
übergibt den Zeiger auf den Parsing-Kontext, den Zeiger
auf das Wort und einen Längenwert an
add_word()
.
static void add_word(MYSQL_FTPARSER_PARAM *param, char *word, size_t len) { MYSQL_FTPARSER_BOOLEAN_INFO bool_info= { FT_TOKEN_WORD, 0, 0, 0, 0, ' ', 0 }; if (param->mode == MYSQL_FTPARSER_FULL_BOOLEAN_INFO) param->mysql_add_word(param->mysql_ftparam, word, len, &bool_info); else param->mysql_add_word(param->mysql_ftparam, word, len, 0); }
Beim Parsen im booleschen Modus füllt
add_word()
die Bestandteile der
bool_info
-Struktur aus, wie in
Abschnitt 26.2.5.2, „Typspezifische Plug-In-Strukturen und -Funktionen“ beschrieben.
Kompilieren Sie die Plug-In-Bibliothek als Shared Library und installieren Sie sie in das Plug-In-Verzeichnis.
Die Prozedur zum Kompilieren von Shared-Objekten ist von
System zu System unterschiedlich. Wenn Sie Ihre Bibliothek
mithilfe der GNU-Autotools bauen, müsste
libtool in der Lage sein, die richtigen
Kompilierbefehle für Ihr System zu generieren. Wenn die
Bibliothek mypluglib
heißt, müssten
Sie zum Schluss mit einer Shared Object-Datei dastehen,
die so ähnlich wie libmypluglib.so
heißt. (Der Dateiname kann auf Ihrem System eine andere
Erweiterung haben.)
Die Stelle, an der Sie die Bibliothek in Ihrem
Plug-In-Verzeichnis installieren müssen, verrät Ihnen
die Systemvariable plugin_dir
. Zum
Beispiel:
mysql> SHOW VARIABLES LIKE 'plugin_dir';
+---------------+----------------------------+
| Variable_name | Value |
+---------------+----------------------------+
| plugin_dir | /usr/local/mysql/lib/mysql |
+---------------+----------------------------+
Wenn Sie die Plug-In-Bibliothek installieren, achten Sie bitte darauf, dass Ihre Berechtigungen eine Ausführung durch den Server erlauben.
Registrieren Sie das Plug-In beim Server.
Die INSTALL PLUGIN
-Anweisung lässt den
Server das Plug-In in die
plugin
-Tabelle
übernehmen und seinen Code aus der Bibliotheksdatei
laden. Registrieren Sie mit dieser Anweisung den
simple_parser
beim Server und
vergewissern Sie sich dann, dass er in die
plugin
-Tabelle aufgenommen wurde:
mysql>INSTALL PLUGIN simple_parser SONAME 'libmypluglib.so';
Query OK, 0 rows affected (0.00 sec) mysql>SELECT * FROM mysql.plugin;
+---------------+-----------------+ | name | dl | +---------------+-----------------+ | simple_parser | libmypluglib.so | +---------------+-----------------+ 1 row in set (0.00 sec)
Nun probieren Sie das Plug-In aus.
Legen Sie eine Tabelle mit einer String-Spalte an und
verbinden Sie das Parser-Plug-In mit einem
FULLTEXT
-Index auf der Spalte:
mysql>CREATE TABLE t (c VARCHAR(255),
->FULLTEXT (c) WITH PARSER simple_parser);
Query OK, 0 rows affected (0.01 sec)
Fügen Sie Text in die Tabelle ein und versuchen Sie einige Suchoperationen. Dabei sollte ausprobiert werden, ob das Parser-Plug-In wirklich alle Nicht-Whitespace-Zeichen als Wortzeichen betrachtet:
mysql>INSERT INTO t VALUES
->('latin1_general_cs is a case-sensitive collation'),
->('I\'d like a case of oranges'),
->('this is sensitive information'),
->('another row'),
->('yet another row');
Query OK, 5 rows affected (0.02 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql>SELECT c FROM t;
+-------------------------------------------------+ | c | +-------------------------------------------------+ | latin1_general_cs is a case-sensitive collation | | I'd like a case of oranges | | this is sensitive information | | another row | | yet another row | +-------------------------------------------------+ 5 rows in set (0.00 sec) mysql>SELECT MATCH(c) AGAINST('case') FROM t;
+--------------------------+ | MATCH(c) AGAINST('case') | +--------------------------+ | 0 | | 1.2968142032623 | | 0 | | 0 | | 0 | +--------------------------+ 5 rows in set (0.00 sec) mysql>SELECT MATCH(c) AGAINST('sensitive') FROM t;
+-------------------------------+ | MATCH(c) AGAINST('sensitive') | +-------------------------------+ | 0 | | 0 | | 1.3253291845322 | | 0 | | 0 | +-------------------------------+ 5 rows in set (0.01 sec) mysql>SELECT MATCH(c) AGAINST('case-sensitive') FROM t;
+------------------------------------+ | MATCH(c) AGAINST('case-sensitive') | +------------------------------------+ | 1.3109166622162 | | 0 | | 0 | | 0 | | 0 | +------------------------------------+ 5 rows in set (0.01 sec) mysql>SELECT MATCH(c) AGAINST('I\'d') FROM t;
+--------------------------+ | MATCH(c) AGAINST('I\'d') | +--------------------------+ | 0 | | 1.2968142032623 | | 0 | | 0 | | 0 | +--------------------------+ 5 rows in set (0.01 sec)
Beachten Sie, dass weder „case“ noch „insensitive“ auf „case-insensitive“ passen, wie es bei dem eingebauten Parser der Fall wäre.
Es gibt zwei Möglichkeiten, neue Funktionen zu MySQL hinzuzufügen:
Die erste Möglichkeit ist die Schnittstelle für
benutzerdefinierte Funktionen (User-Defined Functions, UDFs).
Benutzerdefinierte Funktionen werden als Objektdateien
kompiliert und dann dynamisch mit den Anweisungen
CREATE FUNCTION
und DROP
FUNCTION
auf den Server geladen oder von ihm
entfernt. Siehe auch Abschnitt 26.3.2, „CREATE FUNCTION/DROP FUNCTION
“.
Die zweite Möglichkeit: Sie fügen die Funktionen als native (eingebaute) MySQL-Funktionen hinzu. Native Funktionen werden in den mysqld-Server kompiliert und stehen dann dauerhaft zur Verfügung.
Beide Methoden haben ihre Vor- und Nachteile:
Wenn Sie benutzerdefinierte Funktionen schreiben, müssen Sie zusätzlich zum Server auch noch Objektdateien installieren. Wenn Sie Ihre Funktion in den Server kompilieren, entfällt das.
Um native Funktionen zu erstellen, müssen Sie eine Quelldistribution ändern; für UDFs müssen Sie das nicht. UDFs können Sie auch einer Binärdistribution von MySQL hinzufügen, ohne in den Quellcode von MySQL eingreifen zu müssen.
Wenn Sie Ihre MySQL-Distribution aufrüsten, können Sie die zuvor installierten UDFs auch weiterhin nutzen, es sei denn, die neue Version ist eine, bei der sich die UDF-Schnittstelle geändert hat. Für native Funktionen müssen Sie Ihre Modifikationen bei jedem Upgrade wiederholen.
Ganz gleich, welchen Weg Sie einschlagen, um neue Funktionen
hinzuzufügen: Aufgerufen werden sie in SQL-Anweisungen nicht
anders als native Funktionen, wie zum Beispiel
ABS()
oder SOUNDEX()
.
Auch gespeicherte Funktionen bieten die Möglichkeit, Funktionen hinzuzufügen. Gespeicherte Funktionen schreiben Sie in SQL-Anweisungen, anstatt sie in den Objektcode zu kompilieren. Die Syntax für gespeicherte Funktionen wird in Kapitel 19, Gespeicherte Prozeduren und Funktionen, beschrieben.
Die folgenden Abschnitte beschreiben Features der UDF-Schnittstelle, geben Anleitungen zum Schreiben von UDFs, erläutern Sicherheitsmaßnahmen, mit denen MySQL den Missbrauch von UDFs verhindert, und erklären, wie native MySQL-Funktionen hinzugefügt werden.
Quellcode, der zeigt, wie man UDFs schreibt, finden Sie in der
Datei sql/udf_example.cc
, die in den
MySQL-Quelldistributionen enthalten ist.
Die MySQL-Schnittstelle für benutzerdefinierte Funktionen stellt folgende Features und Fähigkeiten zur Verfügung:
Funktionen können String-, Integer- oder Real-Werte zurückgeben.
Einfache Funktionen, die immer nur auf einer einzigen Zeile operieren, können ebenso definiert werden wie Aggregatfunktionen, die auf Zeilengruppen arbeiten.
Die Funktionen erhalten Informationen, die sie in die Lage versetzen, die Anzahl und den Typ der an sie übergebenen Argumente zu überprüfen.
Sie können MySQL anweisen, Argumenten einen bestimmten Typ aufzuzwingen, bevor sie an eine Funktion übergeben werden.
Sie können verlangen, dass eine Funktion
NULL
zurückgibt oder dass ein Fehler
ausgelöst wird.
CREATE [AGGREGATE] FUNCTIONfunction_name
RETURNS {STRING|INTEGER|REAL|DECIMAL} SONAMEshared_library_name
Mit benutzerdefinierten Funktionen (UDFs) können Sie MySQL um
neue Funktionen erweitern, die wie native (eingebaute)
MySQL-Funktionen wie etwa ABS()
oder
CONCAT()
funktionieren.
function_name
ist der Name, mit dem
die Funktion in SQL-Anweisungen aufgerufen wird. Die
RETURNS
-Klausel zeigt den Rückgabetyp der
Funktion an. DECIMAL
ist zwar hinter
RETURNS
zulässig, aber gegenwärtig geben
DECIMAL
-Funktionen String-Werte zurück und
sollten daher wie STRING
-Funktionen
geschrieben werden.
shared_library_name
ist der Basisname
der Shared Object-Datei, die den Code zur Implementierung der
Funktion enthält. Diese Datei muss im Plug-In-Verzeichnis
liegen, welches durch den Wert der Systemvariablen
plugin_dir
vorgegeben ist.
(Hinweis: Dies ist eine
Änderung in MySQL 5.1. In früheren Versionen von MySQL konnte
die Shared Object-Datei in jedem beliebigen Verzeichnis liegen,
das der dynamische Linker des Systems untersuchte.)
Zur Erstellung einer Funktion benötigen Sie das
INSERT
-Recht für die
mysql
-Datenbank, da CREATE
FUNCTION
in die Systemtabelle
mysql.func
eine Zeile mit dem Namen, Typ und
Shared Library-Namen der Funktion einfügt. Wenn diese Tabelle
bei Ihnen fehlt, müssen Sie sie mit dem Skript
mysql_fix_privilege_tables anlegen. Siehe
Abschnitt 5.6, „mysql_fix_privilege_tables — Upgrade von MySQL-Systemtabellen“.
Eine aktive Funktion ist eine Funktion, die mit CREATE
FUNCTION
geladen, aber nicht mit DROP
FUNCTION
wieder gelöscht wurde. Alle aktiven
Funktionen werden bei jedem Serverstart neu geladen, es sei
denn, Sie starten mysqld mit der Option
--skip-grant-tables
. In diesem Fall wird die
UDF-Initialisierung übersprungen und die UDFs stehen nicht zur
Verfügung.
Anleitungen zum Schreiben benutzerdefinierter Funktionen finden Sie unter Abschnitt 26.3.4, „Hinzufügen einer neuen benutzerdefinierten Funktion“. Damit der UDF-Mechanismus funktioniert, müssen die Funktionen in C oder C++ geschrieben sein, Ihr Betriebssystem muss dynamisches Laden unterstützen, und Sie müssen mysqld dynamisch (nicht statisch) kompiliert haben.
Eine AGGREGATE
-Funktion funktioniert genau
wie eine native MySQL-Aggregatfunktion (Summenfunktion), wie
beispielsweise SUM
oder
COUNT()
. Damit AGGREGATE
funktioniert, muss Ihre mysql.func
-Tabelle
eine type
-Spalte enthalten. Wenn Ihre
mysql.func
-Tabelle diese Spalte nicht hat,
müssen Sie sie mit dem Skript
mysql_fix_privilege_tables erzeugen.
DROP FUNCTION function_name
Diese Anweisung löscht eine benutzerdefinierte Funktion (UDF)
namens function_name
.
Um eine Funktion löschen zu dürfen, benötigen Sie das
DELETE
-Recht für die
mysql
-Datenbank, denn DROP
FUNCTION
löscht aus der Systemtabelle
mysql.func
die Zeile, die den Namen, Typ und
Shared Library-Namen der Funktion enthält.
Damit der UDF-Mechanismus funktioniert, müssen die Funktionen
in C oder C++ geschrieben sein, und Ihr Betriebssystem muss
dynamisches Laden unterstützen. Die MySQL-Quelldistribution
enthält eine Datei namens
sql/udf_example.cc
, die fünf neue
Funktionen definiert. In dieser Datei können Sie sehen, wie die
Aufrufkonventionen für UDFs funktionieren.
Da eine UDF Code enthält, der zum Teil des laufenden Servers
gehört, müssen Sie beim Schreiben von UDFs alle
Einschränkungen beachten, die auch sonst für das Schreiben von
Servercode gelten. Sie können beispielsweise Probleme bekommen,
wenn Sie versuchen, Funktionen aus der
libstdc++
-Bibliothek zu benutzen. Beachten
Sie, dass sich diese Einschränkungen in zukünftigen Versionen
des Servers noch ändern können. Daher kann es sein, dass Sie
bei einem Server-Upgrade auch Ihre für ältere Serverversionen
geschriebenen UDFs überarbeiten müssen. Informationen über
diese Einschränkungen erhalten Sie unter
Abschnitt 2.8.2, „Typische configure-Optionen“, und
Abschnitt 2.8.4, „Probleme beim Kompilieren?“.
Um UDFs nutzen zu können, müssen Sie mysqld
dynamisch verlinken. Bitte konfigurieren Sie MySQL nicht mit der
Option --with-mysqld-ldflags=-all-static
. Wenn
Sie eine UDF benutzen möchten, die auf Symbole aus
mysqld zugreifen muss (beispielsweise die
metaphone
-Funktion in
sql/udf_example.cc
, die
default_charset_info
braucht), müssen Sie
das Programm mit der Option -rdynamic
verlinken
(siehe man dlopen
). Wenn Sie den Einsatz von
UDFs planen, sollten Sie MySQL grundsätzlich mit
--with-mysqld-ldflags=-rdynamic
konfigurieren,
wenn nichts Wichtiges dagegen spricht.
Wenn Sie eine vorkompilierte Distribution von MySQL einsetzen müssen, so verwenden Sie MySQL-Max: Diese Version enthält einen dynamisch verlinkten Server, der dynamisches Laden unterstützt.
Für jede Funktion, die Sie in SQL-Anweisungen nutzen möchten,
sollten Sie zugehörige C- oder C++-Funktionen definieren. In
den folgenden Ausführungen steht der Name „xxx“
für den Namen einer Beispielfunktion. Um zwischen dem Einsatz
in SQL und C/C++ zu unterscheiden, wird ein Funktionsaufruf in
SQL mit XXX()
(Großbuchstaben) und ein
Funktionsaufruf in C/C++ mit xxx()
(Kleinbuchstaben) angegeben.
Sie schreiben folgende C-/C++-Funktionen, um die Schnittstelle
für XXX()
zu implementieren:
xxx()
(obligatorisch)
Die Hauptfunktion. Hier wird das Ergebnis der Funktion berechnet. Die Entsprechung zwischen dem Datentyp der SQL-Funktion und dem Rückgabetyp der C-/C++-Funktion sehen Sie hier:
SQL-Typ | C-/C++-Typ |
STRING | char * |
INTEGER | long long |
REAL | double |
Es ist auch möglich, eine
DECIMAL
-Funktion zu deklarieren. Da deren
Wert jedoch zurzeit noch als String zurückgeliefert wird,
sollten Sie eine solche UDF wie eine
STRING
-Funktion auslegen.
xxx_init()
(optional)
Die Initialisierungsfunktion für xxx()
.
Sie kann zu folgenden Zwecken benutzt werden:
Um zu prüfen, wie viele Argumente
XXX()
hat.
Um nachzuschauen, ob die Argumente den verlangten Typ haben oder, alternativ, um MySQL anzuweisen, den Argumenten die Typen aufzuzwingen, die für den Aufruf der Hauptfunktion erforderlich sind.
Um den von der Hauptfunktion benötigten Speicher zu reservieren.
Um die maximale Länge der Ergebnismenge anzugeben.
Um (für REAL
-Funktionen) die
Höchstzahl der Dezimalstellen in der Ergebnismenge
anzugeben.
Um mitzuteilen, ob das Ergebnis auch
NULL
sein darf.
xxx_deinit()
(optional)
Die Deinitialisierungsfunktion für
xxx()
. Diese sollte den von der
Initialisierungsfunktion reservierten Speicher wieder
freigeben.
Wenn eine SQL-Anweisung XXX()
aufruft, ruft
MySQL die Initialisierungsfunktion xxx_init()
auf, damit sie die notwendigen Vorbereitungen trifft, wie
beispielsweise eine Überprüfung der Argumente oder die
Speicherzuweisung. Wenn xxx_init()
einen
Fehler zurückliefert, bricht MySQL die SQL-Anweisung mit einer
Fehlermeldung ab und ruft weder die Haupt- noch die
Deinitialisierungsfunktion auf. Ansonsten ruft MySQL nun die
Hauptfunktion xxx()
einmal pro Zeile auf.
Wenn alle Zeilen verarbeitet wurden, ruft MySQL die
Deinitialisierungsfunktion xxx_deinit()
auf,
damit diese die notwendigen Aufräumarbeiten übernehmen kann.
Für Aggregatfunktionen, die wie SUM()
arbeiten, müssen Sie außerdem folgende Funktionen
bereitstellen:
xxx_clear()
(obligatorisch für
5.1)
Setzt den aktuellen Aggregatwert zurück, setzt aber das Argument nicht als Aggregatanfangswert für eine neue Gruppe ein.
xxx_add()
(obligatorisch)
Addiert das Argument zum aktuellen Aggregatwert.
MySQL geht folgendermaßen mit Aggregat-UDFs um:
Es ruft xxx_init()
auf, damit die
Aggregatfunktion den Speicher reserviert, den sie für ihre
Ergebnisse benötigt.
Es sortiert die Tabelle entsprechend dem GROUP
BY
-Ausdruck.
Es ruft für die erste Zeile jeder neuen Gruppe
xxx_clear()
auf.
Es ruft für jede neue Zeile, die zu derselben Gruppe
gehört, xxx_add()
auf.
Es ruft xxx()
auf, um das
Aggregatergebnis zu erhalten, wenn die Gruppe wechselt oder
nachdem die letzte Zeile verarbeitet wurde.
Es wiederholt die Schritte 3 bis 5, bis alle Zeilen verarbeitet sind.
Es ruft xxx_deinit()
auf, damit die UDF
den von ihr reservierten Speicher wieder freigibt.
Alle Funktionen müssen Thread-sicher sein, also nicht nur die
Hauptfunktion, sondern auch die Initialisierungs- und
Deinitialisierungsfunktionen und alle sonstigen Funktionen, die
für Aggregatfunktionen erforderlich sind. Dies hat zur Folge,
dass Sie keine globalen oder statischen Variablen zuweisen
dürfen, die sich ändern! Wenn Sie Arbeitsspeicher benötigen,
weisen Sie ihn mit xxx_init()
zu und geben
ihn mit xxx_deinit()
frei.
Dieser Abschnitt beschreibt die verschiedenen Funktionen, die Sie definieren müssen, um eine einfache UDF anzulegen. Abschnitt 26.3.4, „Hinzufügen einer neuen benutzerdefinierten Funktion“, beschreibt die Reihenfolge, in der MySQL diese Funktionen aufruft.
Die xxx()
-Hauptfunktion sollte so
deklariert werden, wie es in diesem Abschnitt gezeigt wird.
Beachten Sie, dass der Rückgabetyp und die Parameter
unterschiedlich sind, je nachdem, ob Sie die SQL-Funktion
XXX()
in der CREATE
FUNCTION
-Anweisung mit dem Rückgabetyp
STRING
, INTEGER
oder
REAL
deklarieren:
Für STRING
-Funktionen gilt:
char *xxx(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);
Für INTEGER
-Funktionen gilt:
long long xxx(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
Für REAL
-Funktionen gilt:
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 Parameter initid
wird an alle drei
Funktionen übergeben. Er verweist auf eine
UDF_INIT
-Struktur, die Informationen
zwischen Funktionen übergibt. Die Bestandteile der
UDF_INIT
-Struktur werden im Folgenden
aufgeführt. Die Initialisierungsfunktion sollte alle
diejenigen Bestandteile ausfüllen, die geändert werden
sollen. (Um einen Standardwert beizubehalten, ändern Sie
einfach nichts.)
my_bool maybe_null
xxx_init()
sollte
maybe_null
auf 1
einstellen, wenn xxx()
den Wert
NULL
zurückliefern kann. Der
Standardwert ist 1
, wenn irgendwelche
Argumente als maybe_null
deklariert
sind.
unsigned int decimals
Die Anzahl der Dezimalstellen rechts vom Dezimalpunkt
(also salopp ausgedrückt: die Nachkommastellen). Der
Standardwert ist die Höchtzahl der Dezimalstellen in den
an die Hauptfunktion übergebenen Argumenten. (Wenn
beispielsweise 1.34
,
1.345
und 1.3
an die
Funktion übergeben werden, wäre der Standardwert 3, da
1.345
3 Nachkommastellen hat.
unsigned int max_length
Die Maximallänge des Ergebnisses. Der Standardwert
max_length
richtet sich nach dem
Ergebnistyp der Funktion. Bei String-Funktionen ist er die
Länge des längsten Arguments; bei Integer-Funktionen ist
er 21 Ziffern. Bei Real-Funktionen ist der Standardwert 13
plus die Anzahl der durch
initid->decimals
vorgegebenen
Dezimalstellen. (Bei numerischen Funktionen sind in der
Länge auch Vorzeichen und Dezimalpunkt inbegriffen.)
Wenn Sie einen Blob-Wert zurückliefern möchten, können
Sie max_length
auf 65 Kbyte oder 16
Mbyte einstellen. Der Speicherplatz wird zwar nicht
reserviert, aber anhand der Länge entscheidet das System,
welcher Datentyp für eine eventuell erforderliche
temporäre Speicherung der Daten verwendet wird.
char *ptr
Ein Zeiger, den die Funktion für ihre eigenen Zwecke
verwenden kann. So können sich die Funktionen
beispielsweise mit initid->ptr
gegenseitig mitteilen, wie viel Speicher sie reservieren
müssen. xxx_init()
sollte den Speicher
reservieren und diesem Zeiger zuweisen.
initid->ptr = allocated_memory;
In xxx()
und
xxx_deinit()
verweisen Sie auf
initid->ptr
, um Speicher zu benutzen
oder freizugeben.
my_bool const_item
xxx_init()
sollte
const_item
auf 1
setzen, wenn xxx()
immer denselben Wert
zurückgibt, und auf 0
, wenn das nicht
der Fall ist.
In diesem Abschnitt erfahren Sie, welche Funktionen Sie definieren müssen, um eine Aggregatfunktion als UDF anzulegen. Abschnitt 26.3.4, „Hinzufügen einer neuen benutzerdefinierten Funktion“, beschreibt die Reihenfolge, in der MySQL diese Funktionen aufruft.
xxx_reset()
Diese Funktion wird aufgerufen, wenn MySQL die erste Zeile
einer neuen Gruppe findet. Sie sollte eventuell vorhandene
interne Summenvariablen zurücksetzen und dann das
gegebene UDF_ARGS
-Argument als ersten
Wert des neuen internen Summenwerts für diese Gruppe
verwenden. xxx_reset()
wird
folgendermaßen deklariert:
char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
xxx_reset()
wird in MySQL
5.1 weder benötigt noch benutzt. Die
UDF-Schnittstelle verwendet stattdessen
xxx_clear()
. Sie können jedoch
xxx_reset()
zusätzlich zu
xxx_clear()
definieren, wenn Ihre UDF
auch mit älteren Serverversionen laufen soll. (Wenn Sie
beide Funktionen einbeziehen möchten, können Sie
xxx_reset()
intern oft implementieren,
indem Sie zunächst xxx_clear()
alle
Variablen zurücksetzen lassen und dann
xxx_add()
aufrufen, um das Argument
UDF_ARGS
als ersten Wert der Gruppe
hinzuzufügen.)
xxx_clear()
Diese Funktion wird aufgerufen, wenn MySQL die
Summenergebnisse zurücksetzen muss. Sie wird für jede
neue Gruppe ganz am Anfang aufgerufen, kann aber auch
verwendet werden, um die Werte für eine Anfrage
zurückzusetzen, wenn keine übereinstimmenden Zeilen
gefunden wurden. xxx_clear()
wird
folgendermaßen deklariert:
char *xxx_clear(UDF_INIT *initid, char *is_null, char *error);
is_null
wird auf
CHAR(0)
eingestellt, bevor
xxx_clear()
aufgerufen wird.
Wenn etwas schief gegangen ist, können Sie einen Wert in
der Variablen speichern, auf die das Argument
error
verweist.
error
zeigt auf eine
Singlebyte-Variable, nicht auf einen String-Puffer.
xxx_clear()
ist für MySQL
5.1 obligatorisch.
xxx_add()
Diese Funktion wird für alle Zeilen außer der ersten
aufgerufen, die zu derselben Gruppe gehören. Mit ihr
addieren Sie den Wert des Arguments
UDF_ARGS
zu Ihrer internen
Summenvariablen.
char *xxx_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
Die xxx()
-Funktion für eine Aggregat-UDF
sollte genauso definiert werden wie die für eine einfache
UDF. Siehe hierzu Abschnitt 26.3.4.1, „UDF-Aufrufsequenzen“.
Für eine Aggregat-UDF ruft MySQL die Funktion
xxx()
auf, nachdem alle Zeilen der Gruppe
abgearbeitet sind. Im Normalfall greifen Sie hier niemals auf
ihr UDF_ARGS
-Argument zu, sondern geben
einen Wert zurück, der auf Ihren internen Summenvariablen
beruht.
Rückgabewerte sollten in xxx()
genau wie
bei einer UDF behandelt werden, die keine Aggregatfunktion
ist. Siehe Abschnitt 26.3.4.4, „Rückgabewerte und Fehlerbehandlung“.
Die Funktionen xxx_reset()
und
xxx_add()
gehen mit ihrem
UDF_ARGS
-Argument genauso um wie Funktionen
für Nicht-Aggregat-UDFs. Siehe
Abschnitt 26.3.4.3, „Verarbeitung von Argumenten“.
Die Zeigerargumente für is_null
und
error
sind dieselben wie bei allen Aufrufen
von xxx_reset()
,
xxx_clear()
, xxx_add()
und xxx()
. Das können Sie nutzen, um sich
zu merken, dass ein Fehler ausgelöst wurde oder ob die
xxx()
-Funktion NULL
zurückgeben sollte. Bitte speichern Sie keinen String in
*error
! error
verweist
auf eine Single-Byte-Variable, nicht auf einen String-Puffer.
*is_null
wird für jede Gruppe (vor dem
Aufruf von xxx_clear()
) zurückgesetzt,
*error
nie.
Sind *is_null
oder
*error
gesetzt, wenn
xxx()
zurückkehrt, liefert MySQL
NULL
als Ergebnis der Gruppenfunktion
zurück.
Der Parameter args
verweist auf eine
UDF_ARGS
-Struktur mit folgenden
Bestandteilen:
unsigned int arg_count
Die Anzahl der Argumente. Diesen Wert muss die Initialisierungsfunktion prüfen, wenn Ihre Funktion mit einer bestimmten Anzahl von Argumenten aufgerufen werden muss. Zum Beispiel:
if (args->arg_count != 2) { strcpy(message,"XXX() requires two arguments"); return 1; }
enum Item_result *arg_type
Ein Zeiger auf ein Array, das die Argumenttypen enthält.
Mögliche Typwerte sind STRING_RESULT
,
INT_RESULT
und
REAL_RESULT
.
Um sicherzustellen, dass die Argumente einen bestimmten
Typ haben, und um einen Fehler auszulösen, wenn der Typ
nicht stimmt, muss die Initialisierungsfunktion das Array
arg_type
betrachten. Zum Beispiel:
if (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != INT_RESULT) { strcpy(message,"XXX() requires a string and an integer"); return 1; }
Anstatt zu verlangen, dass die Funktionsargumente
bestimmte Typen haben, können Sie auch mit der
Initialisierungsfunktion die
arg_type
-Elemente auf die gewünschten
Typen einstellen. Dies veranlasst MySQL, den Argumenten
bei jedem Aufruf von xxx()
die
richtigen Typen aufzuzwingen. Um beispielsweise aus dem
ersten Argument einen String und aus dem zweiten einen
Integer zu machen, muss Ihre
xxx_init()
-Funktion Folgendes tun:
args->arg_type[0] = STRING_RESULT; args->arg_type[1] = INT_RESULT;
char **args
args->args
informiert die
Initialisierungsfunktion über das Wesen der an Ihre
Funktion übergebenen Argumente. Bei einem
Konstantenargument i
verweist
args->args[i]
auf den Argumentwert.
(Wie Sie richtig auf diesen Wert zugreifen, erfahren Sie
weiter unten.) Bei einem Nichtkonstantenargument ist
args->args[i]
gleich
0
. Ein Konstantenargument ist ein
Ausdruck, der nur Konstanten verwendet, wie beispielsweise
3
oder 4*7-2
oder
SIN(3.14)
. Ein Nichtkonstantenargument
ist ein Ausdruck, der auf Werte verweist, die sich von
Zeile zu Zeile ändern können, wie etwa Spaltennamen oder
Funktionen, die mit Nichtkonstantenargumenten aufgerufen
werden.
Für jeden Aufruf der Hauptfunktion enthält
args->args
die Argumente, die
tatsächlich an die in Bearbeitung befindliche Zeile
übergeben werden.
Funktionen können folgendermaßen auf ein Argument
i
verweisen:
Ein Argument vom Typ STRING_RESULT
wird als String-Zeiger mit einer Längenangabe
übergeben, damit auch Binärdaten oder Daten von
beliebiger Länge verarbeitet werden können. Der
String-Inhalt ist als
args->args[i]
und die Länge des
Strings als args->lengths[i]
angegeben. Bitte setzen Sie nicht voraus, dass Strings
mit null beendet werden.
Bei Argumenten vom Typ INT_RESULT
müssen Sie args->args[i]
in
einen long long
-Wert umwandeln:
long long int_val; int_val = *((long long*) args->args[i]);
Bei Argumenten vom Typ REAL_RESULT
müssen Sie args->args[i]
in
einen double
-Wert umwandeln:
double real_val; real_val = *((double*) args->args[i]);
unsigned long *lengths
Für die Initialisierungsfunktion zeigt das Array
lengths
die maximale String-Länge für
jedes Argument an. Diese sollten Sie nicht ändern. Für
jeden Aufruf der Hauptfunktion enthält
lengths
die tatsächlichen Längen der
String-Argumente, die an die in Bearbeitung befindliche
Zeile übergeben werden. Für Argumente der Typen
INT_RESULT
oder
REAL_RESULT
enthält
lengths
immer noch die maximale Länge
des Arguments (wie für die Initialisierungsfunktion).
Die Initialisierungsfunktion sollte 0
zurückgeben, wenn kein Fehler auftritt, und ansonsten
1
. Kommt es zu einem Fehler, so sollte
xxx_init()
eine auf null endende
Fehlermeldung im Parameter message
speichern. Die Meldung wird an den Client zurückgegeben. Der
Puffer für Fehlermeldungen ist zwar
MYSQL_ERRMSG_SIZE
Zeichen lang, aber Sie
sollten dennoch versuchen, die Meldungen auf maximal 80
Zeichen zu begrenzen, damit sie auf die Breite eines normalen
Bildschirms passen.
Der Rückgabewert der Hauptfunktion xxx()
ist für long long
-
und double
-Funktionen der Funktionswert.
Eine String-Funktion sollte einen Zeiger auf das Ergebnis
zurückliefern und *result
und
*length
auf den Inhalt und die Länge des
Ergebniswerts einstellen. Zum Beispiel:
memcpy(result, "result string", 13); *length = 13;
Der result
-Puffer, der an die
xxx()
-Funktion übergeben wird, ist 255
Byte lang. Wenn Ihr Ergebnis hineinpasst, müssen Sie sich um
die Speicherzuweisung für Ergebnisse keine Gedanken machen.
Muss Ihre String-Funktion jedoch einen String zurückliefern,
der länger als 255 Byte ist, so müssen Sie den Speicher
dafür reservieren, indem Sie malloc()
in
Ihrer xxx_init()
- oder
xxx()
-Funktion aufrufen und den Speicher
dann in der xxx_deinit()
-Funktion wieder
freigeben. Sie können den reservierten Speicher auch in dem
ptr
-Slot in der
UDF_INIT
-Struktur speichern, um ihn für
zukünftige xxx()
-Aufrufe wiederverwenden
zu können. Siehe Abschnitt 26.3.4.1, „UDF-Aufrufsequenzen“.
Um den Rückgabewert NULL
in der
Hauptfunktion anzuzeigen, setzen Sie
*is_null
auf 1
:
*is_null = 1;
Um anzuzeigen, dass die Hauptfunktion einen Fehler
zurückgibt, setzen Sie *error
auf
1
:
*error = 1;
Wenn xxx()
den Wert von
*error
für irgendeine Zeile auf
1
setzt, ist der Funktionswert für die
aktuelle Zeile und alle folgenden Zeilen, die in der Anweisung
verarbeitet werden, in welcher XXX()
aufgerufen wird, gleich NULL
.
(xxx()
wird für die nachfolgenden Zeilen
noch nicht einmal aufgerufen.)
Dateien, die UDFs implementieren, müssen auf dem Serverhost
kompiliert und installert werden. Dies wird weiter unten für
die Beispiel-UDF-Datei sql/udf_example.cc
aus der MySQL-Quelldistribution beschrieben.
Die nachfolgenden Instruktionen gelten für Unix. Anleitungen für Windows finden Sie weiter unten in diesem Abschnitt.
Die Datei udf_example.cc
enthält die
folgenden Funktionen:
Die Funktion metaphon()
gibt einen
Metaphon-String aus dem String-Argument zurück. Dieser
ist so etwas wie ein Soundex-String, allerdings besser auf
die englische Sprache zugeschnitten.
Die Funktion myfunc_double()
gibt die
Summe der ASCII-Werte der in ihren Argumenten enthaltenen
Zeichen geteilt durch die Summe der Längen dieser
Argumente zurück.
Die Funktion myfunc_int()
gibt die
Summe der Längen ihrer Argumente zurück.
Die Funktion sequence([const int])
gibt
eine Sequenz zurück, die ab der gegebenen Zahl oder, wenn
keine gegeben wurde, ab 1 beginnt.
Die Funktion lookup()
gibt die
IP-Nummer eines Hostnamens zurück.
Die Funktion reverse_lookup()
gibt den
Hostnamen zu einer IP-Nummer zurück. Diese Funktion kann
entweder mit einem einzigen String-Argument der Form
'xxx.xxx.xxx.xxx'
oder mit vier Zahlen
aufgerufen werden.
Eine Datei, die dynamisch zu laden sein soll, sollte als Sharable Object-Datei mit einem Befehl wie diesem kompiliert werden:
shell> gcc -shared -o udf_example.so udf_example.cc
Wenn Sie gcc benutzen, müsste sich
udf_example.so
auch mit einem einfacheren
Befehl anlegen lassen:
shell> make udf_example.so
Die richtigen Compiler-Optionen für Ihr System finden Sie
einfach heraus, indem Sie folgenden Befehl im
sql
-Verzeichnis Ihres MySQL-Quellbaums
laufen lassen:
shell> make udf_example.o
Sie sollten einen ähnlichen Compile-Befehl geben, wie ihn
make anzeigt, allerdings ohne die Option
-c
am Zeilenende. Stattdessen fügen Sie die
Option -o udf_example.so
hinzu. (Auf manchen
Systemen müssen Sie eventuell die Option -c
im Befehl beibehalten.)
Nachdem Sie ein Shared Object für UDFs kompiliert haben,
müssen Sie es installieren und MySQL mitteilen, dass es da
ist. Wenn Sie ein Shared Object aus
udf_example.cc
kompilieren, entsteht eine
Datei, die ungefähr udf_example.so
heißt (der genaue Name ist plattformabhängig). Diese Datei
kopieren Sie in das Plug-In-Verzeichnis des Servers, das Sie
anhand der Systemvariablen plugin_dir
herausfinden können.
(Hinweis: Dies ist eine
Änderung in MySQL 5.1. In früheren Versionen von MySQL
konnte das Shared Object in jedem Verzeichnis liegen, das der
dynamische Linker Ihres Systems untersuchte.)
Auf manchen Systemen erkennt das Programm
ldconfig, das den dynamischen Linker
konfiguriert, ein Shared Object nur dann, wenn sein Name mit
lib
anfängt. In solchen Fällen sollten
Sie eine Datei wie udf_example.so
in
libudf_example.so
umbenennen.
Auf Windows können Sie benutzerdefinierte Funktionen wie folgt kompilieren:
Zuerst benötigen Sie das BitKeeper-Quellarchiv für MySQL 5.1. Siehe Abschnitt 2.8.3, „Installation vom Entwicklungs-Source-Tree“.
In diesem Quellarchiv suchen Sie im Verzeichnis
VC++Files/examples/udf_example
nach
den Dateien udf_example.def
,
udf_example.dsp
und
udf_example.dsw
.
Außerdem schauen Sie im Quellarchiv in das Verzeichnis
sql
und kopieren daraus die Datei
udf_example.cc
in das
VC++Files/examples/udf_example
-Verzeichnis.
Dann benennen Sie die Datei in
udf_example.cpp
um.
Öffnen Sie die Datei udf_example.dsw
mit Visual Studio VC++ und kompilieren Sie damit die UDFs
als ganz normales Projekt.
Sobald die Shared Object-Datei installiert ist, teilen Sie mysqld die neuen Funktionen mit folgenden Anweisungen mit:
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';
Zum Löschen von Funktionen verwenden Sie DROP
FUNCTION
:
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 Anweisungen CREATE FUNCTION
und
DROP FUNCTION
nehmen Änderungen in der
Systemtabelle func
in der
mysql
-Datenbank vor. Der Name, Typ und
Shared Library-Name werden in dieser Tabelle gespeichert.
Daher benötigen Sie INSERT
- und
DELETE
-Rechte für die
mysql
-Datenbank, um dort Funktionen
anzulegen und zu löschen.
Bitte verwenden Sie CREATE FUNCTION
nicht,
um eine Funktion hinzuzufügen, die bereits angelegt ist. Wenn
Sie eine Funktion neu installieren müssen, entfernen Sie sie
mit DROP FUNCTION
und installieren sie dann
mit CREATE FUNCTION
neu. Das müssen Sie
beispielsweise tun, wenn Sie eine neue Version Ihrer Funktion
installieren, damit auch mysqld von der
neuen Version weiß. Ansonsten würde der Server weiterhin die
alte Version verwenden.
Eine aktive Funktion ist eine Funktion, die mit
CREATE FUNCTION
geladen, aber nicht mit
DROP FUNCTION
entfernt wurde. Alle aktiven
Funktionen werden bei jedem Serverstart neu geladen, es sei
denn, Sie fahren mysqld mit der Option
--skip-grant-tables
hoch. In diesem Fall wird
die UDF-Initialisierung übersprungen und die UDFs stehen
nicht zur Verfügung.
MySQL verhindert durch folgende Maßnahmen den Missbrauch benutzerdefinierter Funktionen:
Sie benötigen das INSERT
-Recht, um
CREATE FUNCTION
, und das
DELETE
-Recht, um DROP
FUNCTION
sagen zu dürfen. Die Berechtigungen sind
erforderlich, weil diese Anweisungen Zeilen der
mysql.func
-Tabelle anlegen und löschen.
Für UDFs sollte zusätzlich zu dem
xxx
-Symbol für die Hauptfunktion
xxx()
mindestens ein weiteres Symbol
definiert sein. Diese Hilfssymbole stehen für die Funktionen
xxx_init()
,
xxx_deinit()
,
xxx_reset()
, xxx_clear()
und xxx_add()
. mysqld
unterstützt überdies eine
--allow-suspicious-udfs
-Option, die steuert,
ob UDFs, die nur das eine xxx
-Symbol haben,
überhaupt geladen werden dürfen. Nach Voreinstellung ist die
Option ausgeschaltet, um zu verhindern, dass aus Shared
Object-Dateien Funktionen geladen werden, die keine gültigen
UDFs sind. Wenn Sie noch mit älteren UDFs arbeiten, die
lediglich das xxx
-Symbol haben und nicht
mit einem Hilfssymbol rekompiliert werden können, kann es
notwendig werden, die Option
--allow-suspicious-udfs
anzugeben. Ansonsten
sollten Sie diese Option möglichst nicht einschalten.
Objektdateien für UDFs dürfen nicht in jedes beliebige
Verzeichnis gelegt werden, sondern nur in das
Plug-In-Verzeichnis des Servers, das Sie am Wert der
Systemvariablen plugin_dir
erkennen
können. (Hinweis: Dies ist
neu in MySQL 5.1. In früheren Versionen von MySQL konnte das
Shared Object in jedem Verzeichnis platziert werden, das der
dynamische Linker des Systems untersuchte.)
Hier wird erklärt, wie Sie vorgehen müssen, um eine neue native Funktion hinzuzufügen. Beachten Sie, dass Sie native Funktionen nicht in eine Binärdistribution einfügen können, da ein Eingriff in den Quellcode von MySQL notwendig ist. Sie müssen MySQL also selbst aus einer Quelldistribution kompilieren. Außerdem müssen Sie bei einer Umstellung auf eine andere MySQL-Version (beispielsweise wenn eine neue Version herauskommt) daran denken, die gesamte Prozedur mit der neuen Version zu wiederholen.
Um eine neue native MySQL-Funktion hinzuzufügen, verfahren Sie folgendermaßen:
Sie binden in lex.h
eine neue Zeile
ein, die den Namen der Funktion im Array
sql_functions[]
definiert.
Wenn der Funktionsprototyp einfach ist (nur null, ein, zwei
oder drei Argumente hat), geben Sie in
lex.h
als zweites Argument im Array
sql_functions[]
den Wert
SYM(FUNC_ARG
an (wobei N
)N
die Anzahl der
Argumente ist) und fügen eine Funktion hinzu, die ein
Funktionsobjekt in item_create.cc
erzeugt. Als Beispiele dafür können Sie sich
"ABS"
und
create_funcs_abs()
anschauen.
Ist der Funktionsprototyp komplizierter (wenn beispielsweise
die Funktion eine variable Anzahl Argumente entgegennimmt),
müssen Sie zwei neue Zeilen in
sql_yacc.yy
einfügen: Die eine gibt
ein Präprozessorsymbol an, das yacc
definieren sollte (diese sollte an den Anfang der Datei
eingefügt werden). Dann definieren Sie die
Funktionsparameter und fügen ein „item“ mit
diesen Parametern zu der Parsing-Regel für
simple_expr
Parametern hinzu. Als
Beispiel dafür, wie dies getan wird, können Sie die
diversen Exemplare von ATAN
in
sql_yacc.yy
anschauen.
Deklarieren Sie in item_func.h
eine
Klasse, die aus Item_num_func
oder
Item_str_func
erbt, je nachdem, ob Ihre
Funktion eine Zahl oder einen String zurückgibt.
In item_func.cc
fügen Sie eine der
folgenden Deklarationen ein, je nachdem, ob Sie eine
numerische oder eine String-Funktion definieren:
double Item_func_newname::val() longlong Item_func_newname::val_int() String *Item_func_newname::Str(String *str)
Wenn Sie Ihr Objekt aus einem der Standardelemente erben
(wie etwa Item_num_func
), müssen Sie
wahrscheinlich nur eine dieser Funktionen definieren und es
dann dem Parent-Objekt überlassen, sich um die übrigen
Funktionen zu kümmern. So definiert zum Beispiel die Klasse
Item_str_func
eine
val()
-Funktion, die
atof()
auf dem Rückgabewert von
::str()
ausführt.
Außerdem müssen Sie wahrscheinlich folgende Objektfunktion definieren:
void Item_func_newname::fix_length_and_dec()
Diese Funktion sollte zumindest
max_length
anhand der gegebenen Argumente
berechnen. max_length
gibt an, wie viele
Zeichen die Funktion höchstens zurückgeben kann. Diese
Funktion sollte überdies maybe_null = 0
einstellen, wenn die Hauptfunktion keinen
NULL
-Wert zurückgeben kann. Ob
irgendwelche Funktionsargumente NULL
zurückgeben könnten, prüft die Funktion anhand der
maybe_null
-Variablen dieser Argumente.
Ein typisches Beispiel dafür finden Sie in
Item_func_mod::fix_length_and_dec
.
Alle Funktionen müssen Thread-sicher sein. Mit anderen Worten: Sie dürfen keine globalen oder statischen Variablen in diesen Funktionen benutzen, ohne sie durch Mutex-Objekte zu schützen.
Wenn Sie NULL
aus einer
::val()
-, ::val_int()
-
oder ::str()
-Funktion zurückgeben möchten,
müssen Sie null_value
auf 1 setzen und 0
zurückliefern.
Für ::str()
-Objektfunktionen müssen Sie
sich darüber hinaus Folgendes merken:
Das String *str
-Argument liefert einen
String-Puffer, der genutzt werden kann, um das Ergebnis zu
speichern. (Um mehr über den Typ String
zu erfahren, werfen Sie einen Blick in die Datei
sql_string.h
.)
Die ::str()
-Funktion sollte den String
zurückgeben, der das Resultat speichert, oder
(char*) 0
, wenn das Resultat
NULL
ist.
Alle gegenwärtigen String-Funktionen versuchen, nur dann Speicher zu reservieren, wenn es absolut unumgänglich ist!
In MySQL können Sie mit der Sprache C++ eine Prozedur definieren,
die Daten in einer Anfrage betrachten und ändern kann, ehe sie
sie an den Client sendet. Diese Änderung kann zeilenweise oder
auf der Ebene von GROUP BY
erfolgen.
Wir haben eine Beispielprozedur geschrieben, um zu zeigen, was alles möglich ist.
Außerdem empfehlen wir Ihnen, einen Blick auf
mylua
zu werfen. Damit können Sie nämlich die
Sprache LUA einsetzen, um eine Prozedur zur Laufzeit in
mysqld zu laden.
analyse([
max_elements
,[max_memory
]])
Diese Prozedur ist in der Datei
sql/sql_analyse.cc
definiert. Sie
untersucht ein Anfrageergebnis und gibt eine Analyse der
Resultate zurück:
max_elements
(Standardwert 256)
ist die Höchstzahl unterschiedlicher Werte, die
analyse
pro Spalte erkennen kann. Diesen
Wert verwendet analyse
, um zu prüfen, ob
ENUM
der optimale Datentyp wäre.
max_memory
(Standardwert 8192)
ist der größtmögliche Speicher, den
analyse
pro Spalte zuweisen kann,
während die Prozedur versucht, alle unterschiedlichen Werte
zu finden.
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max_elements
,[max_memory
]])
Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.