Я пытаюсь отсортировать наш устаревший код, который служит двум целям. Он использует 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);
}
Это работает и в настоящее время прозрачно для конечного пользователя, но это также похоже на дымящуюся кучу нулей и единиц. Есть ли лучший способ повторного подключения к другойбазе данных при сбое подключения?