-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Assumed fix for several "Empty row packet body" bug tickets #2131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
one of the bugs had a patch attached that is probably superior. |
Definitely needs fixing but the referenced patch looks like the way to go. This should go into 5.6, 7.0, and 7.1 besides master. I'll bring this up on internals because it seems that none of the maintainers of |
I wrote one of the maintainers directly before opening a thread on internals, will keep you guys updated. |
any news ? |
Nobody was able to create a reproducible test case for this so far. |
The original author of this patch has stated that there is probably a superior solution, for that reason, and because the author seems to have abandoned working on this patch, I'm closing this PR. Please take this action as encouragement to open a clean PR, with the best possible patch, and try to ensure that there is satisfactory test coverage for the change (I am aware in this case it may be problematic). |
We have a test case by now and I will take care of a clean PR asap. |
I've used this as an easily repeatable test. It sets the mysql session timeout to 3 seconds, sleeps for 5 seconds, so the mysql server will destroy the connection, then attempts to use the connection again. It is not dependent on any database or table structure.
Expected behavior is that it the output will contain the
|
One can still grab the warning with set_error_handler, and turn it into an exception to be catched. I couldn't set up a 100% reproducible test case. The best I could come up so far is: <?php
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=test_sql',
'test-root',
'...');
while (true) {
$connections = $pdo->query("SHOW PROCESSLIST")->fetchAll(PDO::FETCH_ASSOC);
foreach ($connections as $connection) {
if ($connection['Info'] === 'SHOW PROCESSLIST') {
// Don't kill yourself
continue;
} else if (strpos($connection['Info'], 'SELECT') !== false) {
$cmd = "KILL {$connection['Id']}";
echo "$cmd\n";
$pdo->exec($cmd);
}
}
usleep(200000);
} and <?php
// Trying to reproduce the "Empty row packet body" error from MTA/MDL
// Seems related to https://github.com/php/php-src/pull/2131
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=test_sql',
'test-root',
'...');
$q = $pdo->prepare("
SELECT *
FROM (
SELECT '.' AS n
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8) AS a
INNER JOIN (
SELECT 1 AS n
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8) AS b ON TRUE
INNER JOIN (
SELECT 1 AS n
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8) AS c ON TRUE
INNER JOIN (
SELECT 1 AS n
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8) AS d ON TRUE
INNER JOIN (
SELECT 1 AS n
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8) AS e ON TRUE
INNER JOIN (
SELECT 1 AS n
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8) AS f ON TRUE");
set_error_handler(static function ($errno, $errstr, $errfile, $errline) {
/** @noinspection ForgottenDebugOutputInspection */
var_dump($errno, $errstr, $errfile, $errline);
throw new LogicException($errstr);
});
ini_set('memory_limit', '2G');
for ($i = 1000; $i > 0; $i--) {
echo ".";
if ($q->execute()) {
echo $q->fetchAll()[0][0];
}
flush();
} By running both files concurrently (1st should be started 1st, then while running, start the 2nd one), chances are you'll get a "Empty Row Packet Body" (or a "Error reading result set's header" if the KILL occured before data headers were received by PDO). If you have ways of making it more reliable, please say so. Still, if you use |
Set error_info when we fail to read a packet, instead of throwing a warning. Additionally we also need to populate the right error_info in rowp_read -- we'll later take the error from the packet, not the connection. No test case, as this is hard to reliably test. I'm using the test case from: #2131 (comment)
For the record, I've applied a66d73d that fixes the issue for the test case in the preceding comment at least, but I'm not sure if it fixes all cases. |
tickets that complain about this:
https://bugs.php.net/bug.php?id=66370
https://bugs.php.net/bug.php?id=64763
https://bugs.php.net/bug.php?id=65825
One is not reliably able to tell that a mysql response was successfully consumed completly without registering an error handler, that listens for the "Empty row packet body" warning. Not any of the examples does this. We were able to reproduce this by taking a long time in processing result rows, and forcing the mysql on the other end into a write timeout, leaving us with an incomplete result.
This points against master. Once there is a narrativ on what is the correct fix. I volunteer in backporting to the appropriate branches.
Best