Skip to content

Commit 8966f72

Browse files
committed
feat(mysqlnd): send COM_RESET_CONNECTION in restart_psession
Before this commit, persistent MySQL connection reuse relied on COM_CHANGE_USER to reset server-side session state in mysqli. PDO MySQL did not reset server-side state at all; it only pinged to check liveness, leaving user variables, temporary tables, and transaction state from previous requests intact. After this commit, both mysqli and PDO MySQL send COM_RESET_CONNECTION when reusing a persistent connection, which resets all server-side session state without re-authentication. We've implemented this by adding a reset_connection command to mysqlnd's command layer. It's similar to the existing ping command, since there's no payload. We now call it from restart_psession, which we've also changed to return enum_func_status so callers can handle failures. In mysqli, we've replaced the COM_CHANGE_USER call in the persistent reuse path with a call to restart_psession. In PDO MySQL, we've replaced the mysql_ping in check_liveness with a call to restart_psession, which both verifies the connection is alive and resets session state in a single round trip. We've also removed a call to restart_psession on unconnected handles during initialization.
1 parent a935460 commit 8966f72

7 files changed

Lines changed: 109 additions & 19 deletions

File tree

ext/mysqli/mysqli_mysqlnd.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,5 @@
3737
#define mysqli_stmt_close(c, implicit) mysqlnd_stmt_close((c), (implicit))
3838
#define mysqli_free_result(r, implicit) mysqlnd_free_result((r), (implicit))
3939
#define mysqli_async_query(c, q, l) mysqlnd_async_query((c), (q), (l))
40-
#define mysqli_change_user_silent(c, u, p, d, p_len) mysqlnd_change_user_ex((c), (u), (p), (d), true, (size_t)(p_len))
4140

4241
#endif

ext/mysqli/mysqli_nonapi.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,8 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, bool is_real_connect, b
173173
mysql->mysql = zend_ptr_stack_pop(&plist->free_links);
174174

175175
MyG(num_inactive_persistent)--;
176-
/* reset variables */
177176

178-
#ifndef MYSQLI_NO_CHANGE_USER_ON_PCONNECT
179-
if (!mysqli_change_user_silent(mysql->mysql, username, passwd, dbname, passwd_len)) {
180-
#else
181-
if (!mysql_ping(mysql->mysql)) {
182-
#endif
183-
mysqlnd_restart_psession(mysql->mysql);
177+
if (!mysqlnd_restart_psession(mysql->mysql)) {
184178
MyG(num_active_persistent)++;
185179

186180
/* clear error */
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
mysqli_pconnect() - COM_RESET_CONNECTION clears session state
3+
--EXTENSIONS--
4+
mysqli
5+
--SKIPIF--
6+
<?php
7+
require_once 'skipifconnectfailure.inc';
8+
?>
9+
--FILE--
10+
<?php
11+
require_once 'connect.inc';
12+
13+
$host = 'p:' . $host;
14+
15+
$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
16+
if (!$link) {
17+
printf("[001] Cannot connect\n");
18+
}
19+
20+
mysqli_query($link, "SET @test_var = 42");
21+
mysqli_query($link, "CREATE TEMPORARY TABLE test_reset_tmp (id INT)");
22+
mysqli_query($link, "INSERT INTO test_reset_tmp VALUES (1)");
23+
24+
$res = mysqli_query($link, "SELECT @test_var AS v");
25+
$row = mysqli_fetch_assoc($res);
26+
if ($row['v'] !== '42') {
27+
printf("[002] Expected 42, got %s\n", $row['v']);
28+
}
29+
mysqli_free_result($res);
30+
31+
$thread_id = mysqli_thread_id($link);
32+
mysqli_close($link);
33+
34+
$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);
35+
if (!$link) {
36+
printf("[003] Cannot reconnect\n");
37+
}
38+
39+
if (mysqli_thread_id($link) !== $thread_id) {
40+
printf("[004] Got a different connection, persistent reuse did not happen\n");
41+
}
42+
43+
$res = mysqli_query($link, "SELECT @test_var AS v");
44+
$row = mysqli_fetch_assoc($res);
45+
if ($row['v'] !== null) {
46+
printf("[005] Expected NULL after reset, got %s\n", $row['v']);
47+
}
48+
mysqli_free_result($res);
49+
50+
$res = mysqli_query($link, "SHOW TABLES LIKE 'test_reset_tmp'");
51+
if (mysqli_num_rows($res) !== 0) {
52+
printf("[006] Temporary table should not exist after reset\n");
53+
}
54+
mysqli_free_result($res);
55+
56+
mysqli_close($link);
57+
58+
echo "done!";
59+
?>
60+
--EXPECT--
61+
done!

ext/mysqlnd/mysqlnd_commands.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,33 @@ MYSQLND_METHOD(mysqlnd_command, ping)(MYSQLND_CONN_DATA * const conn)
143143
/* }}} */
144144

145145

146+
/* {{{ mysqlnd_command::reset_connection */
147+
static enum_func_status
148+
MYSQLND_METHOD(mysqlnd_command, reset_connection)(MYSQLND_CONN_DATA * const conn)
149+
{
150+
const func_mysqlnd_protocol_payload_decoder_factory__send_command send_command = conn->payload_decoder_factory->m.send_command;
151+
const func_mysqlnd_protocol_payload_decoder_factory__send_command_handle_response send_command_handle_response = conn->payload_decoder_factory->m.send_command_handle_response;
152+
enum_func_status ret = FAIL;
153+
154+
DBG_ENTER("mysqlnd_command::reset_connection");
155+
156+
ret = send_command(conn->payload_decoder_factory, COM_RESET_CONNECTION, NULL, 0, FALSE,
157+
&conn->state,
158+
conn->error_info,
159+
conn->upsert_status,
160+
conn->stats,
161+
conn->m->send_close,
162+
conn);
163+
if (PASS == ret) {
164+
ret = send_command_handle_response(conn->payload_decoder_factory, PROT_OK_PACKET, FALSE, COM_RESET_CONNECTION, FALSE,
165+
conn->error_info, conn->upsert_status, &conn->last_message);
166+
}
167+
168+
DBG_RETURN(ret);
169+
}
170+
/* }}} */
171+
172+
146173
/* {{{ mysqlnd_command::statistics */
147174
static enum_func_status
148175
MYSQLND_METHOD(mysqlnd_command, statistics)(MYSQLND_CONN_DATA * const conn, zend_string ** message)
@@ -659,4 +686,5 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_command)
659686
MYSQLND_METHOD(mysqlnd_command, stmt_close),
660687
MYSQLND_METHOD(mysqlnd_command, enable_ssl),
661688
MYSQLND_METHOD(mysqlnd_command, handshake),
689+
MYSQLND_METHOD(mysqlnd_command, reset_connection),
662690
MYSQLND_CLASS_METHODS_END;

ext/mysqlnd/mysqlnd_connection.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,14 +354,19 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const c
354354

355355

356356
/* {{{ mysqlnd_conn_data::restart_psession */
357-
static void
357+
static enum_func_status
358358
MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn)
359359
{
360360
DBG_ENTER("mysqlnd_conn_data::restart_psession");
361-
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
362-
conn->current_result = NULL;
363-
conn->last_message.s = NULL;
364-
DBG_VOID_RETURN;
361+
362+
enum_func_status ret = conn->command->reset_connection(conn);
363+
if (ret == PASS) {
364+
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
365+
conn->current_result = NULL;
366+
conn->last_message.s = NULL;
367+
}
368+
369+
DBG_RETURN(ret);
365370
}
366371
/* }}} */
367372

ext/mysqlnd/mysqlnd_structs.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ typedef enum_func_status (*func_mysqlnd_execute_com_set_option)(MYSQLND_CONN_DAT
306306
typedef enum_func_status (*func_mysqlnd_execute_com_debug)(MYSQLND_CONN_DATA * const conn);
307307
typedef enum_func_status (*func_mysqlnd_execute_com_init_db)(MYSQLND_CONN_DATA * const conn, const MYSQLND_CSTRING db);
308308
typedef enum_func_status (*func_mysqlnd_execute_com_ping)(MYSQLND_CONN_DATA * const conn);
309+
typedef enum_func_status (*func_mysqlnd_execute_com_reset_connection)(MYSQLND_CONN_DATA * const conn);
309310
typedef enum_func_status (*func_mysqlnd_execute_com_statistics)(MYSQLND_CONN_DATA * const conn, zend_string ** message);
310311
typedef enum_func_status (*func_mysqlnd_execute_com_process_kill)(MYSQLND_CONN_DATA * const conn, const unsigned int process_id, const bool read_response);
311312
typedef enum_func_status (*func_mysqlnd_execute_com_refresh)(MYSQLND_CONN_DATA * const conn, const uint8_t options);
@@ -344,6 +345,7 @@ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_command)
344345
func_mysqlnd_execute_com_stmt_close stmt_close;
345346
func_mysqlnd_execute_com_enable_ssl enable_ssl;
346347
func_mysqlnd_execute_com_handshake handshake;
348+
func_mysqlnd_execute_com_reset_connection reset_connection;
347349
};
348350

349351

@@ -482,7 +484,7 @@ typedef enum_func_status (*func_mysqlnd_conn_data__free_reference)(MYSQLND_CONN_
482484
typedef enum_func_status (*func_mysqlnd_conn_data__send_command_do_request)(MYSQLND_CONN_DATA * const conn, const enum php_mysqlnd_server_command command, const zend_uchar * const arg, const size_t arg_len, const bool silent, const bool ignore_upsert_status);
483485
typedef enum_func_status (*func_mysqlnd_conn_data__send_command_handle_response)(MYSQLND_CONN_DATA * const conn, const enum mysqlnd_packet_type ok_packet, const bool silent, const enum php_mysqlnd_server_command command, const bool ignore_upsert_status);
484486

485-
typedef void (*func_mysqlnd_conn_data__restart_psession)(MYSQLND_CONN_DATA * conn);
487+
typedef enum_func_status (*func_mysqlnd_conn_data__restart_psession)(MYSQLND_CONN_DATA * conn);
486488
typedef void (*func_mysqlnd_conn_data__end_psession)(MYSQLND_CONN_DATA * conn);
487489
typedef enum_func_status (*func_mysqlnd_conn_data__send_close)(MYSQLND_CONN_DATA * conn);
488490

ext/pdo_mysql/mysql_driver.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -603,9 +603,15 @@ static zend_result pdo_mysql_check_liveness(pdo_dbh_t *dbh)
603603
PDO_DBG_ENTER("pdo_mysql_check_liveness");
604604
PDO_DBG_INF_FMT("dbh=%p", dbh);
605605

606+
#ifdef PDO_USE_MYSQLND
607+
if (mysqlnd_restart_psession(H->server)) {
608+
PDO_DBG_RETURN(FAILURE);
609+
}
610+
#else
606611
if (mysql_ping(H->server)) {
607612
PDO_DBG_RETURN(FAILURE);
608613
}
614+
#endif
609615
PDO_DBG_RETURN(SUCCESS);
610616
}
611617
/* }}} */
@@ -724,11 +730,6 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
724730
pdo_mysql_error(dbh);
725731
goto cleanup;
726732
}
727-
#ifdef PDO_USE_MYSQLND
728-
if (dbh->is_persistent) {
729-
mysqlnd_restart_psession(H->server);
730-
}
731-
#endif
732733

733734
dbh->driver_data = H;
734735

0 commit comments

Comments
 (0)