导致 PDO 错误的原因 当其他未缓冲的查询处于活动状态时,无法执行查询


What is causing PDO error Cannot execute queries while other unbuffered queries are active?

我有以下代码:

$dbh = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->prepare("SELECT 1");
$stmt->execute();
$result = $stmt->fetch();
$stmt->execute();
$result = $stmt->fetch();
$stmt = $dbh->prepare("SELECT 1");
$stmt->execute();
$result = $stmt->fetch();

但是,由于某种原因,我在执行第二个预准备语句时出现以下错误:

致命错误:未捕获的异常"PDOException",并显示消息 'SQLSTATE[HY000]:一般错误:2014 无法执行查询,而 其他未缓冲的查询处于活动状态。考虑使用 PDOStatement::fetchAll().或者,如果您的代码只是永远 要针对 MySQL 运行,您可以通过设置 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY属性。

我知道这个错误意味着什么以及如何修复它(要么做unset($stmt);要么$stmt->closeCursor();),所以我不是在寻找如何让它工作的解决方案。据我了解,这通常是由于执行fetch而不是fetchAll并且没有获取所有结果引起的。但是,在这种情况下,只有一个结果,并且正在获取该结果。此外,如果我只执行第一个预准备语句一次,则不会发生错误。仅当第一个语句执行两次时,才会发生这种情况。它也只发生在PDO::ATTR_EMULATE_PREPARES false时。

所以我的问题是,在这种情况下导致上述错误发生的原因是什么?它似乎与我执行过的任何其他查询没有任何不同。

我已经在两台 Ubuntu 13.10 服务器 Debian 和 CentOS 上对此进行了测试,并且都使用默认软件包产生相同的错误。

编辑:

为了回答 Ryan Vincent 的评论,我是一个完整的 mysqli 菜鸟,但我相信我下面的内容大致相当于上面的例子。如果我错了,请纠正我。但是,它不会产生任何错误,因此它似乎是仅 PDO 的错误:

$mysqli = new mysqli($host, $user, $pass, $dbname);
if ($mysqli->connect_errno) {
    die("Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error);
}
if (!($stmt = $mysqli->prepare("SELECT 1"))) {
     die("Prepare 1 failed: (" . $mysqli->errno . ") " . $mysqli->error);
}
if (!$stmt->execute()) {
    die("Execute 1 failed: (" . $stmt->errno . ") " . $stmt->error);
}
$stmt->store_result();
$stmt->bind_result($col1);
$stmt->fetch();
if (!$stmt->execute()) {
    die("Execute 2 failed: (" . $stmt->errno . ") " . $stmt->error);
}
$stmt->store_result();
$stmt->bind_result($col1);
$stmt->fetch();
if (!($stmt = $mysqli->prepare("SELECT 1"))) {
    // The following line is what fails in PDO
    die("Prepare 2 failed: (" . $mysqli->errno . ") " . $mysqli->error);
}
if (!$stmt->execute()) {
    die("Execute 3 failed: (" . $stmt->errno . ") " . $stmt->error);
}
$stmt->store_result();
$stmt->bind_result($col1);
$stmt->fetch();

奇怪的是,Ubuntu 提供的 PHP 包不是使用 Mysql 原生驱动程序编译的,而是使用旧的 libmysqlclient 编译的(在 Ubuntu 13.10 上使用默认软件包进行了测试):

<?php
echo $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION); // prints "5.5.35", i.e MySQL version
// prints "mysqlnd (...)" when using mysqlnd

您的测试用例("Edit 4",带有setAttribute(MYSQL_ATTR_USE_BUFFERED_QUERY, true))在 PHP 5.5.3 中按预期工作,使用 mysqlnd 手动编译:

./configure --with-pdo-mysql=mysqlnd # default driver since PHP v5.4

。但失败并显示:

bash> ./configure --with-pdo-mysql=/usr/bin/mysql_config
奇怪的是

,只有当第一个语句执行两次时,它才会失败;这一定是libmysqlclient驱动程序中的一个错误。

MYSQL_ATTR_USE_BUFFERED_QUERY false时,两个驱动程序都按预期失败。您的常识已经说明了为什么这是预期行为,无论结果集中的行数如何。

Mike发现当前的解决方法是安装php5-mysqlnd软件包,而不是Canonical推荐的php5-mysql

这不一定是这个问题的答案,但这可能会在未来帮助某人。

我遇到了完全相同的错误,花了几个小时才发现出了什么问题。事实证明,这一直只是一个极其小的语法问题。如果您实际上没有使用任何缓冲,但仍然像我一样出现此错误,这可能是您的问题 - 因此请检查您的代码。

当我遇到此错误时,我正在执行正常的数据库查询 - 不是故意使用任何缓冲技术 - 所以我非常怀疑它与缓冲有关。我阅读了有关它的每一个SO问题,并深入研究了它。

这是我愚蠢的语法问题:

$SQL = "UPDATE articles SET
            topicID = :topic;    <-------- semicolon - woops!
            heading = :heading,
            subheading = :subheading,
            keywords = :keywords,
            rawContent = :rawContent,
            content = :content,
            ...
            ...

这导致我收到此缓冲错误。我修复了代码,它消失了。最烦人的是,PDO错误指向另一个查询,下一个查询,但该查询在代码中其他地方的函数中,并且通过我有一段时间偏离了轨道!

看来你PDO::MYSQL_ATTR_USE_BUFFERED_QUERY设置为 FALSE。

在这种情况下,必须确保没有更多的行待检索。要这样做,需要额外运行一次fetch(),因为似乎fetch()返回 false 会以某种方式"释放"非缓冲结果集。如果没有这种额外的调用,非缓冲结果集将保持锁定状态,并导致"命令不同步"错误

只是为了完成导致此问题的可能错误列表......因为我在这方面失去了头发,我想与您分享我的解决方案/发现。

就我而言,我尝试使用 PDO::exec 向数据库发送了几个语句

例如

self::$objDatabase->exec( "SELECT id from testtable; UPDATE testtable SET name = 'example';" );

只允许在 1 个 PDO::exec 中保存 1 个 SQL 语句。