Perl DBI 语句句柄和错误处理

Posted

技术标签:

【中文标题】Perl DBI 语句句柄和错误处理【英文标题】:Perl DBI Statement Handles and Error Handling 【发布时间】:2015-10-31 16:05:13 【问题描述】:

假设我有一个连接到数据库的子程序。然后我想进行查询并接收输出并对其进行处理,当然,但是如果查询无效怎么办?

所以假设我有类似的东西:

$dbh = DBI->connect(<db>, <user>, <pass>);
$query = 'select a, b, c from table';
$sth = $dbh->prepare($query);
$sth->execute();

我意识到我也可以使用“do”,但这对我来说更容易理解。因此,在“table”没有“c”列的情况下,查询将无效,从而使句柄 $sth 无效并且无法执行。对吧?

现在,如果发生这样的失败,$sth 会发生什么?我不能调用finish,因为它是无效的。

【问题讨论】:

感谢您的所有回答。它们都非常有用。 【参考方案1】:

因此,在“table”没有“c”列的情况下,查询将无效,导致句柄 $sth 无效并且无法执行。对吧?

不清楚您所说的“无效”是什么意思。

某些驱动程序,如 DBD::Oracle,可能会在您调用 prepare 时将 SQL 语句发送到服务器,在这种情况下,服务器将检测到错误,prepare 将失败,返回 undef。 (另请参阅@Borodin 关于 RaiseError 的说明。)

如果驱动程序在调用prepare 时将语句发送到服务器,则驱动程序创建并返回一个语句句柄,其中存储了语句字符串。

现在,如果发生这样的失败,$sth 会发生什么?我不能调用finish,因为它是无效的。

同样,不清楚您所说的“无效”是什么意思。 (我发现尝试真正了解我正在考虑的概念和情况很有帮助。强迫自己这样做通常意味着我找到了自己问题的答案,因为“问题”是用我用的话说。)

因此,如上所述,prepare 失败并返回 undef(或由于 RaiseError 引发异常 - 推荐),或者 prepare 返回有效的 $sth。

如果prepare 返回 undef,那么当您尝试调用 execute 时 perl 将抛出异常。

如果prepare返回了一个有效的$sth,那么execute会被调用,语句会被发送到服务器,服务器会检测到错误,execute会返回false(或者如果RaiseError抛出异常已设置 - 推荐)。

我绝不会称 $sth“无效”。要么你因为准备失败而根本没有 $sth,要么你有一个完全有效但在执行时遇到错误的 $sth。

在这种情况下,调用$sth-&gt;finish 会起作用,但正如@ThisSuitIsBlackNot 正确指出的那样,这将毫无意义。 (我应该在很多年前将finish 重命名为discard_pending_rows。)

【讨论】:

【参考方案2】:

这是迄今为止最好的做事方式。坚持使用prepare(您只需要做一次)和execute,避免使用do。您还应该为查询的任何参数使用问号作为占位符,并将调用中的值传递给execute。除此之外,这种方式可以为您处理任何必要的值引用

很少需要致电finishdisconnect。在您更好地了解DBI 之前,您应该将它们排除在外

你是对的,$sth 将是undef,错误代码和错误消息将由$dbh-&gt;err$dbh-&gt;errstr 返回。此外,默认情况下会设置PrintError 选项,如果执行中有任何错误,这会导致DBI 生成警告。如果您希望程序完全停止,请禁用 PrintError 并启用 RaiseError

除此之外,这实际上取决于您在出现错误时选择做什么。例如,某些错误可能是可恢复的,在这种情况下,您可以禁用 both PrintError RaiseError 并测试返回值是否为undef。然后你可能会根据$dbh-&gt;err返回的错误代码执行一些操作

希望对你有帮助

【讨论】:

Borodin 关于调用finishdisconnect 的评论是正确的,但是当我使用普遍的数据库时,其中一个罕见的案例确实让我很痛苦,所以我现在总是明确地调用断开连接,而不是期望它作为 Perl 清理过程的一部分完成。 @RonBergin:如果您需要致电disconnect,那么您可能也应该致电rollback。你用的是DBD::ODBC吗? @Flimzy 嗯,这与 DBI 文档所说的完全相反:“在获取所有行的循环后添加对完成的调用是一个常见错误,不要这样做,它可以掩盖真正的问题就像未捕获的获取错误。当所有数据都从 SELECT 语句中获取时,驱动程序会自动为您调用完成。因此,除非您知道尚未从语句句柄中获取所有数据,否则不应显式调用它而且手柄不会很快被破坏。”出于好奇,你能举个例子吗?也许它取决于驱动程序...... @ThisSuitIsBlackNot:也许吧。我过去遇到过问题,并进行了大型代码审核以在必要时添加它(请注意,我们确实没有全面添加它,因为我们确实了解发生了什么,何时需要和不需要)。 @ThisSuitIsBlackNot:如果没有记错的话,我们正在处理跨多个分叉进程共享的连接池......回想起来,这可能不会咬这个用户。所以我想我会撤回我的反对意见和评论。 :)

以上是关于Perl DBI 语句句柄和错误处理的主要内容,如果未能解决你的问题,请参考以下文章

当 Perl 的 DBI 在准备语句时遇到错误时,如何避免程序退出?

为啥回滚方法不能用于 DBI 句柄?

Perl DBI 何时关闭准备好的语句

perl 中的 DBD::Oracle 和 DBI 错误

Perl 执行 DBI 循环执行

如何使用 Perl 的 DBI 处理 unicode?