🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# SQLite Perl 错误处理 > 原文: [http://zetcode.com/db/sqliteperltutorial/err/](http://zetcode.com/db/sqliteperltutorial/err/) 在本章中,我们将展示如何处理错误。 | 方法名称 | 描述 | | --- | --- | | `$h->err()` | 从上次调用的驱动程序方法返回本机数据库引擎错误代码。 | | `$h->errstr()` | 从最后一个调用的 DBI 方法返回本机数据库引擎错误消息。 | | `$h->state()` | 返回标准`SQLSTATE`五字符格式的状态码 | 以上三种方法处理错误消息。 | DBI 动态属性 | 描述 | | --- | --- | | `$DBI::err` | 相当于`$ h->err()` | | `$DBI::errstr` | 相当于`$h->errstr()` | | `$DBI::state` | 相当于`$h->state()` | 第二张表列出了与错误处理有关的 DBI 动态属性。 这些属性的寿命很短。 应该在可能导致错误的方法之后立即使用它们。 ## 默认错误处理 默认情况下,错误由 Perl DBI 方法返回。 ```perl #!/usr/bin/perl use strict; use DBI; my $dsn = "dbi:SQLite:dbname=test.db"; my $user = ''; my $password = ''; my $dbh = DBI->connect($dsn, $user, $password) or die "Can't connect to database: $DBI::errstr"; my $sth = $dbh->prepare( q{ SELECT Id, Name, Price FROM Cars } ) or die "Can't prepare statement: $DBI::errstr"; my $rc = $sth->execute() or die "Can't execute statement: $DBI::errstr"; while (my($id, $name, $price) = $sth->fetchrow()) { print "$id $name $price\n"; } # check for problems which may have terminated the fetch early warn $DBI::errstr if $DBI::err; $sth->finish(); $dbh->disconnect(); ``` 在第一个脚本中,我们处理返回错误代码的默认行为。 ```perl my $dbh = DBI->connect($dsn, $user, $password) or die "Can't connect to database: $DBI::errstr"; ``` 我们调用`connect()`方法来创建数据库连接。 如果尝试失败,则该方法返回`undef`并设置`$DBI::err`和`$DBI::errstr`属性。 `die()`方法在失败的情况下会打印错误消息并终止脚本。 ```perl my $sth = $dbh->prepare( q{ SELECT Id, Name, Price FROM Cars } ) or die "Can't prepare statement: $DBI::errstr"; ``` 我们称为`prepare()`语句。 如果该方法失败,则`die()`方法将显示错误消息并终止脚本。 ```perl my $rc = $sth->execute() or die "Can't execute statement: $DBI::errstr"; ``` 再次。 我们调用`execute()`方法并检查错误。 如果失败,该方法将返回`undef`。 ```perl warn $DBI::errstr if $DBI::err; ``` 我们检查可能会提早终止提取方法的问题。 ## 引发异常 每次我们调用 DBI 方法时都要检查错误,这可能很麻烦。 如果我们的脚本较大,我们很容易忘记这样做。 处理可能的错误的首选方法是引发异常。 为了引发异常,我们将`RaiseError`属性设置为`true`。 ```perl #!/usr/bin/perl use strict; use DBI; my $dsn = "dbi:SQLite:dbname=test.db"; my $user = ''; my $password = ''; my %attr = ( RaiseError => 1 ); my $dbh = DBI->connect($dsn, $user, $password, \%attr) or die "Can't connect to database: $DBI::errstr"; my $sth = $dbh->prepare("SELECT * FROM Cars LIMIT 5"); $sth->execute(); while (my($id, $name, $price) = $sth->fetchrow()) { print "$id $name $price\n"; } $sth->finish(); $dbh->disconnect(); ``` 在连接属性中,我们将`RaiseError`属性设置为 1。发生错误时,引发异常,而不是返回错误代码。 ```perl my %attr = ( RaiseError => 1 ); ``` 我们将`RaiseError`属性设置为 1。 ```perl my $dbh = DBI->connect($dsn, $user, $password, \%attr) or die "Can't connect to database: $DBI::errstr"; ``` `connect()`方法是我们检查返回代码的唯一方法。 ```perl my $sth = $dbh->prepare("SELECT * FROM Cars LIMIT 5"); $sth->execute(); ``` `prepare()`和`execute()`方法不检查返回错误代码。 如果失败,将引发异常,Perl DBI 将调用`die()`方法并打印错误消息。 ## 错误子程序 使用`HandleError`连接句柄属性,我们可以设置对子例程的引用,该子例程在检测到错误时被调用。 用三个参数调用该子例程:`RaiseError`和`"PrintError"`将使用的错误消息字符串,正在使用的 DBI 句柄以及失败的方法返回的第一个值(通常为`undef`)。 如果子例程返回错误值,则将检查`RaiseError`或`PrintError`属性,并按常规操作。 ```perl #!/usr/bin/perl use strict; use DBI; my $dsn = "dbi:SQLite:dbname=test.db"; my $user = ''; my $password = ''; my %attr = ( RaiseError => 1, AutoCommit => 0, HandleError => \&handle_error ); my $dbh = DBI->connect($dsn, $user, $password, \%attr) or die "Can't connect to database: $DBI::errstr"; $dbh->do("UPDATE Cars SET Price=52000 WHERE Id=1"); $dbh->do("UPDATE Car SET Price=22000 WHERE Id=8"); $dbh->commit(); $dbh->disconnect(); sub handle_error { $dbh->rollback(); my $error = shift; print "An error occurred in the script\n"; print "Message: $error\n"; return 1; } ``` 我们自己的子例程将处理该错误。 ```perl my %attr = ( RaiseError => 1, AutoCommit => 0, HandleError => \&handle_error ); ``` 当检测到错误时,`HandleError`属性提供对所调用的`handle_error()`子例程的引用。 `AutoCommit`已关闭,这意味着我们正在处理事务。 ```perl $dbh->do("UPDATE Car SET Price=22000 WHERE Id=8"); ``` SQL 语句中有错误。 没有`Car`表。 ```perl sub handle_error { $dbh->rollback(); my $error = shift; print "An error occurred in the script\n"; print "Message: $error\n"; return 1; } ``` 这是`handle_error()`子例程。 我们回滚这些更改。 我们打印错误消息并返回 1。如果返回 0,则会出现其他错误消息。 禁止返回与`RaiseError`属性相关的 1 条错误消息。 ```perl $ ./errsub.pl An error occurred in the script Message: DBD::SQLite::db do failed: no such table: Car ``` 示例的输出。 ## `eval`的例子 根据 Perl DBI 文档,处理 DBI 错误的最可靠的方法是使用`eval()`方法。 ```perl #!/usr/bin/perl use strict; use DBI; use DBI qw(:sql_types); my $dsn = "dbi:SQLite:dbname=test.db"; my $user = ''; my $password = ''; my %attr = ( RaiseError => 1, AutoCommit => 0 ); my $dbh = DBI->connect($dsn, $user, $password, \%attr) or die "Can't connect to database: $DBI::errstr"; my @data = ( [ 1, "Audi", 52642], [ 2, "Mercedes", 57127], [ 3, "Skoda", 9000], [ 4, "Volvo", 29000], [ 5, "Bentley", 350000], [ 6, "Citroen", 21000], [ 7, "Hummer", 41400], [ 8, "Volkswagen", 21601] ); eval { $dbh->do("DROP TABLE IF EXISTS Cars"); $dbh->do("CREATE TABLE Cars(Id INTEGER PRIMARY KEY, Name TEXT, Price INT)"); }; my $sql = qq{ INSERT INTO Cars VALUES ( ?, ?, ? ) }; my $sth = $dbh->prepare( $sql ); foreach my $row (@data) { eval { $sth->bind_param( 1, @$row[0], SQL_INTEGER ); $sth->bind_param( 2, @$row[1], SQL_VARCHAR ); $sth->bind_param( 3, @$row[2], SQL_INTEGER ); $sth->execute(); $dbh->commit(); }; if( $@ ) { warn "Database error: $DBI::errstr\n"; $dbh->rollback(); } } $sth->finish(); $dbh->disconnect(); ``` 上面的代码示例是处理错误的最正确方法。 ```perl my %attr = ( RaiseError => 1, AutoCommit => 0 ); ``` 我们提出异常,而不是检查返回码。 我们关闭自动提交模式,然后手动提交或回滚更改。 ```perl my $sql = qq{ INSERT INTO Cars VALUES ( ?, ?, ? ) }; my $sth = $dbh->prepare( $sql ); ``` 我们使用占位符来防止错误和安全问题。 ```perl eval { $sth->bind_param( 1, @$row[0], SQL_INTEGER ); $sth->bind_param( 2, @$row[1], SQL_VARCHAR ); $sth->bind_param( 3, @$row[2], SQL_INTEGER ); $sth->execute(); $dbh->commit(); }; ``` 在`eval()`方法内部,我们放置了容易出错的代码。 该方法捕获异常,并用错误消息填充`$@`特殊变量。 我们使用`bind_param()`方法将变量绑定到占位符。 ```perl if( $@ ) { warn "Database error: $DBI::errstr\n"; $dbh->rollback(); } ``` 如果发生错误,我们将打印错误消息并回滚更改。 在 SQLite Perl 教程的这一部分中,我们正在讨论错误处理。