2013-04-11 21 views
11

Per qualche motivo, su una macchina MySQL 5.5.30, un trigger che cancella una riga da una seconda tabella non attiva più il trigger di cancellazione nella seconda tabella.I trigger a cascata MySQL 5.5.30 non funzionano

Questo funziona perfettamente sul nostro locale versione di MySQL 5.5.25

Non ho trovato alcuna documentazione che potrebbe spiegare questo comportamento, non qualcuno forse ha un problema di parità?

Questo è un errore che si verifica nella versione di MySQL superiore a 5.5.25 o una "funzione" abilitata accidentalmente.

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 qui il mio riprodurre passaggi:

database

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

Tavoli

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 ; 

D: Perché si cita identificatori utilizzando virgolette? (Invece di backticks)

Perché non mi piace "sintassi di nicchia"

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) 

Testcase 1: Comportamento previsto (database della versione 5.2.20)

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

mysql> SET GLOBAL general_log := ON; 

test inserto grilletto

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" 

attesi contenuto della tabella:

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) 

test segnale di avvio, dell'insieme attivo

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" 

contenuto della tabella attesi:

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) 

test segnale di avvio, impostare inattivo

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" 

attesi contenuto della tabella:

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: comportamento imprevisto (MySQL versione 5.5.30)

Santo innesca grml - Sai una cosa? Peccato che non ho la prova secondo caso prima - purtroppo non era in grado di riprodurre l'errore .. il test ha lavorato 5.5.30 pure, vi terremo aggiornati :)

EDIT trigger non ha cascata a causa di un definitore sconosciuto che era rimasto nella discarica sql creata per la produzione. Rimozione DEFINER = nei dump di trigger (soluzione alternativa sarebbe quella di creare l'utente o di cambiare DEFINER = ad uno esistente) risolto il problema, risolto una parte del problema.

Il definitore sconosciuto non ha causato alcun output file di log

+4

* mostra il codice effettivo *, per favore. – Sebas

+0

Qualcosa di interessante nei registri? Inoltre, puoi creare un piccolo esempio su un nuovo database per illustrare il problema? (Se è replicabile, è più facile da capire, imo). – halfer

+0

Hey Michel, sì, come detto halfer potresti darci un esempio del design del tuo tavolo. – medina

risposta

8

Conclusione finale: MySQL 5.5.30 non è bacato in questo caso, anche non vi era alcuna errata configurazione del server stesso.

Diversi errori fatti da sé ha causato il problema:

Mistake I: utente DEFINER non esisteva

Invece di limitarsi generando il database sulla macchina di produzione, ero pigro e scaricate il database di test alla macchina di produzione. Se non si imposta esplicitamente un valorenell'istruzione CREATE TRIGGER, è impostato su CURRENT_USER. Purtroppo questo esatto CURRENT_USER sulla mia macchina di prova non esiste sul server di produzione.

Mistake II: Essere pigri

mysqldump discariche la definizione grilletto con DEFINER e creando il grilletto dovrebbe generare un avvertimento, ma ancora una volta, sono stato pigro e ha fatto qualcosa di simile ..

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

Questo sembra freddo (omg disadattato) e vi fa risparmiare un sacco di file di dump spingere, ma surpresses gli avvertimenti si potrebbe vedere quando si carica il dump dalla console

MySQL scrive il segue circa definitori di trigger:

Se si specifica la clausola DEFINER, queste regole determinano i legali Definer valori utente:

Se non si dispone del privilegio SUPER, l'unico valore utente legale è il proprio account, specificato letteralmente o utilizzando CURRENT_USER. Non è possibile impostare il definitore su un altro account.

Se si dispone del privilegio SUPER, è possibile specificare qualsiasi nome legale dell'account legale . Se l'account non esiste realmente, viene generato un avviso .

Anche se è possibile creare un trigger con un DEFINER conto inesistente, non è una buona idea per tali trigger da attivare fino l'account esiste effettivamente. In caso contrario, il comportamento rispetto al controllo dei privilegi non è definito.

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

Mistake III: Essere pigri

Ho un involucro mysqldump molto cool, che è in grado di generare pulito, file di dump riutilizzabili. Mentre sovrascrivevo i trigger senza DEFINER avevo una transazione di console (lock table2) aperta sul server di produzione, quindi i trigger su table2 non si aggiornavano affatto, ma ancora una volta, a causa della mia pipeline di dati sql su 5 server non vedevo il timeout errore.

Conclusione:

Non c'era errore, solo i trigger non sono stati creati in modo corretto ..

A volte si dovrebbe smettere di essere pigro, dando cose importanti una po 'più di tempo e attenzionepuoi risparmiare un sacco di tempo !!

1

I trigger in MySQL (diversamente dalle stored procedure) vengono sempre eseguiti nel contesto di DEFINER. È possibile che i trigger non funzionino perché lo DEFINER non dispone delle autorizzazioni per eseguire un trigger parziale o completo. In particolare, in MySQL 5.1 e versioni successive lo DEFINER deve disporre del privilegio TRIGGER e dei privilegi SELECT e/o rilevanti.

Quando i trigger non sembrano funzionare, controllare i privilegi.