Commit e6184749b50 for php
commit e6184749b502db932e5dd06a8ad306a9ad7719ed
Author: Kamil Tekiela <tekiela246@gmail.com>
Date: Tue Sep 29 17:13:13 2026 +0100
Reset field_count for OK packet (#23890)
diff --git a/NEWS b/NEWS
index 4ace934375f..7424a378c89 100644
--- a/NEWS
+++ b/NEWS
@@ -68,6 +68,9 @@ PHP NEWS
. Fix GH-22854: Fixed failed assertion when accessing mysqli property after
failed reconnection. (Kamil Tekiela)
+- MySQLnd:
+ . Fixed field_count not resetting on OK packet. (Kamil Tekiela)
+
- Opcache:
. Fixed OSS-Fuzz #546798343 (Heap-buffer-overflow in optimizer with
FCCs and inlining). (ndossche)
diff --git a/ext/mysqli/tests/mysqli_stmt_next_result_field_count.phpt b/ext/mysqli/tests/mysqli_stmt_next_result_field_count.phpt
new file mode 100644
index 00000000000..4b04151d054
--- /dev/null
+++ b/ext/mysqli/tests/mysqli_stmt_next_result_field_count.phpt
@@ -0,0 +1,34 @@
+--TEST--
+mysqli_stmt::$field_count is 0 for the trailing OK packet of a stored procedure
+--EXTENSIONS--
+mysqli
+--SKIPIF--
+<?php
+require_once 'skipifconnectfailure.inc';
+?>
+--FILE--
+<?php
+require_once 'connect.inc';
+
+mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
+$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
+$link->query('DROP PROCEDURE IF EXISTS test_field_count_p');
+$link->query('CREATE PROCEDURE test_field_count_p() BEGIN SELECT 1 AS a; SELECT 2 AS b, 3 AS c; END');
+
+$stmt = $link->prepare('CALL test_field_count_p()');
+$stmt->execute();
+do {
+ var_dump($stmt->field_count);
+ $stmt->get_result();
+} while ($stmt->next_result());
+?>
+--CLEAN--
+<?php
+require_once 'connect.inc';
+$link = new mysqli($host, $user, $passwd, $db, $port, $socket);
+$link->query('DROP PROCEDURE IF EXISTS test_field_count_p');
+?>
+--EXPECT--
+int(1)
+int(2)
+int(0)
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 28527c66ccf..681c8f2a460 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -515,6 +515,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
stmt->state = MYSQLND_STMT_EXECUTED;
if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
+ stmt->field_count = conn->field_count;
DBG_INF("PASS");
DBG_RETURN(PASS);
}
diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset_columncount.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset_columncount.phpt
new file mode 100644
index 00000000000..94d739dc7c5
--- /dev/null
+++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset_columncount.phpt
@@ -0,0 +1,39 @@
+--TEST--
+MySQL PDOStatement->columnCount() is 0 for the trailing OK packet of a stored procedure
+--EXTENSIONS--
+pdo_mysql
+--SKIPIF--
+<?php
+require_once __DIR__ . '/inc/mysql_pdo_test.inc';
+MySQLPDOTest::skip();
+?>
+--FILE--
+<?php
+require_once __DIR__ . '/inc/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP PROCEDURE IF EXISTS pdo_mysql_stmt_nextrowset_columncount_p');
+$db->exec('CREATE PROCEDURE pdo_mysql_stmt_nextrowset_columncount_p() BEGIN SELECT 1 AS a; SELECT 2 AS b, 3 AS c; END');
+
+foreach ([true, false] as $emulate) {
+ $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate);
+ $stmt = $db->prepare('CALL pdo_mysql_stmt_nextrowset_columncount_p()');
+ $stmt->execute();
+ do {
+ var_dump($stmt->columnCount());
+ $stmt->fetchAll();
+ } while ($stmt->nextRowset());
+}
+?>
+--CLEAN--
+<?php
+require_once __DIR__ . '/inc/mysql_pdo_test.inc';
+$db = MySQLPDOTest::factory();
+$db->exec('DROP PROCEDURE IF EXISTS pdo_mysql_stmt_nextrowset_columncount_p');
+?>
+--EXPECT--
+int(1)
+int(2)
+int(0)
+int(1)
+int(2)
+int(0)