Skip to content

Commit 478ef63

Browse files
authored
Merge pull request #3598 from sysown/v2.2.1-3597
Closes #3597: Backport bugfixes to v2.2.1 - 2
2 parents d7252af + 4138c4d commit 478ef63

File tree

9 files changed

+122
-13
lines changed

9 files changed

+122
-13
lines changed

include/MySQL_Protocol.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ class MySQL_Prepared_Stmt_info {
6464

6565
uint8_t mysql_decode_length(unsigned char *ptr, uint64_t *len);
6666

67+
/**
68+
* @brief ProxySQL replacement function for 'mysql_stmt_close'. Closes a
69+
* MYSQL_STMT avoiding any blocking commands that are sent by default
70+
* 'mysql_stmt_close'.
71+
*
72+
* NOTE: This function is not safe, caller must check that the supplied
73+
* argument is not NULL.
74+
*
75+
* @param mysql_stmt An already initialized 'MYSQL_STMT'. Caller must ensure
76+
* that the supplied argument is not NULL.
77+
*
78+
* @return The result of calling 'mysql_stmt_close' function over the internally
79+
* modified 'MYSQL_STMT'.
80+
*/
81+
my_bool proxy_mysql_stmt_close(MYSQL_STMT* mysql_stmt);
82+
6783
class MySQL_Protocol {
6884
private:
6985
MySQL_Connection_userinfo *userinfo;

include/gen_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ inline unsigned long long realtime_time() {
229229
clock_gettime(CLOCK_REALTIME, &ts);
230230
return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
231231
}
232+
232233
#endif /* __GEN_FUNCTIONS */
233234

234235
bool Proxy_file_exists(const char *);

lib/MySQL_Monitor.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ MySQL_Monitor_State_Data::MySQL_Monitor_State_Data(char *h, int p, struct event_
431431
use_ssl=_use_ssl;
432432
ST=0;
433433
hostgroup_id=g;
434+
interr=0;
434435
};
435436

436437
MySQL_Monitor_State_Data::~MySQL_Monitor_State_Data() {
@@ -2138,7 +2139,7 @@ void * monitor_replication_lag_thread(void *arg) {
21382139
int l = strlen(percona_heartbeat_table);
21392140
if (l) {
21402141
use_percona_heartbeat = true;
2141-
char *base_query = (char *)"SELECT MIN(ROUND(TIMESTAMPDIFF(MICROSECOND, ts, SYSDATE(6))/1000000)) AS Seconds_Behind_Master FROM %s";
2142+
char *base_query = (char *)"SELECT MAX(ROUND(TIMESTAMPDIFF(MICROSECOND, ts, SYSDATE(6))/1000000)) AS Seconds_Behind_Master FROM %s";
21422143
char *replication_query = (char *)malloc(strlen(base_query)+l);
21432144
sprintf(replication_query,base_query,percona_heartbeat_table);
21442145
mmsd->async_exit_status=mysql_query_start(&mmsd->interr,mmsd->mysql,replication_query);

lib/MySQL_PreparedStatement.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "SpookyV2.h"
55

66
#include "MySQL_PreparedStatement.h"
7+
#include "MySQL_Protocol.h"
78

89
//extern MySQL_STMT_Manager *GloMyStmt;
910
//static uint32_t add_prepared_statement_calls = 0;
@@ -691,12 +692,7 @@ MySQL_STMTs_local_v14::~MySQL_STMTs_local_v14() {
691692
it != global_stmt_to_backend_stmt.end(); ++it) {
692693
uint64_t global_stmt_id = it->first;
693694
MYSQL_STMT *stmt = it->second;
694-
if (stmt->mysql) {
695-
stmt->mysql->stmts =
696-
list_delete(stmt->mysql->stmts, &stmt->list);
697-
}
698-
stmt->mysql = NULL;
699-
mysql_stmt_close(stmt);
695+
proxy_mysql_stmt_close(stmt);
700696
GloMyStmt->ref_count_server(global_stmt_id, -1);
701697
}
702698
}

lib/MySQL_Protocol.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,7 +2033,7 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned
20332033
(*myds)->sess->session_fast_forward=fast_forward;
20342034
(*myds)->sess->user_max_connections=max_connections;
20352035
}
2036-
if (password==NULL) {
2036+
if (password == NULL) {
20372037
// this is a workaround for bug #603
20382038
if (
20392039
((*myds)->sess->session_type == PROXYSQL_SESSION_ADMIN)
@@ -2098,7 +2098,7 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned
20982098
(*myds)->sess->transaction_persistent=transaction_persistent;
20992099
(*myds)->sess->session_fast_forward=fast_forward;
21002100
(*myds)->sess->user_max_connections=max_connections;
2101-
if (strncmp(password,(char *)pass,strlen(password))==0) {
2101+
if (strcmp(password, (char *) pass) == 0) {
21022102
if (backend_username) {
21032103
free(password);
21042104
password=NULL;
@@ -2157,8 +2157,8 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned
21572157
ret=true;
21582158
}
21592159
} else { // mysql_clear_password
2160-
if (strncmp(password,(char *)pass,strlen(password))==0) {
2161-
ret=true;
2160+
if (strcmp(password, (char *) pass) == 0) {
2161+
ret = true;
21622162
}
21632163
}
21642164
} else {
@@ -3072,3 +3072,15 @@ unsigned long long MySQL_ResultSet::current_size() {
30723072
}
30733073
return intsize;
30743074
}
3075+
3076+
my_bool proxy_mysql_stmt_close(MYSQL_STMT* stmt) {
3077+
// Clean internal structures for 'stmt->mysql->stmts'.
3078+
if (stmt->mysql) {
3079+
stmt->mysql->stmts =
3080+
list_delete(stmt->mysql->stmts, &stmt->list);
3081+
}
3082+
// Nullify 'mysql' field to avoid sending a blocking command to the server.
3083+
stmt->mysql = NULL;
3084+
// Perform the regular close operation.
3085+
return mysql_stmt_close(stmt);
3086+
}

lib/MySQL_Thread.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2051,7 +2051,7 @@ char ** MySQL_Threads_Handler::get_variables_list() {
20512051
// query processor and query digest
20522052
VariablesPointers_int["auto_increment_delay_multiplex"] = make_tuple(&variables.auto_increment_delay_multiplex, 0, 1000000, false);
20532053
VariablesPointers_int["default_query_delay"] = make_tuple(&variables.default_query_delay, 0, 3600*1000, false);
2054-
VariablesPointers_int["default_query_timeout"] = make_tuple(&variables.default_query_timeout, 0, 1000*1000, false);
2054+
VariablesPointers_int["default_query_timeout"] = make_tuple(&variables.default_query_timeout, 1000,20*24*3600*1000, false);
20552055
VariablesPointers_int["query_digests_grouping_limit"] = make_tuple(&variables.query_digests_grouping_limit, 1, 2089, false);
20562056
VariablesPointers_int["query_digests_max_digest_length"] = make_tuple(&variables.query_digests_max_digest_length, 16, 1*1024*1024, false);
20572057
VariablesPointers_int["query_digests_max_query_length"] = make_tuple(&variables.query_digests_max_query_length, 16, 1*1024*1024, false);

lib/gen_utils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,4 @@ bool Proxy_file_regular(const char *path) {
219219
if (sb.st_mode & S_IFREG) return true;
220220
return false;
221221
}
222+

lib/mysql_connection.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ void MySQL_Connection::connect_start() {
654654
} else {
655655
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "proxysql_sha1", "unknown");
656656
}
657-
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "mysql_bug_102266", "ProxySQL is sending a lot of data to MySQL server using CLIENT_CONNECT_ATTRS in order to not hit MySQL bug https://bugs.mysql.com/bug.php?id=102266 . See also https://github.com/sysown/proxysql/issues/3276");
657+
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "mysql_bug_102266", "Avoid MySQL bug https://bugs.mysql.com/bug.php?id=102266 , https://github.com/sysown/proxysql/issues/3276");
658658
}
659659
if (parent->use_ssl) {
660660
mysql_ssl_set(mysql, mysql_thread___ssl_p2s_key, mysql_thread___ssl_p2s_cert, mysql_thread___ssl_p2s_ca, NULL, mysql_thread___ssl_p2s_cipher);
@@ -2144,6 +2144,15 @@ void MySQL_Connection::async_free_result() {
21442144
mysql_stmt_free_result(query.stmt);
21452145
}
21462146
}
2147+
// If we reached here from 'ASYNC_STMT_PREPARE_FAILED', the
2148+
// prepared statement was never added to 'local_stmts', thus
2149+
// it will never be freed when 'local_stmts' are purged. If
2150+
// initialized, it must be freed. For more context see #3525.
2151+
if (this->async_state_machine == ASYNC_STMT_PREPARE_FAILED) {
2152+
if (query.stmt != NULL) {
2153+
proxy_mysql_stmt_close(query.stmt);
2154+
}
2155+
}
21472156
query.stmt=NULL;
21482157
}
21492158
if (mysql_result) {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @file repro_test_leak_3525.cpp
3+
* @brief Test to reproduce issue #3525.
4+
* @details This test is not meant to be executed, it's just a left as DOC of how to
5+
* reproduce issue #3525.
6+
* @date 2021-07-30
7+
*/
8+
9+
#include <string>
10+
#include <mysql.h>
11+
12+
#include "tap.h"
13+
#include "command_line.h"
14+
#include "utils.h"
15+
16+
int main(int argc, char** argv) {
17+
CommandLine cl;
18+
19+
if(cl.getEnv())
20+
return exit_status();
21+
22+
plan(1);
23+
24+
MYSQL* mysql = mysql_init(NULL);
25+
if (mysql == NULL) {
26+
return exit_status();
27+
}
28+
29+
if (
30+
!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)
31+
) {
32+
fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(mysql));
33+
return exit_status();
34+
}
35+
36+
MYSQL_STMT *stmt = mysql_stmt_init(mysql);
37+
if (stmt == NULL) {
38+
ok(false, " mysql_stmt_init(), out of memory\n");
39+
return exit_status();
40+
}
41+
42+
// Invalid statement
43+
bool failed = false;
44+
std::string my_errmsg {};
45+
int my_errno = 0;
46+
47+
std::string query { "BEGIN" };
48+
if (mysql_stmt_prepare(stmt, query.c_str(), query.size())) {
49+
my_errno = mysql_errno(mysql);
50+
my_errmsg = mysql_error(mysql);
51+
52+
failed = true;
53+
} else {
54+
my_errmsg = "NOT A FAILURE!";
55+
}
56+
57+
ok(
58+
failed, "'mysql_stmt_prepare' should fail: ('err_code': %d, 'err_msg': '%s')\n",
59+
my_errno, my_errmsg.c_str()
60+
);
61+
62+
if (mysql_stmt_close(stmt)) {
63+
ok(false, "mysql_stmt_close at line %d failed: %s\n", __LINE__ , mysql_error(mysql));
64+
return exit_status();
65+
}
66+
67+
mysql_stmt_close(stmt);
68+
mysql_close(mysql);
69+
mysql_library_end();
70+
71+
mysql_close(mysql);
72+
}
73+

0 commit comments

Comments
 (0)