【问题标题】:What is causing PDO error Cannot execute queries while other unbuffered queries are active?什么导致 PDO 错误在其他无缓冲查询处于活动状态时无法执行查询?
【发布时间】:2014-05-13 07:01:32
【问题描述】:

我有以下代码:

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

但是,由于某种原因,在执行 second 准备好的语句时出现以下错误:

致命错误:带有消息的未捕获异常“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-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();

【问题讨论】:

  • SHOW TRIGGERS 返回一个空的结果集,所以我猜不是。上面的测试用例是一个新创建的数据库。
  • 在这种情况下,无论是真还是假,我都会收到错误消息,但我知道应用程序的其余部分设置为真,因为当我将其设置为假时,我开始在各处收到错误.这是唯一在缓冲为真时出现此错误的地方。
  • “但是在这种情况下,只有一个结果并且正在被获取” – 这并不重要,因为此时没有人知道 这是唯一的结果记录。 (也许你除外,因为你知道你在查询中输入了LIMIT 1……但其他相关方并不关心。) - “这对我来说根本没有意义,因为错误基本上意味着存在是更多的行” – 不,这并不意味着;这意味着目前还不清楚如果还有更多的结果记录。调用nextRowset“修复”了这个,因为现在,在返回false之后,它是清晰的。
  • 您是否尝试过使用“mysqli”类的相同示例代码?我的理解是他们(PDO 和 mysqli)在 PHP 中使用不同的驱动程序。根据文档,您正在做“正确的事情”。
  • 我在猜测,但有趣的是,您使用 PDO 执行了两个相同的“准备”语句,并且它经常“砰”地一声。我怀疑您遇到的驱动程序中存在“优化”逻辑。因此,在您的 PDO 示例中,要么在第二个准备之前取消设置第一个 '$stmt' 变量,要么使用一个新的 $stmtAnother' 变量来说服驱动程序重建查询。这都是猜测。这是我的意见,它是免费并且值得完全您为它付出的。

标签: php mysql pdo prepared-statement unbuffered-queries


【解决方案1】:

只是为了完成导致此问题的可能错误列表...因为我对此感到无所适从,我想与您分享我的解决方案/发现。

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

例如

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

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

【讨论】:

    【解决方案2】:

    这不一定是这个问题的答案,但这可能对未来的人有所帮助。

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

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

    这是我的 STUPID 语法问题:

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

    这导致我收到此缓冲错误。我修复了代码,它消失了。最烦人的是,PDO 错误指向另一个查询,即下一个查询,但该查询位于代码中其他位置的函数中,而这在我看来已经偏离了轨道!

    【讨论】:

      【解决方案3】:

      奇怪的是,Ubuntu 提供的 PHP 包不是用 Mysql native driver 编译的,而是用旧的 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
      

      您的测试用例(“编辑 4”,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 为false 时,两个驱动程序都按预期失败。 Your Common Sense already demonstrated 为什么这是预期行为,无论结果集中有多少行。

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

      【讨论】:

      • 实际上,它适用于 mysqli(参见我的编辑 5),但不适用于 PDO。 PDO 使用 libmysqlclient 和 mysqli 使用 mysqlnd 是否可能在同一台服务器上?
      • Err... 我的意思是最后的“libmysqlclient”(已修复)。是的,你可以用./configure --with-pdo-mysql=/usr/bin/mysql_config --with-mysqli=mysqlnd 很好地编译你的PHP。让我测试一下它的行为。
      • 是的,我刚刚意识到所有 API 都使用相同的驱动程序(否则会很奇怪)。我想说在 PDO 或 libmysqlclient 中存在一个错误 either,因为 PDO 与 mysqlnd 的工作原理一样。我可能会在 PHP 上提交一个错误,即使我认为它不会被修复,旧的驱动程序有点被弃用。或者,让我们建议 Ubuntu 人员更新他们的编译脚本。
      • @RandomSeed 我刚刚意识到有一个适用于 Ubuntu 的 php5-mysqlnd 包,但是 php5-mysql(它使用 libmysqlclient)在包管理器中有一个小的“Canonical 推荐”图标。如您所见,安装它可以消除错误。
      【解决方案4】:

      您似乎将PDO::MYSQL_ATTR_USE_BUFFERED_QUERY 设置为FALSE。

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

      【讨论】:

      • 如果您一次从“结果集”中获取一行,那么 Mysql 无法关闭结果集,直到您从中获取最后一行。这意味着即使从查询中只返回一行,您也需要获取两次,因为在您尝试读取它们之前,您无法判断没有更多的行。
      • @RyanVincent 是的,但前提是没有缓冲结果。如果是的话,只取一次就足够了
      • 抱歉,我现在“想不通”。
      • 试试看,如果你不喜欢这个声音。
      • 我错了!我已经阅读了我在这里使用的“mysql”版本(Windows XP 上为 5.5.16)的“mysqli”和“PDO”API 的说明,您说得非常正确
      猜你喜欢
      • 2011-01-05
      • 2015-03-28
      • 1970-01-01
      • 2014-09-18
      • 2017-11-09
      • 2015-03-19
      • 1970-01-01
      相关资源
      最近更新 更多