Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mysqlnd: support ER_CLIENT_INTERACTION_TIMEOUT #13618

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ PHP NEWS
. Fixed bug GH-15432 (Heap corruption when querying a vector). (cmb,
Kamil Tekiela)

- PDO_MYSQL:
. mysqlnd: support ER_CLIENT_INTERACTION_TIMEOUT. (Appla)

- Session:
. Emit warnings for non-positive values of session.gc_divisor and negative values
of session.gc_probability. (Jorg Sowa)
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ PHP 8.4 UPGRADE NOTES
has changed. Consult https://github.com/PCRE2Project/pcre2/blob/master/NEWS
for a full changelog.

- MySQLnd
. The error code reported for MySQL server wait timeouts has been changed from 2006
to 4031 for MySQL server versions 8.0.24 and above.

- PCNTL:
. The functions pcntl_sigprocmask(), pcntl_sigwaitinfo() and
pcntl_sigtimedwait() now throw:
Expand Down
34 changes: 34 additions & 0 deletions ext/mysqli/tests/bug81335.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
Bug #81335: Packets out of order after connection timeout
--EXTENSIONS--
mysqli
--SKIPIF--
<?php
if (PHP_OS === 'WINNT') die('skip on windows');
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");

require_once 'connect.inc';
if (!$link = @my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) die("skip cannot connect");
if (mysqli_get_server_version($link) < 80024 || str_contains(mysqli_get_server_info($link), 'MariaDB')) {
die("skip: Due to many MySQL Server differences, the test requires >= 8.0.24");
}
?>
--FILE--
<?php

require_once 'connect.inc';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new my_mysqli($host, $user, $passwd, $db, $port, $socket);
$mysqli->query('SET WAIT_TIMEOUT=1');
usleep(1000000 * 1.1);
try {
$mysqli->query('SELECT 1 + 1');
} catch(mysqli_sql_exception $e) {
echo $e->getMessage();
echo "\n";
echo $e->getCode();
}
?>
--EXPECTF--
The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.
4031
1 change: 1 addition & 0 deletions ext/mysqlnd/mysqlnd_enum_n_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
#define CR_INVALID_PARAMETER_NO 2034
#define CR_INVALID_BUFFER_USE 2035
#define CR_LOAD_DATA_LOCAL_INFILE_REJECTED 2068
#define CR_CLIENT_INTERACTION_TIMEOUT 4031

#define MYSQLND_EE_FILENOTFOUND 7890

Expand Down
2 changes: 1 addition & 1 deletion ext/mysqlnd/mysqlnd_result.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);

if (FAIL == (ret = PACKET_READ(conn, &rset_header))) {
if (conn->error_info->error_no != CR_SERVER_GONE_ERROR) {
if (conn->error_info->error_no != CR_SERVER_GONE_ERROR && conn->error_info->error_no != CR_CLIENT_INTERACTION_TIMEOUT) {
php_error_docref(NULL, E_WARNING, "Error reading result set's header");
}
break;
Expand Down
17 changes: 16 additions & 1 deletion ext/mysqlnd/mysqlnd_wireprotocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,19 @@ mysqlnd_read_header(MYSQLND_PFC * pfc, MYSQLND_VIO * vio, MYSQLND_PACKET_HEADER
pfc->data->packet_no++;
DBG_RETURN(PASS);
}
// @see https://dev.mysql.com/worklog/task/?id=12999
if (header->size > 0) {
zend_uchar *buf = mnd_emalloc(header->size);
if ((PASS == pfc->data->m.receive(pfc, vio, buf, header->size, conn_stats, error_info)) && buf[0] == ERROR_MARKER) {
php_mysqlnd_read_error_from_line(buf + 1, header->size - 1,
error_info->error, sizeof(error_info->error),
&error_info->error_no, error_info->sqlstate
);
mnd_efree(buf);
DBG_RETURN(FAIL);
}
mnd_efree(buf);
}

DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size=%zu",
pfc->data->packet_no, header->packet_no, header->size);
Expand Down Expand Up @@ -294,7 +307,9 @@ mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,
DBG_INF_FMT("buf=%p size=%zu", buf, buf_size);
if (FAIL == mysqlnd_read_header(pfc, vio, packet_header, stats, error_info)) {
SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
if (error_info->error_no == 0) {
SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
}
Comment on lines +310 to +312
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the same reason, I don't think this check should be done either, but is there any reason why error_no has to be 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider 0 to mean that no error occurred, but this cannot be guaranteed. so may be compare with CLIENT_INTERACTION_TIMEOUT here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried various things.
There may already be a meaningful error set here, so checking if it's 0 makes sense to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

DBG_ERR_FMT("Can't read %s's header", packet_type_as_text);
DBG_RETURN(FAIL);
}
Expand Down
Loading