Skip to content

Commit f740cd1

Browse files
committed
Ignore CURSOR_EXISTS flag if no cursor requested
1 parent 3eda1f3 commit f740cd1

File tree

2 files changed

+62
-22
lines changed

2 files changed

+62
-22
lines changed

ext/mysqli/tests/bug77935.phpt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Bug #77935: Crash in mysqlnd_fetch_stmt_row_cursor when calling an SP with a cursor
3+
--SKIPIF--
4+
<?php
5+
require_once('skipif.inc');
6+
require_once('skipifconnectfailure.inc');
7+
?>
8+
--FILE--
9+
<?php
10+
require_once(__DIR__ . '/connect.inc');
11+
12+
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
13+
$db = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
14+
$db->query('DROP PROCEDURE IF EXISTS testSp');
15+
$db->query(<<<'SQL'
16+
CREATE
17+
PROCEDURE `testSp`()
18+
BEGIN
19+
DECLARE `cur` CURSOR FOR SELECT 1;
20+
OPEN `cur`;
21+
CLOSE `cur`;
22+
SELECT 1;
23+
END;
24+
SQL);
25+
26+
$stmt = $db->prepare("CALL testSp()");
27+
$stmt->execute();
28+
$result = $stmt->get_result();
29+
while ($row = $result->fetch_assoc()) {
30+
var_dump($row);
31+
}
32+
33+
?>
34+
--EXPECT--
35+
array(1) {
36+
[1]=>
37+
int(1)
38+
}

ext/mysqlnd/mysqlnd_ps.c

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -576,28 +576,30 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
576576
DBG_INF_FMT("server_status=%u cursor=%u", UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status),
577577
UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS);
578578

579-
if (UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS) {
580-
DBG_INF("cursor exists");
581-
stmt->cursor_exists = TRUE;
582-
SET_CONNECTION_STATE(&conn->state, CONN_READY);
583-
/* Only cursor read */
584-
stmt->default_rset_handler = s->m->use_result;
585-
DBG_INF("use_result");
586-
} else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
587-
DBG_INF("asked for cursor but got none");
588-
/*
589-
We have asked for CURSOR but got no cursor, because the condition
590-
above is not fulfilled. Then...
591-
592-
This is a single-row result set, a result set with no rows, EXPLAIN,
593-
SHOW VARIABLES, or some other command which either a) bypasses the
594-
cursors framework in the server and writes rows directly to the
595-
network or b) is more efficient if all (few) result set rows are
596-
precached on client and server's resources are freed.
597-
*/
598-
/* preferred is buffered read */
599-
stmt->default_rset_handler = s->m->store_result;
600-
DBG_INF("store_result");
579+
if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
580+
if (UPSERT_STATUS_GET_SERVER_STATUS(stmt->upsert_status) & SERVER_STATUS_CURSOR_EXISTS) {
581+
DBG_INF("cursor exists");
582+
stmt->cursor_exists = TRUE;
583+
SET_CONNECTION_STATE(&conn->state, CONN_READY);
584+
/* Only cursor read */
585+
stmt->default_rset_handler = s->m->use_result;
586+
DBG_INF("use_result");
587+
} else {
588+
DBG_INF("asked for cursor but got none");
589+
/*
590+
We have asked for CURSOR but got no cursor, because the condition
591+
above is not fulfilled. Then...
592+
593+
This is a single-row result set, a result set with no rows, EXPLAIN,
594+
SHOW VARIABLES, or some other command which either a) bypasses the
595+
cursors framework in the server and writes rows directly to the
596+
network or b) is more efficient if all (few) result set rows are
597+
precached on client and server's resources are freed.
598+
*/
599+
/* preferred is buffered read */
600+
stmt->default_rset_handler = s->m->store_result;
601+
DBG_INF("store_result");
602+
}
601603
} else {
602604
DBG_INF("no cursor");
603605
/* preferred is unbuffered read */

0 commit comments

Comments
 (0)