diff options
| author | Nikita Popov <nikita.ppv@gmail.com> | 2020-12-16 12:12:06 +0100 |
|---|---|---|
| committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-12-18 10:19:13 +0100 |
| commit | bc166844e37a6e1531a18dc0916fbe508152fc6c (patch) | |
| tree | c7f9d77387bba95a1ef7feee421da07c4c104448 /ext/mysqli/tests | |
| parent | 315f3f8dc9b3ba3639c9284301933286fc99a825 (diff) | |
| download | php-git-bc166844e37a6e1531a18dc0916fbe508152fc6c.tar.gz | |
MySQLnd: Support cursors in store/get result
This fixes two related issues:
1. When a PS with cursor is used in store_result/get_result,
perform a COM_FETCH with maximum number of rows rather than
silently switching to an unbuffered result set (in the case of
store_result) or erroring (in the case of get_result).
In the future, we might want to make get_result unbuffered for
PS with cursors, as using cursors with buffered result sets
doesn't really make sense. Unlike store_result, get_result
isn't very explicit about what kind of result set is desired.
2. If the client did not request a cursor, but the server reports
that a cursor exists, ignore this and treat the PS as if it
has no cursor (i.e. to not use COM_FETCH). It appears to be a
server side bug that a cursor used inside an SP will be reported
to the client, even though the client cannot use the cursor.
Fixes bug #64638, bug #72862, bug #77935.
Closes GH-6518.
Diffstat (limited to 'ext/mysqli/tests')
| -rw-r--r-- | ext/mysqli/tests/bug77935.phpt | 38 | ||||
| -rw-r--r-- | ext/mysqli/tests/mysqli_stmt_get_result.phpt | 49 | ||||
| -rw-r--r-- | ext/mysqli/tests/ps_cursor_multiple_result_sets.phpt | 99 |
3 files changed, 153 insertions, 33 deletions
diff --git a/ext/mysqli/tests/bug77935.phpt b/ext/mysqli/tests/bug77935.phpt new file mode 100644 index 0000000000..7a39ac0065 --- /dev/null +++ b/ext/mysqli/tests/bug77935.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #77935: Crash in mysqlnd_fetch_stmt_row_cursor when calling an SP with a cursor +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifconnectfailure.inc'); +?> +--FILE-- +<?php +require_once(__DIR__ . '/connect.inc'); + +mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); +$db = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); +$db->query('DROP PROCEDURE IF EXISTS testSp'); +$db->query(<<<'SQL' +CREATE + PROCEDURE `testSp`() + BEGIN + DECLARE `cur` CURSOR FOR SELECT 1; + OPEN `cur`; + CLOSE `cur`; + SELECT 1; + END; +SQL); + +$stmt = $db->prepare("CALL testSp()"); +$stmt->execute(); +$result = $stmt->get_result(); +while ($row = $result->fetch_assoc()) { + var_dump($row); +} + +?> +--EXPECT-- +array(1) { + [1]=> + int(1) +} diff --git a/ext/mysqli/tests/mysqli_stmt_get_result.phpt b/ext/mysqli/tests/mysqli_stmt_get_result.phpt index 712537a10b..fc07a1d2a2 100644 --- a/ext/mysqli/tests/mysqli_stmt_get_result.phpt +++ b/ext/mysqli/tests/mysqli_stmt_get_result.phpt @@ -110,21 +110,7 @@ if (!function_exists('mysqli_stmt_get_result')) mysqli_stmt_close($stmt); - if (!$stmt = mysqli_stmt_init($link)) - printf("[032] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); - - if (!mysqli_stmt_prepare($stmt, "SELECT id, label FROM test ORDER BY id LIMIT 2")) - printf("[033] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); - - if (!mysqli_stmt_execute($stmt)) - printf("[034] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); - - $id = NULL; - $label = NULL; - if (true !== ($tmp = mysqli_stmt_bind_result($stmt, $id, $label))) - printf("[035] Expecting boolean/true, got %s/%s\n", gettype($tmp), var_export($tmp, 1)); - - // get_result cannot be used in PS cursor mode + // get_result can be used in PS cursor mode if (!$stmt = mysqli_stmt_init($link)) printf("[030] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); @@ -137,23 +123,10 @@ if (!function_exists('mysqli_stmt_get_result')) if (!mysqli_stmt_execute($stmt)) printf("[033] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); - mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); - try { - $res = mysqli_stmt_get_result($stmt); - // we expect no segfault if we try to fetch a row because get_result should throw an error or return false - mysqli_fetch_assoc($res); - } catch (\mysqli_sql_exception $e) { - echo $e->getMessage() . "\n"; - } - - try { - $res = $stmt->get_result(); - // we expect no segfault if we try to fetch a row because get_result should throw an error or return false - $res->fetch_assoc(); - } catch (\mysqli_sql_exception $e) { - echo $e->getMessage() . "\n"; + $result = mysqli_stmt_get_result($stmt); + while ($row = mysqli_fetch_assoc($result)) { + var_dump($row); } - mysqli_report(MYSQLI_REPORT_OFF); if (!$stmt = mysqli_stmt_init($link)) printf("[034] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); @@ -213,8 +186,18 @@ Warning: mysqli_stmt_fetch(): invalid object or resource mysqli_stmt Warning: mysqli_stmt_get_result(): invalid object or resource mysqli_stmt in %s on line %d -mysqli_stmt_get_result() cannot be used with cursors -get_result() cannot be used with cursors +array(2) { + ["id"]=> + int(1) + ["label"]=> + string(1) "a" +} +array(2) { + ["id"]=> + int(2) + ["label"]=> + string(1) "b" +} [040] [2014] [Commands out of sync; you can't run this command now] [041] [0] [] array(2) { diff --git a/ext/mysqli/tests/ps_cursor_multiple_result_sets.phpt b/ext/mysqli/tests/ps_cursor_multiple_result_sets.phpt new file mode 100644 index 0000000000..ed15d10e4a --- /dev/null +++ b/ext/mysqli/tests/ps_cursor_multiple_result_sets.phpt @@ -0,0 +1,99 @@ +--TEST-- +PS using cursor and returning multiple result sets +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifconnectfailure.inc'); +?> +--FILE-- +<?php +require_once(__DIR__ . '/connect.inc'); + +mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); +$db = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); +$db->query('DROP PROCEDURE IF EXISTS testPs'); +$db->query(<<<'SQL' +CREATE PROCEDURE testPs() BEGIN + DECLARE testCursor CURSOR FOR SELECT 'stuff'; + OPEN testCursor; + CLOSE testCursor; + SELECT 1 as a, 2 as b; + SELECT 3 as a, 4 as b; +END +SQL +); + +echo "use_result:\n"; +$stmt = $db->prepare("call testPs()"); +$stmt->execute(); +$stmt->bind_result($v1, $v2); +while ($stmt->fetch()) { + var_dump($v1, $v2); +} + +$stmt->next_result(); +$stmt->bind_result($v1, $v2); +while ($stmt->fetch()) { + var_dump($v1, $v2); +} +$stmt->next_result(); + +echo "\nstore_result:\n"; +$stmt = $db->prepare("call testPs()"); +$stmt->execute(); +$stmt->store_result(); +$stmt->bind_result($v1, $v2); +while ($stmt->fetch()) { + var_dump($v1, $v2); +} + +$stmt->next_result(); +$stmt->store_result(); +$stmt->bind_result($v1, $v2); +while ($stmt->fetch()) { + var_dump($v1, $v2); +} +$stmt->next_result(); + +echo "\nget_result:\n"; +$stmt = $db->prepare("call testPs()"); +$stmt->execute(); +$result = $stmt->get_result(); +while ($row = $result->fetch_assoc()) { + var_dump($row); +} + +$stmt->next_result(); +$result = $stmt->get_result(); +while ($row = $result->fetch_assoc()) { + var_dump($row); +} +$stmt->next_result(); + +?> +--EXPECT-- +use_result: +int(1) +int(2) +int(3) +int(4) + +store_result: +int(1) +int(2) +int(3) +int(4) + +get_result: +array(2) { + ["a"]=> + int(1) + ["b"]=> + int(2) +} +array(2) { + ["a"]=> + int(3) + ["b"]=> + int(4) +} |
