2012-09-29 35 views
2

這個問題指的是從池上此評論:DBI:RAISEERROR在EVAL

[...] 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 ([email protected]) { print [email protected] }; 

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 ([email protected]) { print "ERROR: [email protected]" }; 

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

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

輸出:

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. 
+0

這是預期的行爲 - DBI返回undef(因爲異常被關閉); Perl不知道如何處理這個undef值並死掉。什麼是問題? – Dallaylaen

回答

3

如果失敗的一些原因,大多數$胸徑方法將:

  • (如果RaiseError選項設置爲0)返回undef
  • (如果RaiseError選項設置爲1),以退出消息給出的錯誤原因立即退出腳本('die')。

這裏的關鍵是它是由您決定如何處理錯誤。如果你願意,你可以不理會他們,例如(以下顯然將只與RaiseError組工作0):

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

在這個片段中(從複製@你已經在你的問題中提到池上的答案)你循環訪問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時執行)

...或者只是包裝成Perl化「的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([email protected]); 
} 

什麼是選擇,它給你:這是「在return語句錯誤」之間共同決定的(除爲了獲取實際的錯誤,你必須要求$ dbh對象),以及異常。

但底線是你不能只是寫這:

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

...如果你沒有設置RaiseError0。在這種情況下,腳本不會死亡,$sth將被分配一個undef,並且你得到一個'派生'錯誤(因爲你不能調用方法undef)。

而這正是你原來的問題的最後一部分代碼發生了什麼。