6

私は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の山のように感じられます。接続に失敗したときに別のデータベースに再接続するためのより良い方法はありますか?

4

1 に答える 1

7

これが機能するかどうかはわかりませんが、次を使用するよりも望ましい場合があります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);
}
于 2012-05-04T14:36:49.497 に答える