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

Mat*_*t S 7 php postgresql pdo transactions

inTransaction()如果抛出数据库异常,PDO 仍然在事务中返回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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

PHP 5.5.32和PostgreSQL 9.4.发现了一个两年前相关的错误报告,该报告在旧版本的PHP中关闭.

cb0*_*cb0 5

要回答您的问题:

这是预期的行为还是PHP中的错误?

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

为什么我们不能依靠驱动程序或数据库来告诉我们交易是否正在进行?

因为驱动程序和数据库是由人创建的。当创建这样一个非常复杂的应用程序(例如数据库或驱动程序)时,人会犯错。对我来说,您的问题似乎不是一个极端的情况,并且还可能在任何数据库中引起严重的完整性问题。您可能还需要考虑在php bug跟踪器上打开一个代码。

补丁代码分析

但是我调查了一下。比较2年前的补丁()和当前代码()中的代码,发现自从修补该错误以来,没有任何变化。至少在那些直接受影响的函数中没有,而且那时候的错误可能也略有不同。所以我的猜测是,还有其他事情当然不会帮助您解决问题。

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

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

SELECT pg_backend_pid();

就我而言,它返回了数字19339。在启动将导致异常的查询之前,应保存该编号。现在在您的catch块中,您需要查看表内部pg_catalog.pg_stat_activity

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

activeidle in transactionidle 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.
Run Code Online (Sandbox Code Playgroud)

最后状态表明这一切的不利之处。仅将track_activities配置标志设置为true