私は2つの目的を果たすいくつかのレガシーコードをソートしようとしています。DBIを使用してデータベースを作成し、後でDBIを使用してそのデータベースに接続します。残念ながら、それぞれに同じコードを使用していました。つまり、sales
データベースを作成する場合、後でreconnectを使用するときに、明示的にを呼び出す必要があります$dbh->do('use sales')
。これは、開発者がそれを忘れたり、データベースが再接続を処理してどのデータベースにあったかを忘れたりするなど、あらゆる種類の問題につながります。
最初のパスの修正として実行しようとしているのは、データベースが存在しない場合にDBI::connect()
メソッドを使用してMySQLに再接続し、データベースを作成できるようにすることです。HandleError
さまざまなレガシーの理由(ええ、私たちは皆そこにいます)のために、connect()
メソッドの外で「不明なデータベース」エラーをトラップしようとするのははるかに困難です。
したがって、これを解決するための私の最初のパスは次のとおりです。
use strict;
use warnings;
use DBI;
use PadWalker 'peek_my';
my $dbh = DBI->connect(
$dsn,
$user,
$pass,
{ RaiseError => 1,
PrintError => 0,
HandleError => \&reconnect_if_unknown_database,
},
);
sub reconnect_if_unknown_database {
my ($msg, $drh, $dbh) = @_;
return unless $msg =~ /Unknown database/;
my ($dsn, $user, $pass, $attr) = @{peek_my(1)}{qw/$dsn $user $pass $attr/};
unless ($dsn && $user && $pass && $attr) {
return; # don't do this if we can't get everything
}
# they're all scalar refs.
$_ = $$_ foreach $dsn, $user, $pass, $attr;
unless ($dsn =~ s/^[^;]+;/DBI:mysql:mysql;/) {
return; # can't parse dsn, so return
}
delete $attr->{HandleError}; # infinite loops tickle
$_[2] = DBI->connect($dsn, $user, $pass, $attr);
}
これは機能し、現在はエンドユーザーには透過的ですが、1と0の山のように感じられます。接続に失敗したときに別のデータベースに再接続するためのより良い方法はありますか?