11

何らかの理由で、MySQL 5.5.30 マシンで、2 番目のテーブルから行を削除するトリガーが、2 番目のテーブルの削除トリガーを起動しなくなりました。

これは、ローカルの MySQL バージョン 5.5.25 で完全に動作します。

この動作を説明するドキュメントは見つかりませんでした。誰かが同じ問題を抱えているのでしょうか?

これは、MySQL バージョン 5.5.25 以降で発生するバグか、誤って有効化された「機能」のいずれかです。

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 ここで私の再現手順:

データベース

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

テーブル

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;

トリガー

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 ;

Q: 二重引用符を使用して識別子を引用するのはなぜですか? (バッククォートの代わりに)

私は「ニッチな構文」が好きではないので

    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)

テストケース 1: 予想される動作 (データベース バージョン 5.2.20)

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

mysql> SET GLOBAL general_log := ON;

挿入トリガーのテスト

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"

予想されるテーブルの内容:

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)

更新トリガーのテスト、アクティブに設定

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"

予想されるテーブルの内容:

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)

更新トリガーのテスト、非アクティブに設定

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"

予想されるテーブルの内容:

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)

テストケース 2: 予期しない動作 (MySQL バージョン 5.5.30)

神聖なトリガー grml - 何を知っていますか? 2番目のケースを最初にテストしなかったのは残念です-残念ながら、エラーを再現できませんでした..テストは5.5.30でも機能しました。最新情報をお届けします:)

プロダクション用に作成された SQL ダンプに不明な定義者が残っていたため、EDITトリガーはカスケードしませんでした。トリガーダンプで DEFINER= を削除すると (代替の解決策は、ユーザーを作成するか、DEFINER= を既存のものに変更することです) 、問題が解決し、問題の一部が解決されました。

不明な定義者により、ログ ファイルが出力されませんでした

4

3 に答える 3

8

最終的な結論: この場合、MySQL 5.5.30 にはバグがなく、サーバー自体の設定ミスもありませんでした。

いくつかの自作の間違いが問題を引き起こしました。

間違い I: DEFINER ユーザーが存在しませんでした

本番マシンでデータベースを生成するだけでなく、怠け者で、テスト データベースを本番マシンにダンプしました。ステートメントでaDEFINERを明示的に設定しない場合は、 に設定されます。残念ながら、これは私のテスト マシンとまったく同じですが、運用サーバーには存在しません。CREATE TRIGGERCURRENT_USERCURRENT_USER

過ち II: 怠惰であること

mysqldumpは DEFINER でトリガー定義をダンプし、トリガーを作成すると警告が生成されるはずですが、やはり、私は怠け者で、このようなことをしました..

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

これはクールに見え (おおおおおおおおおおおおおおお)、ダンプファイルのプッシュを大幅に節約できますが、コンソール内からダンプをロードするときに表示される可能性のある警告を抑制します

MySQL は、トリガーの定義者について次のように書いています。

DEFINER 句を指定すると、次の規則によって有効な DEFINER ユーザー値が決定されます。

SUPER 権限を持っていない場合、正当なユーザー値は、文字どおりに指定するか、または CURRENT_USER を使用して指定した自分のアカウントだけです。定義者を他のアカウントに設定することはできません。

SUPER 権限を持っている場合は、構文的に正しいアカウント名を指定できます。アカウントが実際に存在しない場合は、警告が生成されます。

存在しない DEFINER アカウントでトリガーを作成することは可能ですが、アカウントが実際に存在するまでそのようなトリガーをアクティブにすることはお勧めできません。それ以外の場合、権限チェックに関する動作は未定義です。

ソース: http://dev.mysql.com/doc/refman/5.5/en/create-trigger.html

過ち III: 怠惰であること

私は非常にクールな mysqldump ラッパーを持っています。これはクリーンで再利用可能なダンプ ファイルを生成できます。DEFINER を使用せずにトリガーを上書きしている間、実稼働サーバーでコンソール トランザクション (テーブル 2 をロック) を開いていたため、テーブル 2 のトリガーはまったく更新されませんでしたが、5 台のサーバーを超えるデータ SQL パイプラインのためにタイムアウトが表示されませんでしたエラー。

結論:

トリガーが正しく作成されなかっただけで、バグはありませんでした..

時々、怠惰になるのをやめるべきです。重要なことにもう少し時間を割いて注意を払うことで、多くの時間を節約できます!!

于 2013-04-24T15:24:52.970 に答える
1

MySQL のトリガーは (ストアド プロシージャとは異なり)、常にDEFINER. DEFINERトリガーの一部またはすべてを実行する権限がないため、トリガーが機能していないように見えることがあります。特に、MySQL 5.1 以降では、関連するおよび/または権限だけでなくDEFINER、権限も必要です。 TRIGGERSELECTUPDATE

トリガーが機能していないように見える場合は、権限を確認してください。

于 2013-04-27T22:15:33.270 に答える