
昨日から今日にかけて はまったこと。
DBI + DBD::mysql + MySQL で、RaiseError が効かない。
上記のように、存在しないテーブルやカラムに対して DML を発行しようとすると、
RaiseError を ON にしておけば SQL 解析の prepare で勝手に die してくれる。
エラーはこんな感じ。
これは Perl における use strict のようなもので、RaiseError を ON
にするのは当然の話。
しかし、ある環境で実行すると、
存在しないので prepare は undef を返して終了し、undef なものに対して execute
メソッドを実行したから落ちただけのこと。prepare の時点で RaiseError により
落ちたわけではない。
RaiseError も効かないし、PrintError も効かない。それどころか
でも落ちない。
どうしたものかと思って調べていたら、
DBD::mysqlより引用:
ということらしい。DBD::mysql-3.0002_1 から「サーバサイド prepare」というものが
デフォルトで有効になったとか。
というわけで、
として解決。
で、「サーバサイド prepare」とは何かと言うと、
MySQL Lists: mysql-ja: Prepared Statement (訳)
らしい。
というのはわかるが、mysql_emulated_prepare=1、つまり prepare をエミュレートするとは
どういうことか? サーバサイドの prepare はなぜ RaiseError が効かないのか?
ってのが全くわからない。
Oracle だと prepare ってのは要はカーソルオープンしっぱなしにするわけで、
つまりはサーバサイドなわけだが、MySQL の言うサーバサイド prepare ってのは
それと違う?
よくわからん人はおとなしく DBD::mysql-2.9x でも使ってなさいってことか。
DBI + DBD::mysql + MySQL で、RaiseError が効かない。
use DBI;
my $dbh = DBI->connect(
'DBI:mysql:dbname', 'user', 'password',
{AutoCommit => 0, RaiseError => 1, PrintError => 0}
) || die;
my $sth = $dbh->prepare("select * from not_exist_table");
$sth->execute;
上記のように、存在しないテーブルやカラムに対して DML を発行しようとすると、
RaiseError を ON にしておけば SQL 解析の prepare で勝手に die してくれる。
エラーはこんな感じ。
DBD::mysql::st execute failed: Table 'dbname.not_exist_table' doesn't exist at a line 5.
これは Perl における use strict のようなもので、RaiseError を ON
にするのは当然の話。
しかし、ある環境で実行すると、
Can't call method "execute" on an undefined value at b line 6.となってしまった。一応プログラムは異常終了しているのだが、これはテーブルが
存在しないので prepare は undef を返して終了し、undef なものに対して execute
メソッドを実行したから落ちただけのこと。prepare の時点で RaiseError により
落ちたわけではない。
RaiseError も効かないし、PrintError も効かない。それどころか
my $sth = $dbh->prepare("select * from not_exist_table") || die "$DBI::errstr";
でも落ちない。
どうしたものかと思って調べていたら、
DBD::mysqlより引用:
Prepared statement support (server side prepare)
As of 3.0002_1, server side prepare statements are on by default
(if your server is >= 4.1.3)
To use driver emulated prepared statements, all you need to do
is set the variable mysql_emulated_prepare in the connect:
$dbh = DBI->connect( "DBI:mysql:database=test;host=localhost;mysql_emulated_prepare=1",
"", "", { RaiseError => 1, AutoCommit => 1 } );
* Note: delimiter for this param is ';'
To make sure that the 'make test' step tests whether server prepare works,
you just need to export the env variable MYSQL_SERVER_PREPARE:
export MYSQL_EMULATED_PREPARE=1
ということらしい。DBD::mysql-3.0002_1 から「サーバサイド prepare」というものが
デフォルトで有効になったとか。
というわけで、
my $dbh = DBI->connect(
'DBI:mysql:dbname;mysql_emulated_prepare=1', 'user', 'password',
{AutoCommit => 0, RaiseError => 1, PrintError => 0}
) || die;
として解決。
で、「サーバサイド prepare」とは何かと言うと、
MySQL Lists: mysql-ja: Prepared Statement (訳)
らしい。
- これまでの DBD::mysql の prepare はクライアントサイドの prepare を使っており、
速度向上にはあまり寄与していなかった。 - DBD::mysql-3.0002_1 からサーバサイドの prepare を使うようにした。
というのはわかるが、mysql_emulated_prepare=1、つまり prepare をエミュレートするとは
どういうことか? サーバサイドの prepare はなぜ RaiseError が効かないのか?
ってのが全くわからない。
Oracle だと prepare ってのは要はカーソルオープンしっぱなしにするわけで、
つまりはサーバサイドなわけだが、MySQL の言うサーバサイド prepare ってのは
それと違う?
よくわからん人はおとなしく DBD::mysql-2.9x でも使ってなさいってことか。
スポンサーサイト


