如果抛出数据库异常,PDO' s inTransaction()
仍在事务中返回false。这可能是特定于使用PostgreSQL。 e.g。
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中关闭了。
答案 0 :(得分:5)
回答您的问题:
这是预期的行为还是PHP中的错误?
这不是预期的行为,必须是PDO PgSQL扩展中的错误。
为什么我们不能依赖驱动程序或数据库告诉我们是否一个 交易正在进行中?
因为驱动程序和数据库是由人类创建的。当创建如此高度复杂的应用程序(如数据库或驱动程序)时,人类可能会犯错误。对我来说,你的问题看起来不是边缘情况,也可能导致任何数据库中出现严重的完整性问题。您可能还想考虑在php bug跟踪器上打开一个自动收报机。
修补代码分析
然而我更多地研究了它。比较2年前补丁(source)和当前代码1(source)中的代码,表明自修补该错误后没有任何变化。至少不在那些直接受影响的功能中,当时它可能也是一个稍微不同的错误。所以我的猜测是,还有其他东西当然无法帮助你解决问题。
围绕当前问题的可能工作 m:
您可以检查您的连接是否有未完成的交易。为此,您需要在命中异常时创建第二个连接。首先确定您当前的连接ID。
SELECT pg_backend_pid();
在我的情况下,它返回了数字19339
。您应该在启动将导致异常的查询之前保存此数字。
现在在catch块中,您需要查看表pg_catalog.pg_stat_activity
内部。
查找旧连接并查看其状态是否为
active
,idle in transaction
或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
时才有效。