
最初の方に追記しておくと生DBI限定の話です。僕のしらない wrapper モジュールだと動くのかもしれません。
けど、こういうエントリを僕が書いてる時点で、そういうモジュールは筋悪いんだとおもいます。
人のプロジェクトのコードを見てたら
my $dbh = DBI->connect('dbi:mysql:dbname=geekdb;sql-mode=STRICT_TRANS_TABLES', 'yappo', 'moneyfriend', %attr);
みたいな感じの DSN があったんですよね。エントリ書いてからもっかい確かめようと思って実装みてたんですが、延々と XS コード出てきて説明面倒になってきたので禿げてないでコード書いてみた。
DBI 1.622
DBD::mysql 4.022
MySQL のドキュメントを見ると「STRICT_TRANS_TABLES は厳格なんで ENUM に定義してない値とかいれてもエラーだよ」って書いてあるので、分かりやすく ENUM で定義されてないデータを突っ込んでみる。
use strict;
use warnings;
use 5.012;
use DBI;
my $dbh = DBI->connect(
'dbi:mysql:dbname=geekdb;sql-mode=STRICT_TRANS_TABLES', 'yappo', 'moneyfriend',
+{
PrintError => 0,
RaiseError => 1,
AutoCommit => 1,
}
);
$dbh->do(q{DROP TABLE IF EXISTS geeks});
$dbh->do(q{CREATE TABLE geeks (
geek ENUM('matz', 'dan') NOT NULL
)});
$dbh->do(q{INSERT INTO geeks (geek) VALUES('dan')});
my($geek1) = $dbh->selectrow_array('SELECT geek FROM geeks LIMIT 1');
say $geek1;
$dbh->do(q{DELETE FROM geeks});
$dbh->do(q{INSERT INTO geeks (geek) VALUES('hyoshiok')});
my($geek2) = $dbh->selectrow_array('SELECT geek FROM geeks LIMIT 1');
say $geek2;
これを実行すると、普通に実行されてるが $geek1 で dan を表示してるのに $geek2 では何も表示しないで終わってる。
これは hyoshiok という文字列が geek の ENUM に定義されてないから、なにも入れられてないって事で STRICT_TRANS_TABLES が有効になってなさそう。。。。!
椅子とともに SET SESSION sql_mode=... しろと言われたので、 connect 時に SQL を打ってみる。
ここで何も考えないで $dbh->do つかってやっちゃうと reconnect した時とかに無効になるので、筋良く Callbacks オプションを使う。
説明は椅子プロさんの解説が Perl Advent Calendar に乗ってた!
use strict;
use warnings;
use 5.012;
use DBI;
my $dbh = DBI->connect(
'dbi:mysql:dbname=geekdb;sql-mode=STRICT_TRANS_TABLES', 'yappo', 'moneyfriend',
+{
PrintError => 0,
RaiseError => 1,
AutoCommit => 1,
Callbacks => +{
connected => sub {
my $dbh = shift;
$dbh->do(q{SET SESSION sql_mode=STRICT_TRANS_TABLES});
return;
},
},
}
);
$dbh->do(q{DROP TABLE IF EXISTS geeks});
$dbh->do(q{CREATE TABLE geeks (
geek ENUM('matz', 'dan') NOT NULL
)});
$dbh->do(q{INSERT INTO geeks (geek) VALUES('dan')});
my($geek1) = $dbh->selectrow_array('SELECT geek FROM geeks LIMIT 1');
say $geek1;
$dbh->do(q{DELETE FROM geeks});
$dbh->do(q{INSERT INTO geeks (geek) VALUES('hyoshiok')});
my($geek2) = $dbh->selectrow_array('SELECT geek FROM geeks LIMIT 1');
say $geek2;
今度のケースを実行すると。。。
dan DBD::mysql::db do failed: Data truncated for column 'geek' at row 1 at ./hoge.pl line 32.ちゃんとエラーが出てくれてて STRICT_TRANS_TABLES が有効になってるっぽい!111
DSN を良きにしてくれる O/R Mapper とかあるのかなとおもって DBIC みてもそういう実装じゃないし、そもそも DSN 変な拡張入れる筋悪なの使う人は迷惑なので、実際なくて良かった。
ということで、接続してから
SET SESSION sql_mode=...;すべし、と椅子プロさんに指摘頂きました。
コピペでプロジェクト始めるやつは空気椅子で仕事してろや!
Posted by Yappo at 2012年11月28日 13:37 | TrackBack | Perl