PDO MySQL: Normalize handling of empty stored procedure result set

MySQL always returns a trailing empty result set for stored
procedure calls, which is used to convey status information.
The PDO MySQL implementation is presently confused about what to
do with it: If mysqlnd is used and native prepared statements are
used, this result set is skipped. In all other cases it is not
skipped. We also have quite a few XFAILed tests relating to this.

This patch normalizes (for PHP-8.0 only) the behavior towards
always retaining the empty result set. This is simply how MySQL
stored procedures work (some expletives omitted here) and we can't
distinguish this "useless" result set from an empty result of a
multi query. Multi queries are not a concern for native prepared
statements, as PDO does not allow them in that case, but they are
a concern for emulated prepared statements.

Closes GH-6497.
This commit is contained in:
Nikita Popov 2020-12-08 14:28:18 +01:00
parent bfa69d27bc
commit 2df09b9b64
6 changed files with 66 additions and 36 deletions

View file

@ -359,16 +359,6 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt) /* {{{ */
PDO_DBG_RETURN(0);
}
if (!mysqlnd_stmt_more_results(S->stmt)) {
/*
MySQL gives us n + 1 result sets for
CALL proc() and n result sets returned by the proc itself.
Result set n + 1 is about the procedure call itself.
As the PDO emulation does not return it, we skip it as well
*/
PDO_DBG_RETURN(0);
}
/* TODO - this code is stolen from execute() - see above */
if (S->result) {
mysql_free_result(S->result);

View file

@ -18,8 +18,6 @@ if ($version < 50000)
die(sprintf("skip Need MySQL Server 5.0.0+, found %d.%02d.%02d (%d)\n",
$matches[1], $matches[2], $matches[3], $version));
?>
--XFAIL--
nextRowset() problem with stored proc & emulation mode & mysqlnd
--FILE--
<?php
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
@ -79,6 +77,8 @@ array(1) {
string(1) "4"
}
}
array(0) {
}
array(1) {
[0]=>
array(1) {
@ -86,6 +86,8 @@ array(1) {
string(1) "4"
}
}
array(0) {
}
Native Prepared Statements...
array(1) {
[0]=>
@ -94,6 +96,8 @@ array(1) {
string(1) "4"
}
}
array(0) {
}
array(1) {
[0]=>
array(1) {
@ -101,4 +105,6 @@ array(1) {
string(1) "4"
}
}
array(0) {
}
done!

View file

@ -1,7 +1,5 @@
--TEST--
PDO MySQL Bug #41997 (stored procedure call returning single rowset blocks future queries)
--XFAIL--
nextRowset() problem with stored proc & emulation mode & mysqlnd
--SKIPIF--
<?php
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
@ -46,6 +44,8 @@ array(1) {
string(1) "1"
}
}
array(0) {
}
array(3) {
[0]=>
string(5) "00000"

View file

@ -53,31 +53,13 @@ MySQLPDOTest::skip();
// requires MySQL 5+
$stmt = $db->prepare('CALL p()');
$stmt->execute();
$expected = array(
array(
"z" => NULL,
"a" => NULL,
"b" => " ",
"c" => NULL,
"d" => " d",
"e" => " e",
),
);
do {
$tmp = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($tmp != $expected) {
printf("[004] Expecting %s got %s\n",
var_export($expected, true), var_export($tmp, true));
}
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
} while ($stmt->nextRowset());
$stmt->execute();
do {
$tmp = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($tmp != $expected) {
printf("[005] Expecting %s got %s\n",
var_export($expected, true), var_export($tmp, true));
}
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
} while ($stmt->nextRowset());
}
@ -124,4 +106,42 @@ array(1) {
string(3) "%se"
}
}
array(1) {
[0]=>
array(6) {
["z"]=>
NULL
["a"]=>
string(0) ""
["b"]=>
string(1) " "
["c"]=>
string(0) ""
["d"]=>
string(2) " d"
["e"]=>
string(2) " e"
}
}
array(0) {
}
array(1) {
[0]=>
array(6) {
["z"]=>
NULL
["a"]=>
string(0) ""
["b"]=>
string(1) " "
["c"]=>
string(0) ""
["d"]=>
string(2) " d"
["e"]=>
string(2) " e"
}
}
array(0) {
}
done!

View file

@ -132,6 +132,8 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
array(3) {
[0]=>
@ -173,6 +175,8 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
Warning: PDO::query(): SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your %s server version for the right syntax to use near 'INSERT INTO test (id, label) VALUES (99, 'x')' at line 1 in %s on line %d
@ -219,6 +223,8 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
array(3) {
[0]=>
@ -260,6 +266,8 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
string(5) "00000"
done!

View file

@ -1,7 +1,5 @@
--TEST--
MySQL PDOStatement->nextRowSet()
--XFAIL--
nextRowset() problem with stored proc & emulation mode & mysqlnd
--SKIPIF--
<?php
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
@ -159,6 +157,8 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
array(1) {
[0]=>
@ -208,6 +208,8 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
Native PS...
array(1) {
@ -258,6 +260,8 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
array(1) {
[0]=>
@ -307,5 +311,7 @@ array(3) {
string(1) "a"
}
}
array(0) {
}
bool(false)
done!