我正在尝试对一些有两个用途的遗留代码进行排序.它使用DBI 创建数据库,然后使用DBI 连接到该数据库.不幸的是,它为每个使用相同的代码.这意味着如果您创建sales数据库,稍后在使用重新连接时,您必须显式调用$dbh->do('use sales').这会导致各种各样的问题,例如开发人员忘记这样做或数据库处理重新连接并忘记它所在的数据库.
我们作为第一次修复尝试做的是如果数据库不存在则DBI::connect()使用该方法HandleError重新连接到MySQL,从而允许我们创建数据库.由于各种遗留原因(是的,我们都在那里),尝试在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);
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,它目前对最终用户是透明的,但它也像一堆热气腾腾的零点.在连接失败时是否有更好的方法重新连接到不同的数据库?
我不确定这是否可行,但最好使用PadWalker:
use strict;use warnings;
use DBI;
my %attr = (RaiseError => 1, PrintError => 0);
my $dbh = DBI->connect(
$dsn,
$user,
$pass,
{
%attr,
HandleError => sub {
reconnect_if_unknown_database(
$dsn, $user, $pass, \%attr, @_
)
},
},
);
sub reconnect_if_unknown_database {
my ($dsn, $user, $pass, $attr, $msg, $drh, $dbh) = @_;
return unless $msg =~ /Unknown database/;
return unless $dsn =~ s/^[^;]+;/DBI:mysql:mysql;/;
$_[-1] = DBI->connect($dsn, $user, $pass, $attr);
}
Run Code Online (Sandbox Code Playgroud)