DBI:评估中的引发错误
Posted
技术标签:
【中文标题】DBI:评估中的引发错误【英文标题】:DBI: raiseerror in eval 【发布时间】:2012-09-20 23:54:53 【问题描述】:这个问题参考了池上的这条评论:
[...] 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.
【问题讨论】:
这是预期的行为 - DBI 返回 undef(因为异常被关闭); Perl 不知道如何处理这个 undef 值并且死掉了。问题是什么? 【参考方案1】:如果由于某些原因失败,大多数 $dbh 方法会:
(如果RaiseError
选项设置为0)返回undef
(如果 RaiseError
选项设置为 1)立即退出脚本('die'),错误原因作为退出消息给出。
这里的关键是如何处理错误取决于您。如果您愿意,您可以忽略它们,例如(以下显然仅适用于 RaiseError
设置为 0
):
for my $db ( ... )
my $dbh = get_database_handle( $db )
or next;
...
在这个 sn-p 中(复制自您在问题中提到的@ikegami 的回答),您循环浏览一些数据库连接设置列表;如果某个连接给了你一个undef
,你就去找另一个,并且什么都不做。
不过,通常情况下,当错误发生时,您必须做的不仅仅是“下一步”——但话又说回来,您有两个选择:要么检查 each $dbh
-related 声明,像这样:
$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
上调用方法)。
这正是您原始问题的最后一部分代码中发生的事情。
【讨论】:
以上是关于DBI:评估中的引发错误的主要内容,如果未能解决你的问题,请参考以下文章
SQL 编译错误:无法评估不受支持的子查询类型 - SELECT 子句中的函数调用