2013-04-11 10 views
11

Aus irgendeinem Grund feuert ein Trigger, der eine Zeile aus einer zweiten Tabelle löscht, nicht mehr den Löschtrigger für die zweite Tabelle auf einer MySQL 5.5.30-Maschine.MySQL 5.5.30 kaskadierte Trigger funktionieren nicht

Das funktioniert perfekt auf unsere lokalen MySQL Version 5.5.25

Ich habe keine Dokumentation finden, die dieses Verhalten erklären würde, vielleicht hat jemand das gleiche Problem?

Dies ist entweder ein Fehler in der MySQL-Version größer als 5.5.25 oder ein "Feature", das versehentlich aktiviert wird.

UPDATE table1 => fires BEFORE UPDATE trigger ON table1 
     table1 BEFORE UPDATE TRIGGER executes: DELETE FROM table2 => should fire BEFORE DELETE trigger on table2 (but doesn't) 
      table 2 BEFORE DELETE TRIGGER executes: DELETE FROM table3 (never happens) 

OK hier meine Schritte reproduzieren:

Datenbank

CREATE DATABASE "triggerTest" DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; 

Tabellen

CREATE TABLE "table1" (
    "id" int(11) NOT NULL AUTO_INCREMENT, 
    "active" tinyint(1) NOT NULL DEFAULT '0', 
    "sampleData" varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', 
    PRIMARY KEY ("id") 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC; 


CREATE TABLE "table2" (
    "id" int(11) NOT NULL AUTO_INCREMENT, 
    "table1_id" int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY ("id"), 
    CONSTRAINT "test2_fk_table1_id" FOREIGN KEY ("table1_id") REFERENCES "table1" ("id") ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC; 


CREATE TABLE "table3" (
    "id" int(11) NOT NULL AUTO_INCREMENT, 
    "table1_id" int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY ("id"), 
    CONSTRAINT "test3_fk_table1_id" FOREIGN KEY ("table1_id") REFERENCES "table1" ("id") ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC; 

Trigger

DELIMITER $$ 

CREATE TRIGGER "table1_rtrg_AI" AFTER INSERT ON "table1" FOR EACH ROW 
BEGIN 
    IF NEW."active" THEN 
     INSERT INTO "table2" ("table1_id") SELECT NEW."id"; 
    END IF; 
END$$ 

CREATE TRIGGER "table1_rtrg_BU" BEFORE UPDATE ON "table1" FOR EACH ROW 
BEGIN 
    IF NOT NEW."active" AND OLD."active" THEN 
     DELETE FROM "table2" WHERE "table1_id" = OLD."id"; 
    END IF; 

    IF NEW."active" AND NOT OLD."active" THEN 
     INSERT INTO "table2" ("table1_id") SELECT NEW."id"; 
    END IF; 
END$$ 

CREATE TRIGGER "table2_rtrg_AI" AFTER INSERT ON "table2" FOR EACH ROW 
BEGIN 
    INSERT INTO "table3" ("table1_id") SELECT NEW."table1_id"; 
END$$ 

CREATE TRIGGER "table2_rtrg_BD" BEFORE DELETE ON "table2" FOR EACH ROW 
BEGIN 
    DELETE FROM "table3" WHERE "table1_id" = OLD."table1_id"; 
END$$ 

DELIMITER ; 

F: Warum zitieren Sie Bezeichner doppelte Anführungszeichen verwenden? (Anstelle von Backticks)

Weil Ich mag "Nische Syntax" nicht

mysql> show variables LIKE 'sql_mode'; 
+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| Variable_name | Value                                    | 
+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| sql_mode  | PIPES_AS_CONCAT,**ANSI_QUOTES**,IGNORE_SPACE,NO_UNSIGNED_SUBTRACTION,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | 
+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 
1 row in set (0.00 sec) 

Testfall 1: Erwartetes Verhalten (Datenbankversion 5.2.20)

mysql> SELECT VERSION(); 
+-----------+ 
| VERSION() | 
+-----------+ 
| 5.5.20 | 
+-----------+ 
1 row in set (0.00 sec) 

mysql> SET GLOBAL general_log := ON; 

Testeinsatz Trigger

mysql> INSERT INTO "table1" ("active", "sampleData") SELECT 0, 'sample data row 1'; 
Query OK, 1 row affected (0.00 sec) 
Records: 1 Duplicates: 0 Warnings: 0 

general_log: 
130423 12:51:27 78010 Query  INSERT INTO "table1" ("active", "sampleData") SELECT 0, 'sample data row 1' 


mysql> INSERT INTO "table1" ("active", "sampleData") SELECT 1, 'sample data row 2'; 
Query OK, 1 row affected (0.00 sec) 
Records: 1 Duplicates: 0 Warnings: 0 

general_log: 
130423 12:51:33 78010 Query  INSERT INTO "table1" ("active", "sampleData") SELECT 1, 'sample data row 2' 
       78010 Query  INSERT INTO "table2" ("table1_id") SELECT NEW."id" 
       78010 Query  INSERT INTO "table3" ("table1_id") SELECT NEW."table1_id" 

erwartet Tabelleninhalt:

mysql> SELECT * FROM "table1"; 
+----+--------+-------------------+ 
| id | active | sampleData  | 
+----+--------+-------------------+ 
| 1 |  0 | sample data row 1 | 
| 2 |  1 | sample data row 2 | 
+----+--------+-------------------+ 
2 rows in set (0.00 sec) 

mysql> SELECT * FROM "table2"; 
+----+-----------+ 
| id | table1_id | 
+----+-----------+ 
| 1 |   2 | 
+----+-----------+ 
1 row in set (0.00 sec) 

mysql> SELECT * FROM "table3"; 
+----+-----------+ 
| id | table1_id | 
+----+-----------+ 
| 1 |   2 | 
+----+-----------+ 
1 row in set (0.00 sec) 

Testaktualisierungstrigger, aktiv gesetzt

mysql> UPDATE "table1" SET "active" = 1 WHERE "id" = 1; 
Query OK, 1 row affected (0.00 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 

query_log: 
130423 12:52:15 78010 Query  UPDATE "table1" SET "active" = 1 WHERE "id" = 1 
       78010 Query  INSERT INTO "table2" ("table1_id") SELECT NEW."id" 
       78010 Query  INSERT INTO "table3" ("table1_id") SELECT NEW."table1_id" 

erwartet Tabelleninhalt:

mysql> SELECT * FROM "table1"; 
+----+--------+-------------------+ 
| id | active | sampleData  | 
+----+--------+-------------------+ 
| 1 |  1 | sample data row 1 | 
| 2 |  1 | sample data row 2 | 
+----+--------+-------------------+ 
2 rows in set (0.00 sec) 

mysql> SELECT * FROM "table2"; 
+----+-----------+ 
| id | table1_id | 
+----+-----------+ 
| 2 |   1 | 
| 1 |   2 | 
+----+-----------+ 
2 rows in set (0.00 sec) 

mysql> SELECT * FROM "table3"; 
+----+-----------+ 
| id | table1_id | 
+----+-----------+ 
| 2 |   1 | 
| 1 |   2 | 
+----+-----------+ 
2 rows in set (0.00 sec) 

Testaktualisierungstrigger,

mysql> UPDATE "table1" SET "active" = 0 WHERE "id" = 2; 
Query OK, 1 row affected (0.01 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 

query_log: 

130423 12:52:49 78010 Query  UPDATE "table1" SET "active" = 0 WHERE "id" = 2 
       78010 Query  DELETE FROM "table2" WHERE "table1_id" = NEW."id" 
       78010 Query  DELETE FROM "table3" WHERE "table1_id" = OLD."table1_id" 

erwartet Tabelleninhalte inaktiv gesetzt:

mysql> SELECT * FROM "table1"; 
+----+--------+-------------------+ 
| id | active | sampleData  | 
+----+--------+-------------------+ 
| 1 |  1 | sample data row 1 | 
| 2 |  0 | sample data row 2 | 
+----+--------+-------------------+ 
2 rows in set (0.00 sec) 

mysql> SELECT * FROM "table2"; 
+----+-----------+ 
| id | table1_id | 
+----+-----------+ 
| 2 |   1 | 
+----+-----------+ 
1 row in set (0.00 sec) 

mysql> SELECT * FROM "table3"; 
+----+-----------+ 
| id | table1_id | 
+----+-----------+ 
| 2 |   1 | 
+----+-----------+ 
1 row in set (0.00 sec) 

Testcase2: unerwartetes Verhalten (MySQL Version 5.5.30)

Heilige löst grml - Weißt du was? Schade, dass ich nicht den zweiten Fall zuerst prüfen hat - leider war ich nicht in der Lage, den Fehler zu reproduzieren .. der Test auf 5.5.30 wie gut funktioniert, halten Sie auf dem Laufenden :)

EDIT Auslöser tat nicht Kaskade wegen eines unbekannten Definierers, der in der für die Produktion gemachten Sqldumps verblieb. Entfernen von DEFINER = in den Trigger-Dumps (alternative Lösung wäre, den Benutzer zu erstellen oder DEFINER = zu einem vorhandenen zu ändern) löste das Problem, löste einen Teil des Problems.

Der unbekannte definer keine Protokolldateiausgabe

+4

* zeigen Sie bitte das tatsächliche Stück Code *. – Sebas

+0

Alles von Interesse in den Protokollen? Können Sie auch ein kleines Beispiel für eine brandneue Datenbank erstellen, um das Problem zu veranschaulichen? (Wenn es replizierbar ist, ist es einfacher zu verstehen, imo). – halfer

+0

Hey Michel, ja, als Halber sagten Sie könnten uns ein Beispiel für das Design Ihres Tisches geben. – medina

Antwort

8

Schluss Abschluss verursacht hat: MySQL 5.5.30 wird in diesem Fall nicht fehlerhaft, auch gab es keine Fehlkonfiguration des Servers selbst.

Mehrere selbstgemachte Fehler verursacht das Problem:

Fehler I: DEFINER Benutzer nicht

gab es Anstatt nur die Datenbank auf der Produktionsmaschine zu erzeugen, war ich faul und kippte die Testdatenbank zur Produktionsmaschine. Wenn Sie DEFINER in Ihrer CREATE TRIGGER Anweisung nicht explizit festlegen, wird es auf CURRENT_USER festgelegt. Leider ist diese genaue CURRENT_USER auf meiner Testmaschine nicht auf dem Produktionsserver vorhanden.

Fehler II: faul

mysqldump Dumps die Trigger-Definition mit DEFINER und den Auslöser zu schaffen sollte eine Warnung erzeugen, aber wieder, ich war faul und tat so etwas wie dieses ..

mysqldump --triggers --routines -h test -p database | gzip -3 | ssh production "gunzip -c | mysql -h production_database_host -p production_database" 

Diese sieht cool (omg Aussenseiter) und spart Ihnen eine Menge von Dump-Datei schieben, aber es Unterdrückt die Warnungen Sie sehen können, wenn Sie die Sicherung laden, aus der Konsole

MySQL schreibt die folgendes über Trigger definers:

Wenn Sie die DEFINER Klausel angeben, diese Regeln die gesetzlichen DEFINER Benutzerwerte bestimmen:

Wenn Sie nicht über die Berechtigung SUPER verfügen, ist der einzige zulässige Benutzerwert Ihr eigenes Konto, entweder wörtlich oder mithilfe von CURRENT_USER. Sie können den Definierer nicht auf ein anderes Konto festlegen.

Wenn Sie das SUPER-Privileg haben, können Sie einen beliebigen syntaktischen legalen Account-Namen angeben. Wenn das Konto nicht existiert, wird eine Warnung generiert.

Obwohl es möglich ist, einen Trigger mit einem nicht vorhandenen DEFINER -Konto zu erstellen, ist es nicht eine gute Idee für einen solchen Auslöser das Konto tatsächlich existiert, bis aktiviert werden. Andernfalls ist das Verhalten bezüglich der Berechtigungsprüfung in Bezug auf nicht definiert.

Quelle: http://dev.mysql.com/doc/refman/5.5/en/create-trigger.html

Fehler III: Faulenzen

Ich habe einen sehr kühlen mysqldump Wrapper, die sauber, wiederverwendbare Dump-Dateien erzeugen kann. Beim Überschreiben der Trigger ohne DEFINER hatte ich eine Konsolentransaktion (locking table2) auf dem Produktionsserver geöffnet, so dass die Trigger auf table2 überhaupt nicht aktualisiert wurden, aber aufgrund meiner Datensql-Pipeline über 5 Server habe ich das Timeout nicht gesehen Error.

Fazit:

Es gab keine Fehler, nur die Auslöser nicht korrekt erstellt wurden ..

Manchmal sollten Sie faul sein stoppen, wichtige Dinge eine bisschen mehr Zeit und Aufmerksamkeitgeben kann dir viel Zeit ersparen !!

1

Trigger in MySQL (im Gegensatz zu gespeicherten Prozeduren) werden immer im Kontext der DEFINER ausgeführt. Trigger können scheinbar nicht funktionieren, da DEFINER keine Berechtigungen zum Ausführen eines Teils oder des gesamten Triggers besitzt. Insbesondere muss das DEFINER in MySQL 5.1 und höher das Privileg TRIGGER sowie die relevanten SELECT und/oder UPDATE Privilegien haben.

Wenn Trigger nicht zu funktionieren scheinen, überprüfen Sie die Berechtigungen.