diff --git a/NEWS b/NEWS index a94fef476a74c..54b3b252896e0 100644 --- a/NEWS +++ b/NEWS @@ -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) diff --git a/UPGRADING b/UPGRADING index 48740ce43418e..610dee8b9e786 100644 --- a/UPGRADING +++ b/UPGRADING @@ -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: diff --git a/ext/mysqli/tests/bug81335.phpt b/ext/mysqli/tests/bug81335.phpt new file mode 100644 index 0000000000000..26f6bcd7e2d14 --- /dev/null +++ b/ext/mysqli/tests/bug81335.phpt @@ -0,0 +1,34 @@ +--TEST-- +Bug #81335: Packets out of order after connection timeout +--EXTENSIONS-- +mysqli +--SKIPIF-- += 8.0.24"); +} +?> +--FILE-- +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 diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index 15db002aa7349..5fe81b009febb 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -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 diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 7f781d9e5cca7..ae8dc77b4cd9a 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -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; diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 3b46fb2c985be..b1f38f01b2180 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -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); @@ -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); + } DBG_ERR_FMT("Can't read %s's header", packet_type_as_text); DBG_RETURN(FAIL); }