Kapitel 19. Gespeicherte Prozeduren und Funktionen
Zurück |  | Weiter

## Kapitel 19. Gespeicherte Prozeduren und Funktionen

MySQL 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.

## 19.1. Gespeicherte Routinen und die Berechtigungstabellen

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.

## 19.2. Syntax gespeicherter Prozeduren

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 `db_name`` ausgeführt (und wieder rückgängig gemacht, wenn die Routine endet). `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.

### 19.2.1. `CREATE PROCEDURE` und `CREATE FUNCTION`

    CREATE PROCEDURE sp_name ([proc_parameter[,...]])
        [characteristic ...] routine_body

    CREATE FUNCTION sp_name ([func_parameter[,...]])
        RETURNS type
        [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 `value``-Anweisung enthalten.

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 `db_name`` ausgeführt (und wieder rückgängig gemacht, wenn die Routine endet). `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.

### 19.2.2. `ALTER PROCEDURE` und `ALTER FUNCTION`

    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.

### 19.2.3. `DROP PROCEDURE` und `DROP FUNCTION`

    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.

### 19.2.4. Syntax der `CALL`-Anweisung

    CALL sp_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.

### 19.2.5. `BEGIN ... END`-Syntax für komplexe Anweisungen

    [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.

### 19.2.6. Syntax der `DECLARE`-Anweisung

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.

### 19.2.7. Variablen in gespeicherten Routinen

Auch Variablen können in einer Routine deklariert und benutzt werden.

#### 19.2.7.1. Lokale `DECLARE`-Variablen

    DECLARE var_name[,...] type [DEFAULT value]

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.

#### 19.2.7.2. Variable `SET`-Anweisung

    SET var_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.

#### 19.2.7.3. `SELECT ... INTO`-Anweisung

    SELECT col_name[,...] INTO var_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“.

### 19.2.8. Bedingungen und Handler

Manche Bedingungen erfordern eine Sonderbehandlung, darunter Bedingungen, die Fehler oder den allgemeinen Kontrollfluss innerhalb einer Routine steuern.

#### 19.2.8.1. `DECLARE`-Bedingungen

    DECLARE condition_name CONDITION FOR condition_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.

#### 19.2.8.2. `DECLARE`-Handler

    DECLARE handler_type HANDLER FOR condition_value[,...] statement

    handler_type:
        CONTINUE
      | EXIT
      | UNDO

    condition_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 `var_name` = `value``) oder auch eine zusammengesetzte Anweisung in einem `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;

### 19.2.9. Cursor

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

#### 19.2.9.1. Deklaration von Cursorn

    DECLARE cursor_name CURSOR FOR select_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.

#### 19.2.9.2. Cursor-`OPEN`-Anweisung

    OPEN cursor_name

Diese Anweisung öffnet einen zuvor deklarierten Cursor.

#### 19.2.9.3. Das Cursor-Statement `FETCH`

    FETCH cursor_name INTO var_name [, var_name] ...

Diese Anweisung holt die nächste Zeile ab (sofern eine existiert). Hierzu verwendet sie den angegebenen, geöffneten Cursor und schiebt den Zeiger im Cursor um eins weiter.

#### 19.2.9.4. Cursor-Statement `CLOSE`

    CLOSE cursor_name

Diese Anweisung schließt einen zuvor geöffneten Cursor.

Wenn ein Cursor nicht explizit geschlossen wurde, wird er am Ende der zusammengesetzten Anweisung geschlossen, in der er deklariert ist.

### 19.2.10. Konstrukte für die Ablaufsteuerung

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.

#### 19.2.10.1. `IF`-Anweisung

    IF search_condition THEN statement_list
        [ELSEIF search_condition THEN statement_list] ...
        [ELSE statement_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“.

#### 19.2.10.2. `CASE`-Anweisung

    CASE case_value
        WHEN when_value THEN statement_list
        [WHEN when_value THEN statement_list] ...
        [ELSE statement_list]
    END CASE

Oder:

    CASE
        WHEN search_condition THEN statement_list
        [WHEN search_condition THEN statement_list] ...
        [ELSE statement_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.

#### 19.2.10.3. `LOOP`-Anweisung

    [begin_label:] LOOP
        statement_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.

#### 19.2.10.4. `LEAVE`-Anweisung

    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.

#### 19.2.10.5. `ITERATE`-Anweisung

    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

#### 19.2.10.6. `REPEAT`-Anweisung

    [begin_label:] REPEAT
        statement_list
    UNTIL search_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)

#### 19.2.10.7. `WHILE`-Anweisung

    [begin_label:] WHILE search_condition DO
        statement_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

## 19.3. Gespeicherte Prozeduren, Funktionen, Trigger und Replikation: häufig gestellte Fragen

- 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:

1. Eine `INSERT`-Anweisung für `EMP` wird abgesetzt.

2. Der `AFTER`-Trigger auf `EMP` wird aktiviert.

3. Die `INSERT`-Anweisung wird in das Binärlog geschrieben.

4. Der Replikations-Slave nimmt die `INSERT`-Anweisung für `EMP` auf und führt sie aus.

5. Der `AFTER`-Trigger auf `EMP`, der auf dem Slave existiert, wird aktiviert.

## 19.4. Binärloggen gespeicherter Routinen und Trigger

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 `func_name`()`-Anweisung in das Binärlog geschrieben, wenn die Funktion eine Änderung vornimmt. Angenommen, folgende Funktionen werden auf dem Master ausgeführt:

      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.

Zurück |  | Weiter
Kapitel 18. Raumbezogene Erweiterungen in MySQL | Zum Anfang | Kapitel 20. Trigger