php - PDO inTransaction() 在数据库异常后返回 false

标签 php postgresql pdo transactions

如果抛出数据库异常,

PDO 的 inTransaction() 将在仍在事务中时返回 false。这可能特定于使用 PostgreSQL。例如

try {
    $pdo->beginTransaction();
    $pdo->exec('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
    // ...
    // Cause any PDO exception
    // ...
    $pdo->commit();
} catch (\Exception $e) {
    if ($pdo->inTransaction()) {
        // Never gets here
        $pdo->rollback();
    }
    throw $e;
}

交易肯定没有结束,因为如果我开始另一个交易,我会得到一个异常,表明已经有一个交易在进行中。我没有测试每种类型的异常,但它肯定会发生 SQLSTATE[40001]: Serialization failure 和主键违规。 这是预期的行为还是 PHP 中的错误?

似乎知道回滚的唯一方法是保留一个单独的变量来知道我在事务中,使 inTransaction() 无用。我注意到一些开源框架(如 Doctrine )和应用程序(如 Drupal )为事务状态保留自己的变量。 为什么我们不能依靠驱动程序或数据库来告诉我们交易是否正在进行?

PHP 5.5.32 和 PostgreSQL 9.4。找到一个两岁的相关bug report在旧版本的 PHP 中关闭。

最佳答案

回答您的问题:

Is this expected behavior or is it a bug in PHP?

不,这不是预期的行为,一定是 PDO PgSQL 扩展中的错误。

Why can't we rely on the driver or database to tell us if a transaction is in progress?

因为驱动程序和数据库都是人为创建的。在创建如此高度复杂的应用程序(例如数据库或驱动程序)时,人类可能会犯错误。对我来说,你的问题看起来不是边缘情况,也可能在任何数据库中导致严重的完整性问题。您可能还想考虑在 php 错误跟踪器上打开一个自动收报机。

补丁代码分析

但是我仔细研究了一下。比较 2 年前的补丁代码 ( source ) 和当前代码 ( source ) 表明自从他们修补该错误后没有任何变化。至少在那些直接受影响的功能中没有,而且当时它也可能是一个略有不同的错误。所以我的猜测是还有其他东西当然不会帮助您解决问题。

解决当前问题的可能方法m:

您可以检查您的连接是否有未完成的交易。为此,您需要在遇到异常时创建第二个连接。首先确定您当前的连接 ID。

SELECT pg_backend_pid();

在我的例子中,它返回了数字 19339。在启动将导致异常的查询之前,您应该保存此数字。 现在在您的 catch block 中,您需要查看表 pg_catalog.pg_stat_activity

查找旧连接并查看其状态是否为

activeidle in transaction or idle in transaction (aborted)

与:

SELECT state FROM pg_catalog.pg_stat_activity WHERE pid=19339;

如果它返回 idle,则表示该旧连接没有当前事务。如果它是前三者之一,则有一个交易仍然活跃。 postgresql 的手册说:

active: The backend is executing a query.
idle: The backend is waiting for a new client command.
idle in transaction: The backend is in a transaction, but is not currently executing a query.
idle in transaction (aborted): This state is similar to idle in transaction, except one of the statements in the transaction caused an error.
fastpath function call: The backend is executing a fast-path function.
disabled: This state is reported if track_activities is disabled in this backend.

最后的状态表明了这一切的缺点。 它仅在 track_activities 时有效配置标志设置为 true

关于php - PDO inTransaction() 在数据库异常后返回 false,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37952823/

相关文章:

php - 匹配 PHP 数组中的数据并显示不匹配的内容

php - 在 &lt;title&gt; 中回显 <h1> 与 php 可能

php - CakePhp:读取 Controller 内的查看规则消息

mysql - PDO - 更新和设置查询(2 个数组)

php - 一次设置每个 $row 的样式?

postgresql - 我无法从管理员连接到 postgresql

sql - 在 Postgres 中查询时间序列

sql - 如何防止 PostgreSQL 舍入 double 值的输出?

php - 对 MySQL 查询的 PHP 结果进行“分组”

php - 使用 PDO 语句获取受影响的行数和最后插入的 ID