From edaf00b79fa7b2e87397617a29b635e9d204cb05 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Thu, 24 Sep 2026 15:08:28 +0100 Subject: [PATCH] Reset field_count for OK packet --- NEWS | 3 ++ .../mysqli_stmt_next_result_field_count.phpt | 34 ++++++++++++++++ ext/mysqlnd/mysqlnd_ps.c | 1 + ...pdo_mysql_stmt_nextrowset_columncount.phpt | 39 +++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 ext/mysqli/tests/mysqli_stmt_next_result_field_count.phpt create mode 100644 ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset_columncount.phpt diff --git a/NEWS b/NEWS index f61ceb97aa00..4a174e76efa8 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,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 000000000000..4b04151d054d --- /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-- + +--FILE-- +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-- +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 28527c66ccff..681c8f2a4603 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 000000000000..94d739dc7c59 --- /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-- + +--FILE-- +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-- +exec('DROP PROCEDURE IF EXISTS pdo_mysql_stmt_nextrowset_columncount_p'); +?> +--EXPECT-- +int(1) +int(2) +int(0) +int(1) +int(2) +int(0)