# MySQL 中的 Perl 错误处理

> 原文： [http://zetcode.com/db/mysqlperl/err/](http://zetcode.com/db/mysqlperl/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:mysql:dbname=mydb";
my $user = "user12";
my $password = "34klq*";

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:mysql:dbname=mydb";
my $user = "user12";
my $password = "34klq*";
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`连接的`handle`属性，我们可以设置对子例程的引用，该子例程在检测到错误时被调用。 用三个参数调用该子例程：`RaiseError`和`PrintError`将使用的错误消息字符串，正在使用的 DBI 句柄以及失败的方法返回的第一个值（通常为`undef`）。

如果子例程返回错误值，则将检查`RaiseError`或`PrintError`属性，并按常规操作。

```perl
#!/usr/bin/perl

use strict;
use DBI;

my $dsn = "dbi:mysql:dbname=mydb";
my $user = "user12";
my $password = "34klq*";
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 语句中有错误。 没有汽车表。

```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::mysql::db do failed: Table 'mydb.Car' doesn't exist

```

示例的输出。

## `eval`的例子

根据 Perl DBI 文档，处理 DBI 错误的最可靠的方法是使用`eval()`方法。

```perl
#!/usr/bin/perl

use strict;
use DBI;
use DBI qw(:sql_types);

my $dsn = "dbi:mysql:dbname=mydb";
my $user = "user12";
my $password = "34klq*";
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(); 
}

```

如果发生错误，我们将打印错误消息并回滚更改。

在 MySQL Perl 教程的这一部分中，我们讨论了错误处理。