DBI:eval中的raiseerror

时间:2012-09-29 08:22:32

标签: perl exception-handling eval dbi raiseerror

这个问题是指池上的这个评论:

[...] But if you're going to put an eval around every statement, just use RaiseError => 0. [...]

在此thread

如果我在这种情况下将RaiseError设置为0,我会获得什么?

#!/usr/bin/env perl
use warnings;
use 5.10.1;
use DBI;

my $db = 'my_test_sqlite_db.sqlite';
open my $fh, '>', $db or die $!;
close $fh or die $!;

my ( $dbh, $sth );
eval {
    $dbh = DBI->connect( "DBI:SQLite:dbname=$db", "", "", {} );
};
if ( $@ ) { print $@ };

my $table = 'my_sqlite_table';

say   "RaiseError = 1";
say   "PrintError = 0";
$dbh->{RaiseError} = 1;
$dbh->{PrintError} = 0;
eval {
    $sth = $dbh->prepare( "SELECT * FROM $table" );
    $sth->execute();
};
if ( $@ ) { print "ERROR: $@" };

say "\nRaiseError = 0";
say   "PrintError = 1";
$dbh->{RaiseError} = 0;
$dbh->{PrintError} = 1;
eval {
    $sth = $dbh->prepare( "SELECT * FROM $table" );
    $sth->execute();
};
if ( $@ ) { print "ERROR: $@" };

say "\nRaiseError = 0";
say   "PrintError = 0";
$dbh->{RaiseError} = 0;
$dbh->{PrintError} = 0;
eval {
    $sth = $dbh->prepare( "SELECT * FROM $table" );
    $sth->execute();
};
if ( $@ ) { print "ERROR: $@" };

输出:

RaiseError = 1
PrintError = 0
ERROR: DBD::SQLite::db prepare failed: no such table: my_sqlite_table at ./perl2.pl line 23.

RaiseError = 0
PrintError = 1
DBD::SQLite::db prepare failed: no such table: my_sqlite_table at ./perl2.pl line 33.
ERROR: Can't call method "execute" on an undefined value at ./perl2.pl line 34.

RaiseError = 0
PrintError = 0
ERROR: Can't call method "execute" on an undefined value at ./perl2.pl line 44.

1 个答案:

答案 0 :(得分:3)

如果由于某些原因失败,大多数$ dbh方法将:

  • (如果RaiseError选项设置为0)返回undef
  • (如果RaiseError选项设置为1)立即退出脚本('die'),错误原因为退出消息。

这里的关键点在于由您决定如何处理错误。如果您愿意,可以忽略它们,例如(以下显然只能将RaiseError设置为0):

for my $db ( ... ) {
    my $dbh = get_database_handle( $db )
       or next;
    ...
}

在这个片段中(从您在问题中提到的@ ikegami的答案中复制而来),您可以循环访问DB连接的一些设置列表;如果某个连接为您提供了undef,那么您只需要另一个连接,并且不会对错误做任何事情。

但是,通常情况下,当发生错误时,你必须做的不仅仅是'nexting' - 但是再次,你有两个选择:用某些东西检查每个 $dbh相关的陈述像这样:

$sth = $dbh->prepare('some_params') 
  or process_db_error('In prepare');
...
$res = $sth->execute('another_set_of_params') 
  or process_db_error('In execute');
...
$res->doAnythingElse('something completely different') 
  or process_db_error('In something completely different');

(因为or部分仅在相应的“左侧部分”在布尔上下文中评估为false时才会执行

...或者将所有这些包装成Perlish'try-catch'块:

if (!eval {    
   $sth = $dbh->prepare('some_params');
   ...
   $res = $sth->execute('another_set_of_params');
   ...
   $res->doSomethingElse('something completely different') 
   ...
   1  # No exception
}) {
   process_db_error($@);
}

选择什么,取决于你:这是'返回语句中的错误'之间的常见决定(除了获取实际错误,你必须要求$ dbh对象)以及异常。

但最重要的是你不能写这个:

$sth = $dbh->do_something('that_can_result_in_error');
$sth->do_something('else');

...如果您确实将RaiseError设置为0。在这种情况下,脚本不会死亡,$sth将被分配undef,并且您会遇到“衍生”错误(因为您无法在undef上调用方法)。

正是在原始问题的代码的最后部分发生的事情。