Perl error: use of uninitialized value $DBI::err in concatenation

Perl error: use of uninitialized value $DBI::err in concatenation

我编写了一个程序,使用库 DBI 将数据从 xml 文件导入 MariaDB。该过程有效,但我不明白为什么以下代码会给我消息:

use of uninitialized value $DBI::err in concatenation (.) or string at ...

这里是代码(缩写):

my $insert_art = $dbh->prepare(
    "INSERT INTO odbb_articles (creation_dt,ref_data,comp_no)". 
     "VALUES (?,?,?)"
);
....
my $comp_no = $xpc->findvalue('./sr:ARTCOMP/sr:COMPNO',$node1);
....
$insert_art->execute($creation_dt,$ref_data,$comp_no)
or die "Fehler bei der Ausfuehrung: ".
       "$DBI::err -> $DBI::errstr (odbb_articles $DBI::state)\n"; 

如果我插入代码

if ($comp_no eq "") { $comp_no = undef; }

就在 $insert_art->执行 程序之前。当 xml 文件中没有元素 COMPNO 的条目时,会发生此错误。如果我将它定义为undef,我可以避免它。我只是想知道

  1. 为什么 $comp_no 会导致这个问题并且
  2. 除了控制 $comp_no 是否为 "" 并将其定义为 undef 之外,还有其他解决方案吗?

第二个问题的原因是如果有很多 variables/columns 可能有空条目时避免 if 语句。

感谢您的帮助。

在 SQL 数据库中,空字符串与 null 非常不同。 如果 comp_no 有一个外键指向另一个 table 中的记录,那么值 "" 是一个 accettable 只有当有一条记录以 "" 作为主键时,非常不太可能。

你可以修复这个将空值转换为 undef:

for ($creation_dt,$ref_data,$comp_no ){
  defined $_  and $_ eq '' and  $_ =  undef;
}
$insert_art->execute($creation_dt,$ref_data,$comp_no);

$insert_art->execute(map {defined($_) && length($_) ? $_ : undef} ($creation_dt,$ref_data,$comp_no));

这是一个可能的捷径:

$comp_no ||= undef;

需要注意的是,这在 $comp_no 计算结果为 false 的任何情况下都有效,这意味着 0 值实际上也会导致结果变为 undef,这对您来说可能重要也可能不重要。如果你的字段是数字,我会说它很重要。

use of uninitialized value $DBI::err in concatenation (.) or string at ...

您看到的错误消息是 Perl 告诉您 $DBI::errundef。那不是因为你 $comp_no 的价值。这只是您的程序正在执行的操作的结果。

因此,当您将空字符串传递给 comp_no 列时,数据库不喜欢这样。它抛出一个错误。 DBI 捕获该错误并将其传递。 $insert_art->execute returns 为假值, or 的右侧被调用。那是你的 die

现在,在传递给 die 的字符串中,放置三个变量:

  • $DBI::err
  • $DBI::errstr
  • $DBI::state

根据 the DBI documentation,这些等同于函数 $h->err$h->errstr$h->state,其中 $h 是最后一个 使用的句柄。让我们看看这些文档。

  • $h->err

    Returns the native database engine error code from the last driver method called. The code is typically an integer but you should not assume that.

    The DBI resets $h->err to undef before almost all DBI method calls, so the value only has a short lifespan. Also, for most drivers, the statement handles share the same error variable as the parent database handle, so calling a method on one handle may reset the error on the related handles. [...]

    这个不解释什么时候可以undef.

  • $h->errstr

    Returns the native database engine error message from the last DBI method called. This has the same lifespan issues as the "err" method described above.

    The returned string may contain multiple messages separated by newline characters.

    The errstr() method should not be used to test for errors, use err() for that, because drivers may return 'success with information' or warning messages via errstr() for methods that have not 'failed'.

    好的,这是文本。不要用它来测试特定的错误。你没有那样做。你只想在程序失败时给出调试输出。

  • $h->state

    Returns a state code in the standard SQLSTATE five character format. Note that the specific success code 00000 is translated to any empty string (false). If the driver does not support SQLSTATE (and most don't), then state() will return S1000 (General Error) for all errors.

    The driver is free to return any value via state, e.g., warning codes, even if it has not declared an error by returning a true value via the "err" method described above.

    The state() method should not be used to test for errors, use err() for that, because drivers may return a 'success with information' or warning state code via state() for methods that have not 'failed'.

    再说一遍,这个到底有多大用处不是很清楚

我的建议是去掉 $DBI::err$DBI::state。你不需要那些来弄清楚问题是什么。只输出 $DBI::errstr.

$insert_art->execute($creation_dt,$ref_data,$comp_no)
  or die "Fehler bei der Ausfuehrung: " . $dbh->errstr;

现在你的程序仍然会失败,但至少你会有一条有意义的错误消息,它会解释你的数据库不喜欢该语句的地方。这比被告知错误处理代码中存在错误要好。

之后,其他答案可能适用于解决第一种情况下发生这种情况的原因。


关于 die 的另一个注意事项:如果您在参数末尾提供 \n,它不会打印您当前的脚本、行号和输入句柄行号。但是这些可能对您有用。您可以包括它们。