调用 MySQL 存储过程时出现数据包无序错误


Packets out of order error when calling MySQL stored proc

我正在尝试使用 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。

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

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

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

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

当我

尝试更改 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 驱动程序将返回两个行集。 本机驱动程序要求您在重用连接之前处理两者。 您需要前进到第二行集,即使您不关心其中的内容,并且在准备和调用第二行集之前还要关闭光标。 喜欢这个:

...
$queryResult->execute();
$result = $queryResult->fetchAll(PDO::FETCH_ASSOC);
$queryResult->nextRowSet();
$queryResult->closeCursor();
// Now you can prepare your second statement
...

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

@http203我使用带有mysql 5.6.17的WAMP服务器时遇到了同样的问题

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

修复了这个问题,但我也建议增加 mysql 中的查询限制.ini

max_allowed_packet = 1MB //Default

max_allowed_packet = 3MB

为了防止将来出现问题,对于有相对问题的任何人,请阅读此参考文章 警告:数据包顺序错误,MySQL 服务器已消失

这个 PHP

错误已在 PHP 7.4.15 中修复

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

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