13

我有以下代码:

$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();),所以我不是在寻找如何让它工作的解决方案。据我了解,这通常是由做fetchfetchAll不是获取所有结果引起的。但是在这种情况下,只有一个结果并且正在被获取。另外,如果我只执行第一个准备好的语句一次,则不会发生错误。它仅在第一条语句执行两次时发生。它也只发生在PDO::ATTR_EMULATE_PREPARESis时false

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

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

编辑:

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

$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();
4

4 回答 4

13

奇怪的是,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”,with setAttribute(MYSQL_ATTR_USE_BUFFERED_QUERY, true))可以使用 mysqlnd 手动编译的 PHP 5.5.3 按预期工作:

./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当is时,两个驱动程序都按预期失败false。无论结果集中有多少行,您的常识已经证明了为什么这是预期的行为。

Mike 发现当前的解决方法是安装php5-mysqlnd包而不是 Canonical-recommended php5-mysql

于 2014-04-07T06:37:26.553 回答
3

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

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

当我遇到这个错误时,我正在执行我的正常数据库查询——不是故意使用任何缓冲技术——所以我高度怀疑它与缓冲有什么关系。我阅读了所有关于它的 SO 问题,并对其进行了更深入的研究。

这是我的 STUPID 语法问题:

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

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

于 2014-08-11T22:50:56.483 回答
2

看来您已PDO::MYSQL_ATTR_USE_BUFFERED_QUERY设置为 FALSE。

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

于 2014-04-02T08:44:55.063 回答
0

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

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

例如

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

1 个 PDO::exec 中仅允许并保存 1 个 SQL 语句。

于 2017-10-23T11:03:27.493 回答