2012-05-04 33 views
6

Estoy tratando de ordenar nuestro código heredado que tiene dos propósitos. Utiliza DBI a crea una base de datos y luego utiliza DBI a conecta a esa base de datos. Desafortunadamente, usó el mismo código para cada uno. Esto significa que si crea una base de datos sales, más adelante, cuando la vuelva a conectar, debe llamar explícitamente al $dbh->do('use sales'). Eso lleva a todo tipo de problemas, como que los desarrolladores se olviden de hacer eso o que la base de datos maneje reconectar y olvidar en qué base de datos se encontraba.DBI: Conectarse a una base de datos diferente si la primera base de datos no existe

Lo que estamos tratando de hacer como una solución de primer paso es tener el método DBI::connect() use HandleError para reconectarse a MySQL si la base de datos no existe, lo que nos permite crear la base de datos. Por varias razones heredadas (sí, todos hemos estado allí), es mucho más difícil tratar de atrapar el error "Base de datos desconocida" fuera del método connect().

Por lo tanto, mi primer paso en la solución de esto es la siguiente:

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

que funciona y es actualmente transparente para el usuario final, pero también se siente como un humeante montón de unos y ceros. ¿Hay una mejor manera de volver a conectar a una diferente base de datos en la falla de conexión?

+0

Un caso de prueba completa sería de gran ayuda. Sin una explicación clara de "usó el mismo código para cada uno", es difícil saber cómo ayudar. –

+0

Tim: Supongo que una mejor descripción hubiera sido "¿Puedo hacer que DBI se vuelva a conectar al fallo, cambiando solo el DSN?" No sé si hay una mejor manera de hacerlo. – Ovid

Respuesta

7

no estoy seguro de si esto iba a funcionar, pero podría ser preferible al uso de 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

Wow. Soy un idiota por no pensar en eso :) – Ovid

+1

Ya sabes lo que dicen * solo otro nivel de direccionamiento indirecto * ;-) –

+1

Probablemente deberías poner '@ _' al final de la lista, ya que no tienes ninguna control real de su tamaño – ikegami

Cuestiones relacionadas