我正在尝试使用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。
答案 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)
错误出现在 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;
?>