2012-05-04 47 views
6

Tôi đang cố sắp xếp một số mã cũ của chúng tôi phục vụ hai mục đích. Nó sử dụng DBI để tạo cơ sở dữ liệu và sau đó sử dụng DBI để kết nối với cơ sở dữ liệu đó. Thật không may, nó được sử dụng cùng một mã cho mỗi. Điều đó có nghĩa là nếu bạn tạo cơ sở dữ liệu sales, sau đó, khi sử dụng kết nối lại, bạn phải gọi một cách rõ ràng $dbh->do('use sales'). Điều đó dẫn đến tất cả các loại vấn đề, chẳng hạn như các nhà phát triển quên làm điều đó hoặc xử lý cơ sở dữ liệu kết nối lại và quên cơ sở dữ liệu mà nó đã nhập.DBI: Kết nối với một cơ sở dữ liệu khác nếu cơ sở dữ liệu đầu tiên không tồn tại

Điều chúng tôi đang cố gắng thực hiện lần sửa đầu tiên là có phương pháp DBI::connect() sử dụng HandleError để kết nối lại với MySQL nếu cơ sở dữ liệu không tồn tại, do đó cho phép chúng tôi tạo cơ sở dữ liệu. Vì nhiều lý do di sản khác nhau (vâng, tất cả chúng ta đã ở đó), khó có thể bẫy lỗi "Cơ sở dữ liệu không xác định" bên ngoài phương thức connect().

Như vậy, vượt qua đầu tiên của tôi tại giải này là như sau:

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); 
} 

đó làm việc và nó hiện trong suốt đối với người dùng cuối, nhưng nó cũng cảm thấy như một đống hấp của những người thân và số không. Có cách nào tốt hơn để kết nối lại với cơ sở dữ liệu khác nhau về lỗi kết nối không?

+0

Một trường hợp thử nghiệm hoàn chỉnh sẽ là một trợ giúp lớn. Nếu không có một giải thích rõ ràng về "sử dụng cùng một mã cho mỗi" thật khó để chắc chắn làm thế nào để giúp đỡ. –

+0

Tim: Tôi đoán mô tả tốt hơn sẽ là "tôi có thể kết nối với DBI khi không thành công, chỉ thay đổi DSN không?" Tôi không biết nếu có một cách tốt hơn để làm điều này. – Ovid

Trả lời

7

Tôi không chắc chắn nếu điều này sẽ làm việc, nhưng nó có thể là thích hợp hơn để sử dụng 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); 
} 
+0

Chà. Tôi là một thằng ngốc vì không nghĩ về điều đó :) – Ovid

+1

Bạn biết những gì họ nói * chỉ là một mức độ khác nhau * ;-) –

+1

Có lẽ nên đặt '@ _' ở cuối danh sách, vì bạn không có bất kỳ kiểm soát thực sự kích thước của nó. – ikegami

Các vấn đề liên quan