MySQLがキャッチしたバグ:複合VIEWに最初のエントリを追加できない

こんにちは、Habr!



私は誠実に仕事をするのに慣れていましたが、この投稿を書く前に、実際にバグであることに気づいたことを何度もチェックし(コンピューターの前で眠れない夜の結果ではなく) 、インターネットで同様のものを見つけようとしました。 無駄だ。 ベロレン。 無駄だ。



したがって、興味がある場合は、Catにようこそ。コンポジットVIEWに最初のレコードを追加しても正しく機能しない単純なアーキテクチャ要素を確認できます。



著者が喫煙したもの



まず、そのようなアーキテクチャソリューションが必要な理由について簡単に説明します。

詳細(機密情報の非開示に関する合意、すべてのもの)を公開せずに、現在のプロジェクトに取り組む際に、次の関係を実装するためにオブジェクトの3つのクラス( abc )が必要であると言います:

cからa -∞から1

cからb -∞から0..1。

したがって、各オブジェクトcは1つのオブジェクトaに関連付けられており、1つのオブジェクトbに関連付けられている場合も、オブジェクトbにまったく関連付けられていない場合もあります。



フリーランサー用エクササイズバイク



このデータベースフラグメントは、次のように設計されました。

+クラスaのすべてのオブジェクトをリストするテーブル(簡単にするために、識別子以外の唯一のパラメーターを名前にします);

+クラスb (同じパセリ)のすべてのオブジェクトをリストするテーブル。

+クラスcのすべてのオブジェクトをリストするテーブル(パラメータを持つ識別子を除く:名前、クラスaのオブジェクトの識別子(必須)、クラスbのオブジェクトの識別子(オプション));

+クラスabのオブジェクトの名前が関連付けられたクラスcのすべてのオブジェクトを含むビュー(セキュリティ上の理由から(テーブル自体に影響を与えずにVIEWに権限を付与できます)、データ整合性検証ロジックの一部をphpからMySQLに転送し、カスケードチェックオプションを使用して、PHPコードのJOINをドラッグしないでください。



ムッシュは子供について多くのことを知っています



ビューの可変性を確保するために、INNER JOIN(LEFT OUTER JOINはビューの可変性を禁止しています)のみを使用する必要がありましたが、一方で、クラスbのオブジェクトに関連しないクラスcのオブジェクトもビューに表示する必要がありました。



これを行うために、次のトリックを適用しました。クラスbの関連オブジェクトの識別子もゼロ( '0')にします。つまり、クラスbの関連オブジェクトはありません。 また、クラスbのオブジェクトのテーブルに、クラスbのオブジェクトが存在しないことに対応する(識別子がゼロの)nullレコードが含まれるようにします(名前 'N / A'とします)。



また、WITH CASCADED CHECK OPTIONと組み合わせたこのトリックは、クラスcのオブジェクトの表現に適用されるINSERTステートメントの異常な動作を与えます



INSERTステートメントの不良を教える方法



状況を再現するモデルクエリをデータベースに提供します。



CREATE TABLE `a`(`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,`name` VARCHAR(255) DEFAULT NULL) ENGINE='InnoDB' CHARSET='utf8' COLLATE='utf8_general_ci'; INSERT INTO `a`(`name`) VALUES('test_a'); CREATE TABLE `b`(`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,`name` VARCHAR(255) DEFAULT NULL) ENGINE='InnoDB' CHARSET='utf8' COLLATE='utf8_general_ci'; SET SESSION `SQL_MODE`='NO_AUTO_VALUE_ON_ZERO'; INSERT INTO `b`(`id`,`name`) VALUES('0','N/A'); INSERT INTO `b`(`id`,`name`) VALUES('1','test_b'); SET SESSION `SQL_MODE`=''; CREATE TABLE `c`(`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,`name` VARCHAR(255) NOT NULL,`a` INT NOT NULL,`b` INT DEFAULT '0',FOREIGN KEY(`a`) REFERENCES `a`(`id`),FOREIGN KEY(`b`) REFERENCES `b`(`id`)) ENGINE='InnoDB' CHARSET='utf8' COLLATE='utf8_general_ci'; CREATE VIEW `C` AS SELECT `t1`.`id` `id`,`t1`.`name` `name`,`t2`.`name` `a`,`t3`.`name` `b`,`t1`.`a` `a_id`,`t1`.`b` `b_id` FROM `c` `t1` JOIN `a` `t2` ON(`t1`.`a`=`t2`.`id`) JOIN `b` `t3` ON(`t1`.`b`=`t3`.`id`) WITH CASCADED CHECK OPTION; SELECT `id` FROM `a`; SELECT `id` FROM `b`;
      
      





 mysql> SELECT `id` FROM `a`; +----+ | id | +----+ | 1 | +----+ 1 row in set (0.01 sec) mysql> SELECT `id` FROM `b`; +----+ | id | +----+ | 0 | | 1 | +----+ 2 rows in set (0.00 sec)
      
      





すべてが正しいはずです。

次に、関連付けられたオブジェクトbのない最初のレコードをCビューに挿入してみましょう。

 mysql> INSERT INTO `C`(`a_id`,`name`) VALUES('1','test_c'); ERROR 1369 (HY000): CHECK OPTION failed 'test.C' mysql>
      
      





がっかり? 私はあなたのことは知りませんが、私は-はい。

わかった それを理解してみましょう。

テーブルcに対してまったく同じクエリを直接実行し、 Cビューの内容を表示します。

 mysql> INSERT INTO `c`(`a`,`name`) VALUES('1','test_c'); Query OK, 1 row affected (0.09 sec) mysql> SELECT * FROM `C`; +----+--------+--------+------+------+------+ | id | name | a | b | a_id | b_id | +----+--------+--------+------+------+------+ | 1 | test_c | test_a | N/A | 1 | 0 | +----+--------+--------+------+------+------+ 1 row in set (0.00 sec) mysql>
      
      





がっかり? 私はあなたのことは知りませんが、私はとても知りました。

「バグ」という言葉を除いて、この動作を説明することはできません。

さらに、テーブルcを元の形式に戻すと、 C表現を介してレコードが強打されて追加されます。

 mysql> DELETE FROM `c`; ALTER TABLE `c` AUTO_INCREMENT=1; INSERT INTO `C`(`a_id`,`name`) VALUES('1','test_c'); SELECT * FROM `C`; Query OK, 1 row affected (0.05 sec) Query OK, 0 rows affected (0.09 sec) Records: 0 Duplicates: 0 Warnings: 0 Query OK, 1 row affected (0.00 sec) +----+--------+--------+------+------+------+ | id | name | a | b | a_id | b_id | +----+--------+--------+------+------+------+ | 1 | test_c | test_a | N/A | 1 | 0 | +----+--------+--------+------+------+------+ 1 row in set (0.01 sec) mysql>
      
      







結論は?



ナフは言った。 ハブロフスクの人々が真夜中の回避と真昼の書癖のこの果物を承認した場合、コミュニティでバグレポートを書く方法を学ぶことを考えています(LinuxコミュニティまたはMySQLコミュニティは別の質問です:MySQL 5.6はまだ見ていません:おそらくこのバグはありません) (少し試してみます:今夜、githubで最初に承認されたプルリクエストを既に受け取りました。)



追記



上記はもともとMySQLバージョン5.1に適用されました(はい、残念ながら、これまでのところMySQL 5.1を使用したプラットフォームでの作業は避けられない条件です)が、MySQL 5.5.35を使用してタイプライターで同じことを試しました(リリースをテストしています)公式Debianリポジトリ)と同じすべての落胆的な結果を見た。



All Articles