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)