Inhaltsverzeichnis
CREATE PROCEDURE
und CREATE
FUNCTION
ALTER PROCEDURE
und ALTER FUNCTION
DROP PROCEDURE
und DROP FUNCTION
CALL
-AnweisungBEGIN ... END
-Syntax für komplexe AnweisungenDECLARE
-AnweisungMySQL 5.1 kennt auch gespeicherte Routinen (Prozeduren und Funktionen). Eine gespeicherte Prozedur ist eine Menge von SQL-Anweisungen, die auf dem Server gespeichert werden kann. So müssen Clients nicht immer wieder die jeweiligen Einzelanweisungen ausführen, sondern können stattdessen die gespeicherte Prozedur aufrufen.
In folgenden Situationen sind gespeicherte Routinen besonders nützlich:
Wenn mehrere Clientanwendungen in verschiedenen Sprachen geschrieben sind oder auf verschiedenen Plattformen laufen, aber dieselben Datenbankoperationen ausführen müssen.
Wenn Sicherheit sehr wichtig ist. Banken verwenden zum Beispiel für alle häufigen Operationen gespeicherte Prozeduren und Funktionen. Das gewährleistet eine konsistente und sichere Umgebung sowie eine korrekte Protokollierung jeder einzelnen Operation. In einer solchen Umgebung haben Anwendungen und Benutzer keinen Direktzugriff auf die Datenbanktabellen, sondern können nur bestimmte gespeicherte Routinen ausführen.
Gespeicherte Routinen bieten eine bessere Leistung, da weniger Informationen zwischen Server und Client übermittelt werden müssen. Der Nachteil ist der, dass die Belastung des Datenbankservers steigt, weil mehr Arbeit auf der Serverseite und weniger Arbeit auf der Seite des Clients (der Anwendungen) erledigt werden muss. Dies müssen Sie berücksichtigen, wenn viele Clientcomputer (wie beispielsweise Webserver) von nur einem oder sehr wenigen Datenbankservern bedient werden.
Mit gespeicherten Routinen sind Funktionsbibliotheken im Datenbankserver möglich. Dieses Feature haben auch moderne Anwendungssprachen, die einen solchen Entwurf intern umsetzen (zum Beispiel durch Klassen). Auch jenseits von Datenbankanwendungen bringen diese Features der Anwendungssprachen dem Programmierer Vorteile.
MySQL verwendet die Syntax für gespeicherte Routinen gemäß dem SQL:2003-Standard, den auch DB2 von IBM nutzt.
Die Implementierung gespeicherter Routinen in MySQL ist noch nicht abgeschlossen. Unterstützt wird allein die in diesem Kapitel beschriebene Syntax. Beschränkungen und Erweiterungen werden an geeigneter Stelle geschildert. Eine weitergehende Behandlung der Restriktionen, die für die Verwendung gespeicherter Routinen gelten, finden Sie in Abschnitt I.1, „Beschränkungen bei gespeicherten Routinen und Triggern“.
Das Binärlogging für gespeicherte Routinen wird in Abschnitt 19.4, „Binärloggen gespeicherter Routinen und Trigger“ beschrieben.
Für gespeicherte Routinen muss die Tabelle
proc
in der mysql
-Datenbank
vorhanden sein. Diese Tabelle wird schon bei der
Installationsprozedur von MySQL 5.1 angelegt. Wenn
Sie von einer älteren Version auf MySQL 5.1
aufrüsten, achten Sie bitte darauf, Ihre Berechtigungstabellen zu
aktualisieren, damit die Tabelle proc
vorhanden
ist. Siehe auch Abschnitt 5.6, „mysql_fix_privilege_tables — Upgrade von MySQL-Systemtabellen“.
Nach Anweisungen, die gespeicherte Routinen erzeugen, ändern oder
löschen, bearbeitet der Server die Tabelle
mysql.proc
. Eine manuelle Manipulation dieser
Tabelle wird der Server nicht bemerken.
Im Berechtigungssystem von MySQL werden gespeicherte Routinen folgendermaßen behandelt:
Das CREATE ROUTINE
-Recht ist erforderlich,
um gespeicherte Routinen zu erzeugen.
Das ALTER ROUTINE
-Recht wird benötigt, um
gespeicherte Routinen zu ändern oder zu löschen. Dieses
Recht wird automatisch dem Erzeuger einer Routine erteilt.
Das EXECUTE
-Recht ist notwendig, um
gespeicherte Routinen auszuführen, wird aber ebenfalls dem
Erzeuger einer Routine automatisch erteilt. Das
voreingestellte SQL SECURITY
-Merkmal einer
Routine ist DEFINER
und erlaubt es
Benutzern, mit denen die Routine verbunden ist, und die
darüber hinaus Datenbankzugriff haben, die Routine
auszuführen.
CREATE PROCEDURE
und CREATE
FUNCTION
ALTER PROCEDURE
und ALTER FUNCTION
DROP PROCEDURE
und DROP FUNCTION
CALL
-AnweisungBEGIN ... END
-Syntax für komplexe AnweisungenDECLARE
-Anweisung
Eine gespeicherte Routine ist entweder eine Prozedur oder eine
Funktion. Gespeicherte Routinen werden mit den Anweisungen
CREATE PROCEDURE
und CREATE
FUNCTION
angelegt. Eine Prozedur wird mit einer
CALL
-Anweisung aufgerufen und kann nur über
Ausgabevariablen Werte zurückgeben. Eine Funktion kann innerhalb
einer Anweisung aufgerufen werden - wie jede andere Funktion auch
(also durch Nennung ihres Namens) - und einen Skalarwert
zurückliefern. Gespeicherte Routinen können andere gespeicherte
Routinen aufrufen.
Eine gespeicherte Prozedur oder Funktion steht mit einer bestimmten Datenbank in Zusammenhang. Das hat Folgen:
Wenn die Routine aufgerufen wird, wird implizit ein
USE
ausgeführt (und wieder rückgängig gemacht, wenn die Routine
endet). db_name
USE
-Anweisungen sind in
gespeicherten Routinen nicht zulässig.
Den Namen einer Routine können Sie mit einem Datenbanknamen
qualifizieren, etwa wenn Sie eine Routine benutzen möchten,
die nicht in der aktuellen Datenbank vorliegt. Um
beispielsweise eine Prozedur p
oder eine
Funktion f
aus der Datenbank
test
aufzurufen, können Sie CALL
test.p()
oder test.f()
sagen.
Wird eine Datenbank gelöscht, so werden alle ihre gespeicherten Routinen mitgelöscht.
MySQL unterstützt die praktischen Erweiterungen, die eine Nutzung
der regulären SELECT
-Anweisungen innerhalb von
gespeicherten Prozeduren ermöglichen (also ohne Cursors oder
lokale Variablen). Die Ergebnismenge einer solchen Anfrage wird
einfach direkt an den Client gesandt. Da mehrere
SELECT
-Anweisungen mehrere Ergebnismengen
generieren, muss der Client eine MySQL-Clientbibliothek verwenden,
die mehrere Ergebnismengen unterstützt. Das bedeutet, dass er die
Clientbibliothek einer MySQL-Version benötigt, die mindestens so
jung ist wie 4.1. Außerdem sollte der Client die Option
CLIENT_MULTI_STATEMENTS
angeben, wenn er sich
verbindet. C-Programme können dies mit der C-API-Funktion
mysql_real_connect()
tun (siehe
Abschnitt 24.2.3.51, „mysql_real_connect()
“).
Die folgenden Abschnitte beschreiben die Syntax, mit der gespeicherte Prozeduren und Funktionen erzeugt, geändert und aufgerufen werden.
CREATE PROCEDUREsp_name
([proc_parameter
[,...]]) [characteristic
...]routine_body
CREATE FUNCTIONsp_name
([func_parameter
[,...]]) RETURNStype
[characteristic
...]routine_body
proc_parameter
: [ IN | OUT | INOUT ]param_name
type
func_parameter
:param_name
type
type
:Any valid MySQL data type
characteristic
: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string
'routine_body
:Valid SQL procedure statement
Diese Anweisungen erzeugen gespeicherte Routinen. Um sie nutzen
zu können, ist das CREATE ROUTINE
-Recht
erforderlich. Wenn Binärlogging eingeschaltet ist, kann für
die CREATE FUNCTION
-Anweisung auch das
SUPER
-Recht erforderlich sein, wie in
Abschnitt 19.4, „Binärloggen gespeicherter Routinen und Trigger“, beschrieben. MySQL
erteilt dem Erzeuger einer Routine automatisch das
ALTER ROUTINE
- und das
EXECUTE
-Recht.
Nach Voreinstellung wird die Routine mit der Standarddatenbank
verbunden. Um sie explizit mit einer bestimmten Datenbank zu
verbinden, geben Sie bei der Erzeugung der Routine den Namen der
Datenbank in der Form db_name.sp_name
an.
Wenn der Name der Routine gleichlautend mit dem Namen einer eingebauten SQL-Funktion ist, müssen Sie zwischen dem Namen und der nachfolgenden Klammer ein Leerzeichen setzen, wenn Sie die Routine definieren, sonst tritt ein Syntaxfehler ein. Das gilt auch, wenn Sie die Routine zu einem späteren Zeitpunkt aufrufen. Aus diesem Grund raten wir Ihnen, keine Namen vorhandener SQL-Funktionen für Ihre eigenen gespeicherten Routinen zu verwenden.
Der SQL-Modus IGNORE_SPACE
gilt für
eingebaute Funktionen und nicht für gespeicherte Routinen. Es
ist immer zulässig, Leerzeichen hinter den Namen einer Routine
zu setzen, egal ob IGNORE_SPACE
eingeschaltet
ist oder nicht.
Die Parameterliste in runden Klammern muss immer vorhanden sein.
Wenn keine Parameter übergeben werden, muss eine leere
Parameterliste ()
verwendet werden. Jeder
Parameter ist nach Voreinstellung ein
IN
-Parameter. Um einen Parameter
anderslautend zu definieren, setzen Sie das Schlüsselwort
OUT
oder INOUT
vor den
Parameternamen.
Hinweis: Als
IN
, OUT
oder
INOUT
können nur
PROCEDURE
-Parameter definiert werden.
(FUNCTION
-Parameter gelten immer als
IN
-Parameter.)
Jeder Parameter kann mit jedem zulässigen Datentyp deklariert
werden, nur das Attribut COLLATE
darf nicht
verwendet werden.
Die RETURNS
-Klausel kann nur für eine
FUNCTION
angegeben werden; hier ist sie sogar
obligatorisch. Sie gibt den Rückgabetyp der Funktion an. Der
Funktionsrumpf muss eine RETURN
-Anweisung enthalten.
value
Der routine_body
besteht aus einer
gültigen SQL-Prozeduranweisung. Diese kann eine einfache
Anweisung wie etwa ein SELECT
oder
INSERT
sein, sie kann aber auch eine
zusammengesetzte Anweisung mit BEGIN
und
END
sein. Die Syntax zusammengesetzter
Anweisungen wird in Abschnitt 19.2.5, „BEGIN ... END
-Syntax für komplexe Anweisungen“, erläutert.
Zusammengesetzte Anweisungen können Deklarationen, Schleifen
und andere Anweisungen mit Kontrollstrukturen enthalten. Die
Syntax dieser Anweisungen wird weiter unten in diesem Kapitel
erklärt, beispielsweise unter Abschnitt 19.2.6, „Syntax der DECLARE
-Anweisung“, und
Abschnitt 19.2.10, „Konstrukte für die Ablaufsteuerung“.
Die CREATE FUNCTION
-Anweisung diente in
früheren MySQL-Versionen der Unterstützung von UDFs
(User-defined Functions, benutzerdefinierte Funktionen). Siehe
auch Abschnitt 26.3, „Hinzufügen neuer Funktionen zu MySQL“. UDFs werden weiterhin
unterstützt, obwohl inzwischen auch die gespeicherten
Funktionen existieren. Eine UDF kann man als externe
gespeicherte Funktion betrachten. Bitte beachten Sie jedoch,
dass gespeicherte Funktionen denselben Namensraum wie UDFs
benutzen.
Eine Prozedur oder Funktion gilt als
„deterministisch“, wenn sie für gleiche
Eingabeparameter immer gleiche Resultate erzeugt; ansonsten ist
sie „nichtdeterministisch“. Wenn in der Definition
der Routine weder DETERMINISTIC
noch
NOT DETERMINISTIC
steht, ist die
Voreinstellung NOT DETERMINISTIC
.
Im Zusammenhang mit Replikation wird eine Routine durch
Verwendung der Funktion NOW()
(oder ihrer
Synonyme) oder RAND()
nicht unbedingt
nichtdeterministisch. Für NOW()
enthält das
Binärlog den Zeitstempel und repliziert korrekt.
RAND()
repliziert ebenfalls richtig, sofern
es in einer Routine nur ein einziges Mal aufgerufen wird. (Den
Ausführungszeitstempel der Routine und den Zufallszahlen-Seed
können Sie als implizite Eingaben betrachten, die auf Master
und Slave identisch sind.)
Zurzeit wird das DETERMINISTIC
-Merkmal vom
Optimierer zwar akzeptiert, aber noch nicht benutzt. Wenn jedoch
Binärlogging eingeschaltet ist, nimmt dieses Merkmal Einfluss
darauf, welche Routinendefinitionen von MySQL akzeptiert werden.
Siehe Abschnitt 19.4, „Binärloggen gespeicherter Routinen und Trigger“.
Einige Merkmale informieren über das Wesen der von der Routine
verwendeten Daten. CONTAINS SQL
weist darauf
hin, dass die Routine keine Anweisungen zum Lesen oder Schreiben
von Daten enthält. NO SQL
bedeutet, dass sie
keine SQL-Anweisungen enthält. READS SQL
DATA
kennzeichnet Routinen mit lesenden, aber ohne
schreibende Anweisungen und MODIFIES SQL DATA
Routinen mit Anweisungen, die Daten schreiben können.
CONTAINS SQL
ist der Standardwert, wenn kein
anderes dieser Merkmale explizit angegeben ist. Die Merkmale
haben nur beratenden Charakter. Sie schränken den Server nicht
ein, wenn es darum geht, welche Arten von Anweisungen
ausgeführt werden dürfen oder nicht.
Das Merkmal SQL SECURITY
gibt an, ob die
Routine mit den Berechtigungen des Benutzers, der sie erzeugte,
oder des Benutzers, der sie aufruft, ausgeführt werden soll.
Der Standardwert ist DEFINER
. Dieses Feature
ist neu in SQL:2003. Der Erzeuger oder Aufrufer muss die
Zugriffsberechtigung auf die Datenbank haben, mit der die
Routine verbunden ist. Um die Routine auszuführen, ist das
EXECUTE
-Recht erforderlich. Der Benutzer, der
diese Berechtigung haben muss, kann nur entweder der Erzeuger
oder der Aufrufer sein, je nachdem, wie SQL
SECURITY
eingestellt ist.
MySQL speichert die Einstellung der Systemvariablen
sql_mode
, die gerade in Kraft ist, wenn eine
Routine erzeugt wird, und führt diese Routine dann immer mit
dieser Einstellung aus.
Beim Aufruf der Routine wird ein implizites USE
ausgeführt (und
wieder rückgängig gemacht, wenn die Routine endet).
db_name
USE
-Anweisungen sind in gespeicherten
Routinen nicht erlaubt.
Der Server verwendet den Datentyp eines Routinenparameters oder
Funktionsrückgabewerts wie folgt. Diese Regeln gelten auch für
lokale Laufzeitvariablen, die mit der
DECLARE
-Anweisung angelegt wurden
(Abschnitt 19.2.7.1, „Lokale DECLARE
-Variablen“).
Zuweisungen werden auf Datentypinkompatibilitäten und Überlauf überprüft. Konvertierungs- und Überlaufprobleme werden mit Warnungen oder im Strict-Modus sogar mit Fehlern quittiert.
Für Zeichendatentypen gilt: Wenn in der Deklaration eine
CHARACTER SET
-Klausel steht, wird der
angegebene Zeichensatz mit seiner Standardkollation
verwendet. Fehlt eine solche Klausel, werden der Zeichensatz
und die Kollation der Datenbank benutzt. (Diese sind durch
die Systemvariablen
character_set_database
und
collation_database
vorgegeben.)
Parametern oder Variablen können nur Skalarwerte zugewiesen
werden. Eine Anweisung wie etwa SET x = (SELECT 1,
2)
wäre ungültig.
Die COMMENT
-Klausel ist eine
MySQL-Erweiterung und kann zur Beschreibung der gespeicherten
Routine genutzt werden. Diese Information wird mit den
Anweisungen SHOW CREATE PROCEDURE
und
SHOW CREATE FUNCTION
angezeigt.
In MySQL dürfen Routinen auch DDL-Anweisungen wie
beispielsweise CREATE
und
DROP
enthalten. Darüber hinaus dürfen
gespeicherte Prozeduren (nicht aber gespeicherte Funktionen) in
MySQL auch Transaktionsanweisungen in SQL enthalten, wie etwa
COMMIT
. Gespeicherte Funktionen dürfen keine
Anweisungen enthalten, die explizit oder implizit ein Commit
oder Rollback ausführen. Eine Unterstützung für diese
Anweisungen wird vom SQL-Standard auch gar nicht verlangt, der
Standard besagt lediglich, dass jeder DBMS-Hersteller selbst
entscheiden kann, ob er sie erlauben will oder nicht.
Gespeicherte Routinen dürfen kein LOAD DATA
INFILE
enthalten.
Anweisungen, die eine Ergebnismenge zurückgeben, dürfen nicht
innerhalb einer gespeicherten Funktion verwendet werden. Dazu
gehören auch SELECT
-Anweisungen, soweit sie
nicht mithilfe einer INTO
-Klausel
Spaltenwerte in Variablen laden, sowie SHOW
und andere Anweisungen wie beispielsweise
EXPLAIN
. Anweisungen, für die zur
Definitionszeit festgelegt werden kann, dass sie eine
Ergebnismenge zurückliefern, lösen einen Not allowed
to return a result set from a function
-Fehler aus
(ER_SP_NO_RETSET_IN_FUNC
). Anweisungen, für
die nur zur Laufzeit festgelegt werden kann, dass sie eine
Ergebnismenge zurückliefern, lösen einen PROCEDURE %s
can't return a result set in the given context
-Fehler
aus (ER_SP_BADSELECT
).
Das folgende Beispiel zeigt eine einfache gespeicherte Prozedur
mit einem OUT
-Parameter. Das Beispiel
verwendet den mysql-Clientbefehl
delimiter
, um das Begrenzungszeichen für die
Anweisung von ;
in //
zu
ändern, während die Prozedur definiert wird. So kann das
Begrenzungszeichen ;
im Prozedurrumpf an den
Server durchgereicht werden, ohne von mysql
selbst interpretiert zu werden.
mysql>delimiter //
mysql>CREATE PROCEDURE simpleproc (OUT param1 INT)
->BEGIN
->SELECT COUNT(*) INTO param1 FROM t;
->END;
->//
Query OK, 0 rows affected (0.00 sec) mysql>delimiter ;
mysql>CALL simpleproc(@a);
Query OK, 0 rows affected (0.00 sec) mysql>SELECT @a;
+------+ | @a | +------+ | 3 | +------+ 1 row in set (0.00 sec)
Wenn Sie den Befehl delimiter
benutzen, geben
Sie bitte nicht das Backslash-Zeichen
(‘\
’) an, da es das Escape-Symbol
für MySQL ist.
Das folgende Beispiel zeigt eine Funktion, die einen Parameter
entgegennimmt, eine Operation mit einer SQL-Funktion ausführt
und das Ergebnis zurückliefert. In diesem Fall ist es nicht
nötig, delimiter
zu benutzen, da die
Funktionsdefinition keine ;
-Zeichen als
interne Anweisungstrennzeichen enthält:
mysql>CREATE FUNCTION hello (s CHAR(20)) RETURNS CHAR(50)
->RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec) mysql>SELECT hello('world');
+----------------+ | hello('world') | +----------------+ | Hello, world! | +----------------+ 1 row in set (0.00 sec)
Eine gespeicherte Funktion gibt einen Wert zurück, dessen
Datentyp in ihrer RETURNS
-Klausel angegeben
ist. Wenn die RETURN
-Anweisung einen Wert
eines anderen Typs zurückgibt, wird dieser mit Gewalt in den
richtigen Typ umgewandelt. Wenn eine Funktion beispielsweise
einen Rückgabewert vom Typ ENUM
oder
SET
hat, die
RETURN
-Anweisung jedoch einen Integer
zurückgibt, so liefert die Funktion den String für das
entsprechende ENUM
-Element oder die Menge der
SET
-Elemente.
ALTER {PROCEDURE | FUNCTION}sp_name
[characteristic
...]characteristic
: { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string
'
Mit dieser Anweisung können die Merkmale einer gespeicherten
Prozedur oder Funktion geändert werden. Hierzu benötigen Sie
das ALTER ROUTINE
-Recht für die betreffende
Routine. (Diese Berechtigung wird dem Erzeuger einer Routine
automatisch erteilt.) Wenn Binärlogging eingeschaltet ist, kann
für die ALTER FUNCTION
-Anweisung unter
Umständen auch die SUPER
-Berechtigung
erforderlich werden, wie in
Abschnitt 19.4, „Binärloggen gespeicherter Routinen und Trigger“, beschrieben.
In einer einzigen ALTER PROCEDURE
- oder
ALTER FUNCTION
-Anweisung können auch mehrere
Änderungen verlangt werden.
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name
Diese Anweisung löscht eine gespeicherte Prozedur oder Funktion
in dem Sinne, dass die darin genannte Routine von dem Server
entfernt wird. Hierzu benötigen Sie das ALTER
ROUTINE
-Recht für die betreffende Routine. (Diese
Berechtigung wird dem Erzeuger einer Routine automatisch
erteilt.)
Die IF EXISTS
-Klausel ist eine
MySQL-Erweiterung. Sie verhindert, dass ein Fehler auftritt,
wenn die Prozdur oder Funktion nicht existiert. Es wird eine
Warnung ausgegeben, die mit SHOW WARNINGS
angezeigt werden kann.
CALLsp_name
([parameter
[,...]])
Die CALL
-Anweisung ruft eine mit
CREATE PROCEDURE
erstellte Prozedur auf.
CALL
kann Werte mithilfe von
OUT
- oder INOUT
-Parametern
an den Aufrufer zurückgeben. Außerdem kann
CALL
die Anzahl der betroffenen Zeilen
„zurückgeben“, wenn auf der SQL-Ebene die Funktion
ROW_COUNT()
oder auf der C-Ebene die Funktion
mysql_affected_rows()
aus der C-API
aufgerufen wird.
[begin_label
:] BEGIN [statement_list
] END [end_label
]
Die BEGIN ... END
-Syntax wird für
zusammengesetzte Anweisungen verwendet, die in gespeicherten
Routinen und Triggern auftreten können. Eine zusammengesetzte
Anweisung enthält mehrere Anweisungen, die zwischen den
Schlüsselwörtern BEGIN
und
END
stehen. Die
statement_list
ist eine Liste mit
einer oder mehreren Anweisungen. Jede Anweisung der
statement_list
muss durch ein
Semikolon (;
) abgegrenzt werden. Beachten
Sie, dass eine statement_list
optional ist, sodass auch eine leere zusammengesetzte Anweisung
(BEGIN END
) zulässig wäre.
Um überhaupt mehrere Anweisungen verbinden zu können, muss ein
Client in der Lage sein, Strings von Anweisungen zu senden, die
durch das Trennzeichen ;
abgegrenzt sind.
Dafür sorgt der Kommandozeilen-Client mysql
mit dem Befehl delimiter
. Wenn Sie das
Begrenzungszeichen für Anweisungen von ;
auf
etwas anderes umstellen (beispielsweise ein
//
), dann kann ;
auch im
Rumpf der Routine verwendet werden. Ein Beispiel finden Sie in
Abschnitt 19.2.1, „CREATE PROCEDURE
und CREATE
FUNCTION
“.
Eine zusammengesetzte Anweisung kann auch beschriftet sein. Ein
end_label
kann allerdings nur
verwendet werden, wo auch ein
begin_label
vorhanden ist. Wo beide
vorhanden sind, müssen sie identisch sein.
Die optionale Klausel [NOT] ATOMIC
wird noch
nicht unterstützt. Dies bedeutet, dass am Anfang eines
Anweisungsblocks kein Transaktions-Savepoint gesetzt wird und
dass eine BEGIN
-Klausel in diesem Kontext
keinen Einfluss auf die aktuelle Transaktion hat.
Die DECLARE
-Anweisung definiert Elemente als
lokal in einer Routine:
Lokale Variablen. Siehe Abschnitt 19.2.7, „Variablen in gespeicherten Routinen“.
Bedingungen und Handler. Siehe Abschnitt 19.2.8, „Bedingungen und Handler“.
Cursors. Siehe Abschnitt 19.2.9, „Cursor“.
Die Anweisungen SIGNAL
und
RESIGNAL
werden zurzeit nicht unterstützt.
DECLARE
ist nur in einer zusammengesetzten
Anweisung mit BEGIN ... END
zulässig und
muss ganz am Anfang vor allen anderen Anweisungen stehen.
Deklarationen müssen in einer bestimmten Reihenfolge stehen. Cursors müssen vor Handlern und Variablen und Bedingungen vor Cursors und Handlern deklariert werden.
Auch Variablen können in einer Routine deklariert und benutzt werden.
DECLAREvar_name
[,...]type
[DEFAULTvalue
]
Diese Anweisung deklariert lokale Variablen. Um der Variablen
einen Standardwert beizugeben, schreiben Sie eine
DEFAULT
-Klausel in die Deklaration. Der
Wert kann auch als Ausdruck angegeben werden, er muss nicht
unbedingt eine Konstante sein. Ist keine
DEFAULT
-Klausel vorhanden, ist der
Anfangswert der Variablen NULL
.
Lokale Variablen werden im Hinblick auf Datentyp und
Speicherüberlauf wie Routinenparameter behandelt. Siehe auch
Abschnitt 19.2.1, „CREATE PROCEDURE
und CREATE
FUNCTION
“.
Eine lokale Variable gilt innerhalb des BEGIN ...
END
-Blocks, in dem sie deklariert ist. Die Variable
kann auch in Blöcken verwendet werden, die in den
deklarierenden Block eingeschachtelt sind, sofern dort nicht
eine Variable mit demselben Namen deklariert ist.
SETvar_name
=expr
[,var_name
=expr
] ...
Die SET
-Anweisung in gespeicherten Routinen
ist eine erweiterte Version der allgemeinen
SET
-Anweisung. Die Variablen können in
einer Routine deklarierte Variablen oder globale
Systemvariablen sein.
Die SET
-Anweisung in gespeicherten Routinen
ist als Teil der bereits existierenden
SET
-Syntax implementiert. Diese ermöglicht
nämlich eine erweiterte Syntax mit SET a=x, b=y,
...
, wobei verschiedene Variablentypen (lokal
deklarierte Variablen sowie globale und
Session-Servervariablen) vermischt werden können. Das
ermöglicht auch Kombinationen von lokalen Variablen und
einigen Optionen, die nur für Systemvariablen sinnvoll sind.
In diesem Fall werden die Optionen zwar erkannt, aber
ignoriert.
SELECTcol_name
[,...] INTOvar_name
[,...]table_expr
Diese SELECT
-Syntax speichert die
ausgewählten Spalten direkt in Variablen. Daher kann nur eine
einzige Zeile abgerufen werden.
SELECT id,data INTO x,y FROM test.t1 LIMIT 1;
Bei den Namen von Benutzervariablen wird nicht zwischen Groß- und Kleinschreibung unterschieden. Siehe Abschnitt 9.3, „Benutzerdefinierte Variablen“.
Wichtig: In SQL dürfen
Variablennamen und Spaltennamen nicht gleich sein. Wenn eine
SQL-Anweisung wie etwa ein SELECT ... INTO
eine Spalte und eine mit demselben Namen deklarierte lokale
Variable benutzt, interpretiert MySQL in der gegenwärtigen
Version diese Referenz als den Namen der Variablen. So wird
beispielsweise xname
in der folgenden
Anweisung als die Variable und nicht als
die Spalte mit dem Namen
xname
interpretiert:
CREATE PROCEDURE sp1 (x VARCHAR(5)) BEGIN DECLARE xname VARCHAR(5) DEFAULT 'bob'; DECLARE newname VARCHAR(5); DECLARE xid INT; SELECT xname,id INTO newname,xid FROM table1 WHERE xname = xname; SELECT newname; END;
Wenn diese Prozedur aufgerufen wird, gibt die Variable
newname
immer den Wert
'bob'
zurück, egal welchen Wert die Spalte
table1.xname
auch immer haben mag.
Siehe auch Abschnitt I.1, „Beschränkungen bei gespeicherten Routinen und Triggern“.
Manche Bedingungen erfordern eine Sonderbehandlung, darunter Bedingungen, die Fehler oder den allgemeinen Kontrollfluss innerhalb einer Routine steuern.
DECLAREcondition_name
CONDITION FORcondition_value
condition_value
: SQLSTATE [VALUE]sqlstate_value
|mysql_error_code
Diese Anweisung spezifiziert Bedingungen, die speziell
behandelt werden müssen. Sie verbindet einen Namen mit einer
Fehlerbedingung. Dieser Name kann danach in einer
DECLARE HANDLER
-Anweisung eingesetzt
werden. Siehe Abschnitt 19.2.8.2, „DECLARE
-Handler“.
Ein condition_value
kann ein
SQLSTATE-Wert oder ein MySQL-Fehlercode sein.
DECLAREhandler_type
HANDLER FORcondition_value
[,...]statement
handler_type
: CONTINUE | EXIT | UNDOcondition_value
: SQLSTATE [VALUE]sqlstate_value
|condition_name
| SQLWARNING | NOT FOUND | SQLEXCEPTION |mysql_error_code
Die DECLARE ... HANDLER
-Anweisung
deklariert Handler, die jeweils eine oder mehrere Bedingungen
behandeln können. Wenn eine dieser Bedingungen eintritt, wird
das angegebene statement
ausgeführt. statement
kann eine
einfache Anweisung sein (beispielsweise SET
) oder auch eine
zusammengesetzte Anweisung in einem var_name
=
value
BEGIN
... END
-Block (siehe
Abschnitt 19.2.5, „BEGIN ... END
-Syntax für komplexe Anweisungen“).
Ein CONTINUE
-Handler lässt die Ausführung
der aktuellen Routine nach der Ausführung der
Handler-Anweisung weiterlaufen. Ein
EXIT
-Handler dagegen beendet die
Ausführung für die zusammengesetzte BEGIN ...
END
-Anweisung, in der er deklariert ist. (Das gilt
selbst dann, wenn die Bedingung in einem inneren Block
eintritt.) Eine Anweisung des Typs
UNDO
-Handler wird zurzeit noch nicht
unterstützt.
Wenn eine Bedingung eintritt, für die kein Handler deklariert
wurde, ist die Standardaktion ein EXIT
.
Ein condition_value
kann einer der
folgenden Werte sein:
Ein SQLSTATE-Wert oder MySQL-Fehlercode.
Ein zuvor mit DECLARE ... CONDITION
deklarierter Bedingungsname. Siehe
Abschnitt 19.2.8.1, „DECLARE
-Bedingungen“.
SQLWARNING
ist eine Abkürzung für
alle SQLSTATE-Codes, die mit 01
beginnen.
NOT FOUND
ist eine Abkürzung für alle
SQLSTATE-Codes, die mit 02
beginnen.
SQLEXCEPTION
ist eine Abkürzung für
alle SQLSTATE-Codes, die nicht unter
SQLWARNING
oder NOT
FOUND
fallen.
Beispiel:
mysql>CREATE TABLE test.t (s1 int,primary key (s1));
Query OK, 0 rows affected (0.00 sec) mysql>delimiter //
mysql>CREATE PROCEDURE handlerdemo ()
->BEGIN
->DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
->SET @x = 1;
->INSERT INTO test.t VALUES (1);
->SET @x = 2;
->INSERT INTO test.t VALUES (1);
->SET @x = 3;
->END;
->//
Query OK, 0 rows affected (0.00 sec) mysql>CALL handlerdemo()//
Query OK, 0 rows affected (0.00 sec) mysql>SELECT @x//
+------+ | @x | +------+ | 3 | +------+ 1 row in set (0.00 sec)
Dieses Beispiel verbindet einen Handler mit der Bedingung
SQLSTATE 23000, die bei doppelten Schlüsselwerten eintritt.
Beachten Sie, dass @x
die
3
ist: Dies zeigt, dass MySQL die Prozedur
bis zum Ende ausgeführt hat. Wäre die Zeile DECLARE
CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
nicht vorhanden, hätte MySQL den Standardweg
(EXIT
) eingeschlagen, nachdem das zweite
INSERT
an dem PRIMARY
KEY
-Constraint gescheitert ist, und SELECT
@x
hätte eine 2
zurückgegeben.
Wenn Sie eine Bedingung ignorieren möchten, können Sie einen
CONTINUE
-Handler für sie deklarieren und
mit einem leeren Block verbinden. Zum Beispiel:
DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;
Einfache Cursors werden in gespeicherten Prozeduren und Funktionen unterstützt. Die Syntax ist dieselbe wie in eingebettetem SQL. Gegenwärtig sind Cursors asensitiv, nur-lesend und nicht scrollbar. Asensitiv bedeutet, dass der Server eine Kopie der Ergebnistabelle anfertigen kann, aber nicht muss.
Cursors müssen vor Handlern und Variablen und Bedingungen vor Cursors und Handlern deklariert werden.
Beispiel:
CREATE PROCEDURE curdemo() BEGIN DECLARE done INT DEFAULT 0; DECLARE a CHAR(16); DECLARE b,c INT; DECLARE cur1 CURSOR FOR SELECT id,data FROM test.t1; DECLARE cur2 CURSOR FOR SELECT i FROM test.t2; DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; OPEN cur1; OPEN cur2; REPEAT FETCH cur1 INTO a, b; FETCH cur2 INTO c; IF NOT done THEN IF b < c THEN INSERT INTO test.t3 VALUES (a,b); ELSE INSERT INTO test.t3 VALUES (a,c); END IF; END IF; UNTIL done END REPEAT; CLOSE cur1; CLOSE cur2; END
DECLAREcursor_name
CURSOR FORselect_statement
Diese Anweisung deklariert einen Cursor. Es können zwar mehrere Cursors in einer Routine deklariert werden, aber in einem gegebenen Block muss jeder Cursor einen eindeutigen Namen haben.
Die SELECT
-Anweisung darf keine
INTO
-Klausel haben.
Die Konstrukte IF
, CASE
,
LOOP
, WHILE
,
REPLACE
ITERATE
und
LEAVE
sind vollständig implementiert.
Viele dieser Konstrukte können andere Anweisungen enthalten,
wie die Grammatikspezifikationen in den nachfolgenden
Abschnitten zeigen. Solche Konstrukte können auch geschachtelt
werden. So kann beispielsweise eine
IF
-Anweisung eine
WHILE
-Schleife enthalten, die ihrerseits eine
CASE
-Anweisung enthält.
FOR
-Schleifen werden zurzeit nicht
unterstützt.
IFsearch_condition
THENstatement_list
[ELSEIFsearch_condition
THENstatement_list
] ... [ELSEstatement_list
] END IF
IF
implementiert ein einfaches
Bedingungskonstrukt. Wenn die
search_condition
zutrifft, wird die
zugehörige SQL-Anweisungsliste ausgeführt. Trifft keine
search_condition
zu, wird die
Anweisungsliste aus der ELSE
-Klausel
ausgeführt. Jede statement_list
besteht aus einer oder mehreren Anweisungen.
Hinweis: Es gibt auch eine
IF()
-Funktion, die
sich von der hier beschriebenen
IF
-Anweisung
unterscheidet. Siehe Abschnitt 12.2, „Ablaufsteuerungsfunktionen“.
CASEcase_value
WHENwhen_value
THENstatement_list
[WHENwhen_value
THENstatement_list
] ... [ELSEstatement_list
] END CASE
Oder:
CASE WHENsearch_condition
THENstatement_list
[WHENsearch_condition
THENstatement_list
] ... [ELSEstatement_list
] END CASE
Die CASE
-Anweisung für gespeicherte
Routinen implementiert ein komplexes Bedingungskonstrukt. Wenn
die search_condition
zutrifft, wird
die zugehörige SQL-Anweisungsliste ausgeführt. Trifft keine
search_condition
zu, wird die
Anweisungsliste aus der ELSE
-Klausel
ausgeführt. Jede statement_list
besteht aus einer oder mehreren Anweisungen.
Hinweis: Die Syntax der hier
gezeigten
CASE
-Anweisung, die in
gespeicherten Routinen zum Einsatz kommt, unterscheidet sich
von der Syntax des
CASE
-Ausdrucks von
SQL, die in Abschnitt 12.2, „Ablaufsteuerungsfunktionen“,
beschrieben wird. Die CASE
-Anweisung darf
keine ELSE NULL
-Klausel haben und wird mit
END CASE
anstelle von
END
abgeschlossen.
[begin_label
:] LOOPstatement_list
END LOOP [end_label
]
LOOP
implementiert ein einfaches
Schleifenkonstrukt, das eine wiederholte Ausführung der aus
einer oder mehreren Anweisungen bestehenden Anweisungsliste
ermöglicht. Die Anweisungen werden in der Schleife so lange
wiederholt, bis die Schleife abgebrochen wird. Dies geschieht
normalerweise mit der LEAVE
-Anweisung.
Eine LOOP
-Anweisung kann auch beschriftet
sein. Ein end_label
kann allerdings
nur verwendet werden, wo auch ein
begin_label
vorhanden ist. Wo beide
vorhanden sind, müssen sie identisch sein.
LEAVE label
Diese Anweisung beendet ein beschriftetes Konstrukt zur
Flusskontrolle. Sie kann in einem BEGIN ...
END
-Block oder in Schleifenkonstrukten
(LOOP
, REPEAT
,
WHILE
) verwendet werden.
ITERATE label
ITERATE
kann nur in
LOOP
-, REPEAT
- und
WHILE
-Anweisungen auftreten.
ITERATE
bedeutet „Durchlaufe die
Schleife noch einmal“.
Beispiel:
CREATE PROCEDURE doiterate(p1 INT) BEGIN label1: LOOP SET p1 = p1 + 1; IF p1 < 10 THEN ITERATE label1; END IF; LEAVE label1; END LOOP label1; SET @x = p1; END
[begin_label
:] REPEATstatement_list
UNTILsearch_condition
END REPEAT [end_label
]
Die Anweisungsliste in einer
REPEAT
-Anweisung wird wiederholt, bis die
Suchbedingung search_condition
zutrifft. Somit geht ein REPEAT
immer
mindestens einmal in die Schleife. Die
statement_list
besteht aus einer
oder mehreren Anweisungen.
Eine REPEAT
-Anweisung kann auch beschriftet
sein. Ein end_label
kann allerdings
nur verwendet werden, wo auch ein
begin_label
vorhanden ist. Wo beide
vorhanden sind, müssen sie identisch sein.
Beispiel:
mysql>delimiter //
mysql>CREATE PROCEDURE dorepeat(p1 INT)
->BEGIN
->SET @x = 0;
->REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
->END
->//
Query OK, 0 rows affected (0.00 sec) mysql>CALL dorepeat(1000)//
Query OK, 0 rows affected (0.00 sec) mysql>SELECT @x//
+------+ | @x | +------+ | 1001 | +------+ 1 row in set (0.00 sec)
[begin_label
:] WHILEsearch_condition
DOstatement_list
END WHILE [end_label
]
Die Anweisungsliste in einer
WHILE
-Anweisung wird wiederholt, solange
die search_condition
zutrifft. Die
statement_list
besteht aus einer
oder mehreren Bedingungen.
Eine WHILE
-Anweisung kann auch beschriftet
sein. Ein end_label
kann allerdings
nur verwendet werden, wo auch ein
begin_label
vorhanden ist. Wo beide
vorhanden sind, müssen sie identisch sein.
Beispiel:
CREATE PROCEDURE dowhile() BEGIN DECLARE v1 INT DEFAULT 5; WHILE v1 > 0 DO ... SET v1 = v1 - 1; END WHILE; END
Funktionieren die gespeicherten Prozeduren von MySQL 5.1 mit Replikation?
Ja. Standardaktionen, die in gespeicherten Prozeduren und Funktionen ausgeführt werden, werden von einem MySQL-Master-Server auf einen Slave-Server repliziert. Es gibt einige wenige Beschränkungen, die in Abschnitt 19.4, „Binärloggen gespeicherter Routinen und Trigger“ genauer ausgeführt werden.
Werden gespeicherte Prozeduren und Funktionen, die auf einem Master-Server angelegt wurden, auf einen Slave repliziert?
Ja, gespeicherte Prozeduren und Funktionen, die mit normalen
DDL-Anweisungen auf einem Master-Server angelegt wurden,
werden auf einen Slave repliziert, sodass die Objekte auf
beiden Servern vorhanden sind. ALTER
- und
DROP
-Anweisungen für gespeicherte
Prozeduren und Funktionen werden ebenfalls repliziert.
Wie werden Aktionen repliziert, die innerhalb von gespeicherten Prozeduren und Funktionen stattfinden?
MySQL zeichnet jedes DML-Ereignis auf, das in einer gespeicherten Prozedur auftritt, und repliziert diese Einzelaktionen auf einen Slave-Server. Die eigentlichen Aufrufe der gespeicherten Prozeduren werden nicht repliziert.
Gespeicherte Funktionen, die Daten ändern, werden als Funktionsaufrufe ins Log geschrieben, und nicht als die DML-Ereignisse, die innerhalb der Funktionen stattfinden.
Gibt es spezielle Sicherheitsanforderungen, um gespeicherte Prozeduren und Funktionen mit Replikation zu benutzen?
Ja. Da ein Slave-Server das Recht hat, jede Anweisung auszuführen, die er aus dem Binärlog seines Masters liest, gibt es besondere Sicherheitsvorkehrungen für die Verwendung von gespeicherten Prozeduren mit Replikation. Wenn Replikation oder Binärlogging im Allgemeinen (für die Point-of-Time-Recovery) aktiv ist, haben MySQL-DBAs zwei Sicherheitsoptionen:
Option 1: Ein Benutzer, der eine gespeicherte Funktion
anlegen möchte, muss das SUPER
-Recht
besitzen.
Option 2: Alternativ kann ein DBA die Systemvariable
log_bin_trust_function_creators
auf 1
setzen. Dann kann jeder, der über die
Standardberechtigung CREATE ROUTINE
verfügt, gespeicherte Funktionen erstellen.
Welche Beschränkungen gelten für die Replikation von Aktionen gespeicherter Prozeduren und Funktionen?
Nichtdeterministische (zufällige) oder zeitabhängige
Aktionen, die in gespeicherten Prozeduren eingebettet sind,
werden eventuell nicht richtig repliziert. Nach dem
Zufallsprinzip entstandene Ergebnisse sind von Natur aus
unvorhersehbar und lassen sich nicht genau reproduzieren.
Somit spiegeln Zufallsaktionen, die auf einen Slave repliziert
werden, nicht die Aktionen des Masters wider. Wenn Sie
gespeicherte Funktionen als DETERMINISTIC
deklarieren oder die Systemvariable
log_bin_trust_function_creators
auf 0
setzen, können keine Operationen mit Zufallswerten aufgerufen
werden.
Auch zeitabhängige Aktionen lassen sich nicht auf einen Slave replizieren, da das Timing solcher Aktionen in einer gespeicherten Prozedur nicht durch das für die Replikation eingesetzte Binärlog reproduzierbar ist. Dieses zeichnet nur DML-Ereignisse auf und berücksichtigt keine Zeiteinschränkungen.
Auch in nichttransaktionssicheren Tabellen, mit denen bei
größeren DML-Aktionen (wie beispielsweise
Massen-Einfügeoperationen) Fehler auftreten, können
Replikationsprobleme entstehen, wenn ein Master aufgrund der
DML-Aktivität teilweise aktualisiert wurde, während der
Slave wegen eines Fehlers diese Änderung nicht mitgemacht
hat. Dies kann man umgehen, indem man die DML-Aktionen einer
Funktion mit dem Schlüsselwort IGNORE
ausführt, damit Änderungen auf dem Master, die Fehler
auslösen, ignoriert und Änderungen, die keine Fehler
auslösen, auf den Slave repliziert werden.
Beeinträchtigen die vorgenannten Beschränkungen die Fähigkeit von MySQL, eine Point-in-Time-Recovery durchzuführen?
Dieselben Beschränkungen, die sich auf die Replikation auswirken, wirken sich auch auf die Point-in-Time-Recovery aus.
Was unternimmt MySQL, um diese Beschränkungen auszuräumen?
Ab MySQL 5.1.5 können Sie zwischen anweisungs- und zeilenbasierter Replikation wählen. Die ursprüngliche Implementierung der Replikation beruht auf dem anweisungsbasierten Binärlogging. Zeilenbasiertes Binärlogging löst die oben beschriebenen Probleme. Mehr zum Thema finden Sie unter Abschnitt 6.3, „Zeilenbasierte Replikation“.
Funktionieren Trigger mit Replikation?
Trigger und Replikation funktionieren in MySQL 5.1 genau wie in den meisten anderen Datenbank-Engines: Aktionen, die auf einem Master mithilfe von Triggern ausgeführt werden, werden nicht auf einen Slave-Server repliziert. Dagegen müssen Trigger auf Tabellen, die auf einem MySQL-Master-Server liegen, auch auf den entsprechenden Tabellen des MySQL-Slave-Servers angelegt werden, um auf den Slaves ebenso wie auf dem Master aktiviert werden zu können.
Wie werden Trigger-Aktionen auf einem Master ausgeführt, der auf einen Slave repliziert wurde?
Erstens müssen die Trigger, die auf dem Master vorhanden
sind, auf dem Slave-Server rekonstruiert werden. Wenn dies
erledigt ist, läuft die Replikation so ab wie bei jeder
anderen DML-Standardanweisung, die an einer Replikation
beteiligt ist. Betrachten Sie als Beispiel die Tabelle
EMP
mit dem Insert-Trigger
AFTER
, die auf einem MySQL-Master-Server
liegt. Dieselbe EMP
-Tabelle mit demselben
AFTER
-Insert-Trigger liegt auch auf dem
Slave-Server. Der Replikationsfluss verliefe folgendermaßen:
Eine INSERT
-Anweisung für
EMP
wird abgesetzt.
Der AFTER
-Trigger auf
EMP
wird aktiviert.
Die INSERT
-Anweisung wird in das Binärlog
geschrieben.
Der Replikations-Slave nimmt die
INSERT
-Anweisung für
EMP
auf und führt sie aus.
Der AFTER
-Trigger auf
EMP
, der auf dem Slave existiert, wird
aktiviert.
Das Binärlog enthält Informationen über SQL-Anweisungen, die Datenbankinhalte ändern. Diese Informationen werden in Form von „Ereignissen“ gespeichert, welche die Modifikationen beschreiben. Das Binärlog dient zwei wichtigen Zwecken:
Für die Replikation sendet der Master-Server die Ereignisse, die in seinem Binärlog stehen, an seine Slaves. Diese führen die Ereignisse dann aus, um die Datenänderungen nachzuvollziehen, die auf dem Master stattgefunden haben. Siehe Abschnitt 6.2, „Replikation: Implementation“.
Für bestimmte Datenwiederherstellungsoperationen muss das Binärlog genutzt werden. Nach der Wiederherstellung einer Sicherungsdatei werden aus dem Binärlog die Ereignisse, die nach Erstellung der Sicherungsdatei eintraten, erneut ausgeführt. Diese Ereignisse bringen die Datenbank von dem Zeitpunkt der Sicherung auf den neuesten Stand. Siehe auch Abschnitt 5.10.2.2, „Verwenden von Datensicherungen zur Wiederherstellung“.
Dieser Abschnitt beschreibt, wie MySQL 5.1 das Binärlogging für gespeicherte Routinen (Prozeduren und Funktionen) und Trigger behandelt. Es wird beschrieben, welche Bedingungen die Implementierung zurzeit an die Verwendung gespeicherter Routinen knüpft, und diese Bedingungen werden auch begründet.
Die hier beschriebenen Probleme gründen im Wesentlichen auf die Tatsache, dass Binärlogging auf SQL-Anweisungsebene stattfindet. In einem künftigen Release von MySQL soll auch Binärlogging auf Zeilenebene eingeführt werden, wobei die Änderungen protokolliert werden, die bei der Ausführung von SQL-Anweisungen in den einzelnen Zeilen stattfinden.
Soweit nichts anderes gesagt wird, gehen wir in den nachfolgenden
Bemerkungen davon aus, dass Sie Binärlogging durch Hochfahren des
Servers mit der Option --log-bin
eingeschaltet
haben. (Siehe auch Abschnitt 5.12.3, „Die binäre Update-Logdatei“.) Wenn das
Binärlog nicht eingeschaltet ist, ist weder eine Replikation
möglich noch steht das Binärlog für die Wiederherstellung von
Daten zur Verfügung.
Die derzeit gültigen Bedingungen für die Nutzung gespeicherter Funktionen in MySQL 5.1 werden im Folgenden zusammengefasst. Diese Bedingungen gelten nicht für gespeicherte Prozeduren, und sie gelten auch ansonsten nur dann, wenn das Binärlogging auch eingeschaltet ist.
Um eine gespeicherte Funktion anzulegen oder zu ändern,
benötigen Sie das SUPER
-Recht zusätzlich
zu dem normalerweise erforderlichen CREATE
ROUTINE
- oder ALTER
ROUTINE
-Recht.
Wenn Sie eine gespeicherte Funktion erstellen, müssen Sie sie entweder als deterministisch deklarieren oder festlegen, dass sie keine Daten modifiziert. Andernfalls kann sie für die Datenwiederherstellung oder Replikation Unsicherheiten bergen.
Zur Lockerung der obigen Bedingungen für die Erstellung einer
Funktion (obligatorisches SUPER
-Recht und
das Erfordernis, entweder eine deterministische Funktion oder
eine Funktion, die keine Daten ändert, zu deklarieren)
können Sie die globale Systemvariable
log_bin_trust_function_creators
auf 1
setzen. Diese Variable hat den Standardwert 0, lässt sich
aber folgendermaßen umstellen:
mysql> SET GLOBAL log_bin_trust_function_creators = 1;
Sie können diese Variable auch einstellen, indem Sie beim
Starten des Servers die Option
--log-bin-trust-function-creators
einstellen.
Wenn Binärlogging nicht aktiviert ist, ist
log_bin_trust_function_creators
unwirksam
und das SUPER
-Recht ist nicht erforderlich,
um eine Funktion anzulegen.
Da Trigger gespeicherten Funktionen ähneln, gelten diese
Ausführungen zu Funktionen auch für Trigger, allerdings mit der
folgenden Einschränkung: Da CREATE TRIGGER
kein optionales DETERMINISTIC
-Merkmal hat, geht
man davon aus, dass Trigger immer deterministisch sind. Doch diese
Grundannahme muss nicht immer gelten. So ist beispielsweise die
Funktion UUID()
nichtdeterministisch (und wird
auch nicht repliziert). Solche Funktionen sollten Sie in Triggern
nur mit Vorsicht benutzen.
Da Trigger Tabellen ändern können, kommen im Zusammenhang mit
CREATE TRIGGER
ähnliche Fehlermeldungen wie
bei gespeicherten Funktionen vor, wenn Sie nicht das
SUPER
-Recht haben und
log_bin_trust_function_creators
den Wert 0 hat.
Im Folgenden erfahren Sie mehr über die Logging-Implementierung und ihre Implikationen.
Der Server schreibt CREATE PROCEDURE
-,
CREATE FUNCTION
-, ALTER
PROCEDURE
-, ALTER FUNCTION
-,
DROP PROCEDURE
- und DROP
FUNCTION
-Anweisungen in das Binärlog.
Der Aufruf einer gespeicherten Funktion wird als
DO
-Anweisung protokolliert, wenn die
Funktion Daten ändert und in einer Anweisung auftritt, die
ansonsten nicht protokolliert würde. Dadurch wird verhindert,
dass Datenänderungen, die durch die Verwendung von
gespeicherten Funktionen in nichtprotokollierten Anwendungen
auftreten, nicht repliziert werden. So werden beispielsweise
SELECT
-Anweisungen nicht in das Binärlog
geschrieben, aber ein SELECT
kann eine
gespeicherte Funktion aufrufen, die Datenänderungen
hervorruft. Um damit umzugehen, wird eine DO
-Anweisung in
das Binärlog geschrieben, wenn die Funktion eine Änderung
vornimmt. Angenommen, folgende Funktionen werden auf dem
Master ausgeführt:
func_name
()
CREATE FUNCTION f1(a INT) RETURNS INT BEGIN IF (a < 3) THEN INSERT INTO t2 VALUES (a); END IF; END; CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (1),(2),(3); SELECT f1(a) FROM t1;
Wenn die SELECT
-Anweisung ausgeführt wird,
wird die Funktion f1()
dreimal aufgerufen.
Zwei der Aufrufe fügen eine Zeile ein und MySQL schreibt für
jede von ihnen eine DO
-Anweisung in das
Log. Somit hält MySQL folgende Anweisungen im Binärlog fest:
DO f1(1); DO f1(2);
Der Server protokolliert auch eine
DO
-Anweisung für einen Aufruf einer
gespeicherten Funktion, wenn die Funktion eine gespeicherte
Prozedur aufruft, die einen Fehler verursacht. In diesem Fall
schreibt der Server die DO
-Anweisung
zusammen mit dem erwarteten Fehlercode in das Log. Wenn auf
dem Slave derselbe Fehler auftritt, ist dies das erwartete
Resultat und die Replikation läuft weiter. Andernfalls bricht
die Replikation ab.
Wenn anstelle von Anweisungen, die eine Funktion ausführt, Aufrufe gespeicherter Funktionen protokolliert werden, hat dies Folgen für die Sicherheit der Replikation. Dafür sind zwei Faktoren verantwortlich:
Es ist möglich, dass eine Funktion auf dem Master und den Slave-Servern unterschiedliche Ausführungspfade einschlägt.
Anweisungen, die auf einem Slave ausgeführt werden, werden von dem SQL-Thread des Slaves verarbeitet, der volle Berechtigungen hat.
Die Folge davon ist, dass zwar jeder Benutzer für die
Erstellung einer Funktion das CREATE
ROUTINE
-Recht benötigt. Doch damit könnte er eine
Funktion schreiben, die eine gefährliche Anweisung enthält,
die nur auf dem Slave ausgeführt wird, da sie dort von dem
SQL-Thread ausgeführt wird, der über volle Berechtigungen
verfügt. Wenn beispielsweise der Master-Server die Server-ID
1 und der Slave-Server die Server-ID 2 hätte, könnte ein
Benutzer auf dem Master-Server eine unsichere Funktion namens
unsafe_func()
folgendermaßen erstellen und
aufrufen:
mysql>delimiter //
mysql>CREATE FUNCTION unsafe_func () RETURNS INT
->BEGIN
->IF @@server_id=2 THEN
->dangerous_statement
; END IF;RETURN 1;
->END;
->//
mysql>delimiter ;
mysql>INSERT INTO t VALUES(unsafe_func());
Da die CREATE FUNCTION
- und die
INSERT
-Anweisung in das Binärlog
geschrieben werden, führt der Slave sie aus. Und da der
Slave-SQL-Thread wiederum über so umfassende Berechtigungen
verfügt, wird er die gefährliche Anweisung auch befolgen.
Somit hat der Funktionsaufruf auf dem Master andere Folgen als
auf dem Slave und ist nicht replikationssicher.
Um Server, auf denen das Binärlogging eingeschaltet ist, vor
dieser Gefahr zu schützen, benötigen die Erzeuger
gespeicherter Funktionen zusätzlich zu dem üblichen
CREATE ROUTINE
-Recht, das ohnehin notwendig
ist, auch das SUPER
-Recht. Ebenso darf
ALTER FUNCTION
nur benutzen, wer
zusätzlich zu dem ALTER ROUTINE
-Recht auch
das SUPER
-Recht besitzt. Fehlt die
SUPER
-Berechtigung, wird ein Fehler
ausgelöst:
ERROR 1419 (HY000): You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
Wenn Sie von den Erzeugern von Funktionen nicht verlangen
möchten, dass sie das SUPER
-Recht besitzen
(zum Beispiel, wenn in Ihrem System alle Benutzer, die das
CREATE ROUTINE
-Recht haben, erfahrene
Anwendungsentwickler sind), dann können Sie die globale
Systemvariable
log_bin_trust_function_creators
auf 1
setzen. Das können Sie auch tun, indem Sie beim Serverstart
die Option --log-bin-trust-function-creators
einstellen. Wenn kein Binärlogging aktiviert ist, ist
log_bin_trust_function_creators
wirkungslos
und zum Anlegen von Funktionen ist kein
SUPER
-Recht erforderlich.
Wenn eine Funktion, die Änderungen vornimmt, nichtdeterministisch ist, so ist sie auch nicht wiederholbar. Dies kann zwei unangenehme Folgen haben:
Ein Slave entspricht nicht mehr dem Master.
Wiederhergestellte Daten entsprechen nicht mehr den Originaldaten.
Um diese Probleme in den Griff zu bekommen, stellt MySQL folgende Anforderung: Auf einem Master-Server ist die Erzeugung und Änderung einer Funktion nur möglich, wenn diese als deterministisch deklariert ist oder keine Daten ändert. Hierbei kommen zwei Arten von Funktionsmerkmalen ins Spiel:
Die Merkmale DETERMINISTIC
und
NOT DETERMINISTIC
zeigen an, ob eine
Funktion für dieselben Eingabewerte auch immer dieselben
Ergebnisse produziert. Da die Standardeinstellung, wenn
nichts anderes angegeben wird, NOT
DETERMINISTIC
lautet, müssen Sie explizit das
Merkmal DETERMINISTIC
angeben, um
klarzustellen, dass eine Funktion deterministisch ist.
Durch Verwendung der Funktion NOW()
(oder ihrer Synonyme) oder RAND()
wird
eine Funktion nicht unbedingt deterministisch. Für
NOW()
zeichnet das Binärlog den
Zeitstempel auf und repliziert richtig. Die Funktion
RAND()
repliziert ebenfalls korrekt,
sofern sie in einer Funktion nicht mehrmals aufgerufen
wird. (Den Ausführungs-Zeitstempel der Funktion und den
Zufallszahlen-Seedwert können Sie als implizite Eingaben
betrachten, die bei Master und Slave identisch sind.)
Die Merkmale CONTAINS SQL
, NO
SQL
, READS SQL DATA
und
MODIFIES SQL DATA
geben Informationen
darüber, ob die Funktion Daten liest oder schreibt.
NO SQL
oder READS SQL
DATA
zeigt an, dass eine Funktion keine Daten
ändert, aber Sie müssen eines dieser Merkmale explizit
angeben, da ansonsten der Standardwert CONTAINS
SQL
ist.
Damit eine CREATE FUNCTION
-Anweisung
akzeptiert wird, muss nach Voreinstellung
DETERMINISTIC
bzw. entweder NO
SQL
oder READS SQL DATA
explizit
angegeben werden. Andernfalls tritt ein Fehler auf:
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
Wenn Sie log_bin_trust_function_creators
auf 1 setzen, wird nicht mehr gefordert, dass Funktionen
entweder deterministisch sind oder keine Daten ändern.
Wie die Natur einer Funktion eingeschätzt wird, hängt von
der „Ehrlichkeit“ ihres Erzeugers ab: MySQL
prüft nicht, ob eine als DETERMINISTIC
deklarierte Funktion auch tatsächlich keine Anweisungen
enthält, die nichtdeterministische Ergebnisse produzieren.
Aufrufe an gespeicherte Prozeduren werden auf Anweisungs-
statt auf CALL
-Ebene protokolliert. Das
bedeutet, dass der Server nicht die
CALL
-Anweisung protokolliert, sondern
diejenigen Anweisungen der Prozedur, die tatsächlich
ausgeführt werden. Dadurch treten auf den Slave-Servern
dieselben Änderungen wie auf dem Master ein. So werden
Probleme vermieden, die entstehen könnten, wenn eine Prozedur
auf verschiedenen Rechnern verschiedene Ausführungspfade hat.
Im Allgemeinen werden Anweisungen, die in einer gespeicherten Prozedur ausgeführt werden, nach denselben Regeln in das Binärlog geschrieben, die auch für eigenständig ausgeführte Anweisungen gelten. Die Protokollierung von Prozeduranweisungen wird besonders vorsichtig gehandhabt, da die Ausführung von Anweisungen innerhalb einer Prozedur nicht dasselbe ist wie außerhalb einer Prozedur:
Eine zu protokollierende Anweisung könnte Verweise auf lokale Prozedurvariablen enthalten. Da diese Variablen außerhalb des Prozedurkontextes gar nicht vorhanden sind, kann eine Anweisung, die eine solche Variable benutzt, nicht wörtlich protokolliert werden. Stattdessen wird für das Log jede Referenz auf eine lokale Variable durch das folgende Konstrukt ersetzt:
NAME_CONST(var_name
,var_value
)
var_name
ist der Name der
lokalen Variablen und var_value
eine Konstante, die den Wert der Variablen zum Zeitpunkt
der Protokollierung der Anweisung anzeigt.
NAME_CONST()
hat den Wert
var_value
und den
„Namen“ var_name
.
Somit erhalten Sie folgendes Ergebnis, wenn Sie diese
Funktion direkt aufrufen:
mysql> SELECT NAME_CONST('myname', 14);
+--------+
| myname |
+--------+
| 14 |
+--------+
NAME_CONST()
ermöglicht es, eine
selbstständige Anweisung auf einem Slave so auszuführen,
dass sie denselben Effekt hat wie die Originalanweisung,
die innerhalb einer gespeicherten Prozedur auf dem
Master-Server ausgeführt wurde.
Eine zu protokollierende Anweisung könnte Referenzen auf
benutzerdefinierte Variablen enthalten. Also schreibt
MySQL eine SET
-Anweisung in das
Binärlog, um zu gewährleisten, dass die Variable auf dem
Slave mit demselben Wert wie auf dem Master existiert.
Wenn beispielsweise eine Anweisung die Variable
@my_var
benutzt, steht vor dieser
Anweisung im Binärlog folgende Anweisung, wobei
value
der Wert ist, den
@my_var
auf dem Master hat:
SET @my_var = value
;
Prozeduraufrufe können in einer committeten oder
zurückgerollten Transaktion auftreten. Früher wurden
CALL
-Anweisungen auch dann
protokolliert, wenn sie in einer zurückgerollten
Transaktion vorkamen. Seit MySQL 5.0.12 wird der
Transaktionskontext berücksichtigt, sodass die
transaktionsbedingten Aspekte der Prozedurausführung
korrekt repliziert werden. Das bedeutet, dass der Server
in der Prozedur nur diejenigen Anweisungen protokolliert,
die auch tatsächlich ausgeführt werden und Daten
ändern. Nach Bedarf protokolliert er darüber hinaus auch
BEGIN
-, COMMIT
- und
ROLLBACK
-Anweisungen. Wenn
beispielsweise eine Prozedur nur Transaktionstabellen
ändert und innerhalb einer Transaktion ausgeführt wird,
die zurückgerollt wird, werden solche Änderungen nicht
ins Log geschrieben. Tritt die Prozedur hingegen in einer
committeten Transaktion auf, werden
BEGIN
- und
COMMIT
-Anweisungen mit den Updates
protokolliert. Die Anweisungen einer in einer
zurückgerollten Transaktion ausgeführten Prozedur werden
nach denselben Regeln protokolliert, die auch gelten
würden, wenn die Anweisungen selbstständig ausgeführt
worden wären:
Änderungen an Transaktionstabellen werden nicht protokolliert.
Änderungen an anderen Tabellen werden protokolliert, weil sie durch ein Rollback nicht rückgängig gemacht werden.
Änderungen an einem Mix von Transaktionstabellen und
anderen Tabellen werden innerhalb eines
BEGIN
-ROLLBACK
Blocks protokolliert, damit die Slaves dieselben
Änderungen und Rollbacks wie der Master vornehmen.
Ein Aufruf einer gespeicherten Prozedur wird
nicht auf Anweisungsebene in das
Binärlog geschrieben, wenn die Prozedur innerhalb einer
gespeicherten Funktion aufgerufen wird. In einem solchen Fall
wird lediglich die Anweisung protokolliert, welche die
Funktion aufruft, (wenn diese innerhalb einer protokollierten
Anweisung benutzt wird), oder eine
DO
-Anweisung (wenn sie in einer Anweisung
benutzt wird, die nicht protokolliert wird). Daher sollten Sie
vorsichtig mit gespeicherten Funktionen sein, die eine
Prozedur aufrufen, selbst wenn die Prozedur ansonsten sicher
ist.
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.