调用MySQL存储过程时数据包乱序错误

时间:2013-10-15 19:50:27

标签: php mysql sql stored-procedures pdo

我正在尝试使用PDO调用存储过程,但在尝试对结果进行提取时遇到以下错误。

警告:数据包乱序。预计1收到16.数据包大小= 163

我的存储过程使用了两个游标,在从临时表中选择之前我关闭了这两个游标。我怀疑这可能是问题,因为我可以直接在MySQL中调用我的SP并且可以看到结果。在迁移到php_pdo_mysql.dll之前使用php_mysql扩展时,我也没遇到过这个SP的问题。 我还可以使用PDO在PHP中调用包含INPUT参数的其他更简单的存储过程,并且可以毫无错误地获取结果。

以下是返回错误的代码:

$db = new PDO('mysql:host='.__DB_HOST__.';dbname='.__DB_NAME__.';charset=utf8', __DB_USER__, __DB_PASS__);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

/* DOES NOT WORK */
$queryResult = $db->prepare("CALL GetResults(:siteId,null)");
$siteId = 19;
$queryResult->bindValue(':siteId', $siteId, PDO::PARAM_INT);
$queryResult->execute();
$result = $queryResult->fetchAll(PDO::FETCH_ASSOC); // returns packets out of order warning
print_r($result);

我在Try / Catch块中有这个代码,没有抛出任何异常。事实上,PHP在浏览器中将其显示为警告。

我的存储过程签名如下所示:

CREATE DEFINER=`root`@`localhost` 
PROCEDURE `GetResults`(IN siteIdParam INT(11), IN siteSearchText VARCHAR(45))

我也不确定问题是否将 null 作为其中一个参数传递。有时第一个参数传递 null ,有时它是第二个。但无论它总是直接在MySQL服务器上运行。

我尝试了bindParam和bindValue,结果相同。我也可以发布我的SP,但它可能有点过分。

有没有办法从PDO扩展开启其他日志记录?

有任何想法或建议吗?如果您需要更多信息,请告诉我。

注意:我使用的是PHP v5.5.4和MySQL v5.6.14。

7 个答案:

答案 0 :(得分:11)

在花了很多时间试图隔离部分代码来解决这个问题之后,我注意到在将ATTR_EMULATE_PREPARES标志设置为true后错误消失了。

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

这告诉PDO模拟准备好的语句,而不是MySQL本地模拟。根据我一直在阅读的内容,如果您使用最新版本的MySQL和PHP,通常建议关闭此标志(默认情况下为true)。您可以在此SO article中找到有关该内容的更多信息。

我确实认为这是MySQL的一个错误(我遇到了5.6.17版本的问题)。关于这个特定问题没有太多讨论,所以希望这可以节省其他时间的故障排除。问题也在this MySQL bug page上进行了讨论,但是在我的情况下,发布的解决方案并没有帮助我。

答案 1 :(得分:3)

我尝试更改PDO :: ATTR_EMULATE_PREPARES属性时遇到了同样的问题。

当我尝试在同一个PDO连接上调用第二个预处理语句时,我可以可靠地重现您所描述的内容。像这样:

$db = new PDO('mysql:host='.__DB_HOST__.';dbname='.__DB_NAME__.';charset=utf8', __DB_USER__, __DB_PASS__);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

/* DOES NOT WORK */
$queryResult = $db->prepare("CALL GetResults(:siteId,null)");
$siteId = 19;
$queryResult->bindValue(':siteId', $siteId, PDO::PARAM_INT);
$queryResult->execute();
$result = $queryResult->fetchAll(PDO::FETCH_ASSOC); // works fine
print_r($result); 

// Call a second stored procedure
$secondCall = $db->prepare("CALL GetOtherResults()");
$secondCall->execute();
$secondResult = $secondCall->fetchAll(PDO::FETCH_ASSOC); // returns packets out of order
print_r($secondResult);

我知道你的例子只显示一个调用,但是为了触发这个错误,存储的proc调用不必在同一个文件中,只是共享相同的PDO连接。我希望下面的建议仍有帮助。

执行存储过程时,MySQL驱动程序返回两个行集。在重用连接之前,本机驱动程序要求您处理两者。您需要advance to the second rowset,即使您不关心其中的内容以及close the cursor,然后再准备并拨打第二个。像这样:

...
$queryResult->execute();
$result = $queryResult->fetchAll(PDO::FETCH_ASSOC);
$queryResult->nextRowSet();
$queryResult->closeCursor();

// Now you can prepare your second statement
...

如果您忘记拨打这两个额外的电话,看起来PDO的模拟准备会更宽容。

答案 2 :(得分:2)

@ http203我使用带有mysql 5.6.17的WAMP服务器使用此

遇到了同样的问题
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

解决了这个问题,但我也建议在mysql.ini中增加查询限制

max_allowed_packet = 1MB //Default

max_allowed_packet = 3MB

为防止未来出现问题,对于有相关问题的任何人,请阅读此参考文章Warning: Packets out of order, MySQL server has gone away

答案 3 :(得分:1)

此 PHP 错误已在 PHP 7.4.15 中修复

错误出现在 mysqlnd 中,当使用准备好的语句时,它无法从内部使用游标的存储过程中正确获取记录。现在已经修复了。

对于使用旧版本的人,切换到模拟准备的解决方法仍然有效。

答案 4 :(得分:0)

对我来说,将PDO::ATTR_PERSISTENT设置为false可以解决此问题。

mysql_upgrade输入MariaDB之后开始出现问题。不知道实际的问题是什么。 :-/

答案 5 :(得分:0)

我的存储过程中有光标。我通过一次获取所有记录然后进行遍历来解决。我已经没有这个“通知”了

$sqlQuery = $conn->prepare('CALL myStoredProcedure(?,?)');
$sqlQuery->bind_param('ss', $param1, $param2);
$sqlQuery->execute();
$resultSet = $sqlQuery->get_result();
$result = $resultSet->fetch_all();
$len = sizeof($result);

for($i=0; $i < $len; $i++){
  // Fetching data and processing it
}

答案 6 :(得分:-1)

就我而言,我需要关闭数据库连接。

我的错误如下:

致命错误:未捕获的PDOException:数据包混乱。 预期收到0个1.数据包大小= 23 in html \ www \ website \ core \ Database.php:41堆栈 跟踪:

这里是Example #3 Closing a connection

<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// use the connection here
$sth = $dbh->query('SELECT * FROM foo');

// and now we're done; close it
$sth = null;
$dbh = null;
?>