问题
Does one of these two ways have to be preferred or is it only a matter of taste?
#!/usr/bin/env perl
use warnings;
use strict;
use DBI;
my $db = 'sqlite_db';
#################### A ####################
sub get_database_handle {
my ( $db ) = @_;
my $dbh;
eval {
$dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} )
or die DBI->errstr;
};
if ( $@ ) {
print $@;
return;
}
return $dbh;
}
DATABASES: while ( 1 ) {
# choose a database from a list of databases
# ...
my $dbh = get_database_handle( $db );
next DATABASES if not defined $dbh;
# ...
# do something with the database
}
#################### B ####################
sub get_database_handle {
my ( $db ) = @_;
my $dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} )
or die DBI->errstr;
return $dbh;
}
DATABASES: while ( 1 ) {
# choose a database from a list of databases
# ...
my $dbh;
eval { $dbh = get_database_handle( $db ); };
if ( $@ ) {
print $@;
next DATABASES;
}
# ...
# do something with the database
}
回答1:
Why eval at all? What are you going to do when you can't get a database handle?
At the very least, the subroutine should either return a database handle or die, so no eval in there.
If you really have something productive to do when there is a database error, then eval outside of the subroutine. Not necessary around just the subroutine, could be even wider scope, as appropriate for your error handling logic.
But if all you want is terminate the program and print an error, just let the exception bubble up.
回答2:
It really makes no sense to use RaiseError => 1
at all if your code is going to look like that. If you want to keep that code layout, use RaiseError => 0
instead. (You can always turn it on later using $dbh->{RaiseError} = 1;`.)
sub get_database_handle {
my ( $db ) = @_;
return DBI->connect("DBI:SQLite:$db", '', '', {
...
RaiseError => 0,
PrintError => 1,
});
}
for my $db ( ... ) {
my $dbh = get_database_handle( $db )
or next;
...
}
That said, I suggest that you continue using RaiseError => 1
, but that you change your code layout instead. Specifically, you should widen the scope of the eval.
sub get_database_handle {
my ( $db ) = @_;
return DBI->connect("DBI:SQLite:$db", '', '', {
...
RaiseError => 1,
PrintError => 0,
});
}
for my $db ( ... ) {
if (!eval {
my $dbh = get_database_handle( $db );
...
1 # No exception
}) {
warn("Error processing database $db: $@");
}
}
This will catch any errors, not just database connection errors.
回答3:
It depends on the preferred way of handling errors in the rest of your project.
If you plan to use exceptions, let your function throw them.
If you are going to handle errors manually via conditionals, don't throw (eval inside).
I myself prefer exceptions. They are loud (you know something is broken!), plus stack traces via Carp::confess
/Carp::longmess
and $SIG{__DIE__}
are a nice bonus in case of a large codebase.
Here's an (un)success story. A month or two ago we rolled out broken code that silently corrupted data due to unchecked return value from DB-handling function. It was in production for a day. Resurrecting the data was a world of pain.
来源:https://stackoverflow.com/questions/12635495/should-i-put-the-eval-in-the-subroutine-or-the-subroutine-in-the-eval