fc2ブログ

無精で短気で傲慢なプログラマ

UNIX や web やプログラムの技術的なことを中心に。

MySQL と RaiseError とサーバサイド prepare

昨日から今日にかけて はまったこと。

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 でも使ってなさいってことか。
スポンサーサイト



PageTop

コメント


管理者にだけ表示を許可する