Skip to content

Commit d7252af

Browse files
authored
Merge pull request #3596 from sysown/v2.2.1-3595
Closes #3595: Backport bugfixes to v2.2.1 - 1
2 parents e14accd + 9eec243 commit d7252af

File tree

7 files changed

+158
-12
lines changed

7 files changed

+158
-12
lines changed

include/MySQL_Session.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,18 @@ class MySQL_Session
7979

8080
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_FIELD_LIST(PtrSize_t *);
8181
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_INIT_DB(PtrSize_t *);
82-
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *);
82+
/**
83+
* @brief Handles 'COM_QUERIES' holding 'USE DB' statements.
84+
*
85+
* @param pkt The packet being processed.
86+
* @param query_digest The query digest returned by the 'QueryProcessor'
87+
* holding the 'USE' statement without the initial comment.
88+
*
89+
* @details NOTE: This function used to be called from 'handler_special_queries'.
90+
* But since it was change for handling 'USE' statements which are preceded by
91+
* comments, it's called after 'QueryProcessor' has processed the query.
92+
*/
93+
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt, const char* query_digest);
8394
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_PING(PtrSize_t *);
8495

8596
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_CHANGE_USER(PtrSize_t *, bool *);

lib/MySQL_Session.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,12 +1135,6 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) {
11351135
}
11361136
}
11371137

1138-
if (session_type != PROXYSQL_SESSION_CLICKHOUSE) {
1139-
if (pkt->size>(5+4) && strncasecmp((char *)"USE ",(char *)pkt->ptr+5,4)==0) {
1140-
handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(pkt);
1141-
return true;
1142-
}
1143-
}
11441138
if (pkt->size==SELECT_VERSION_COMMENT_LEN+5 && strncmp((char *)SELECT_VERSION_COMMENT,(char *)pkt->ptr+5,pkt->size-5)==0) {
11451139
// FIXME: this doesn't return AUTOCOMMIT or IN_TRANS
11461140
PtrSize_t pkt_2;
@@ -3593,6 +3587,23 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) {
35933587
clock_gettime(CLOCK_THREAD_CPUTIME_ID,&begint);
35943588
}
35953589
qpo=GloQPro->process_mysql_query(this,pkt.ptr,pkt.size,&CurrentQuery);
3590+
// This block was moved from 'handler_special_queries' to support
3591+
// handling of 'USE' statements which are preceded by a comment.
3592+
// For more context check issue: #3493.
3593+
// ===================================================
3594+
if (session_type != PROXYSQL_SESSION_CLICKHOUSE) {
3595+
if (strncasecmp((char *)"USE ",CurrentQuery.get_digest_text(),4)==0) {
3596+
handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(&pkt, CurrentQuery.get_digest_text());
3597+
3598+
if (mirror == false) {
3599+
break;
3600+
} else {
3601+
handler_ret = -1;
3602+
return handler_ret;
3603+
}
3604+
}
3605+
}
3606+
// ===================================================
35963607
if (qpo->max_lag_ms >= 0) {
35973608
thread->status_variables.stvar[st_var_queries_with_max_lag_ms]++;
35983609
}
@@ -5219,12 +5230,12 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
52195230

52205231
// this function was introduced due to isseu #718
52215232
// some application (like the one written in Perl) do not use COM_INIT_DB , but COM_QUERY with USE dbname
5222-
void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt) {
5233+
void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt, const char* query_digest) {
52235234
gtid_hid=-1;
52245235
proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUERY with USE dbname\n");
52255236
if (session_type == PROXYSQL_SESSION_MYSQL) {
52265237
__sync_fetch_and_add(&MyHGM->status.frontend_use_db, 1);
5227-
char *schemaname=strndup((char *)pkt->ptr+sizeof(mysql_hdr)+5,pkt->size-sizeof(mysql_hdr)-5);
5238+
char *schemaname=strndup(query_digest+4,strlen(query_digest)-4);
52285239
char *schemanameptr=trim_spaces_and_quotes_in_place(schemaname);
52295240
// handle cases like "USE `schemaname`
52305241
if(schemanameptr[0]=='`' && schemanameptr[strlen(schemanameptr)-1]=='`') {
@@ -5443,7 +5454,11 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
54435454
#endif
54445455
if (index(dig,';') && (index(dig,';') != dig + strlen(dig)-1)) {
54455456
string nqn = string((char *)CurrentQuery.QueryPointer,CurrentQuery.QueryLength);
5446-
proxy_warning("Unable to parse multi-statements command with SET statement: setting lock hostgroup . Command: %s\n", nqn.c_str());
5457+
proxy_warning(
5458+
"Unable to parse multi-statements command with SET statement from client"
5459+
" %s:%d: setting lock hostgroup. Command: %s\n", client_myds->addr.addr,
5460+
client_myds->addr.port, nqn.c_str()
5461+
);
54475462
*lock_hostgroup = true;
54485463
return false;
54495464
}

lib/MySQL_Thread.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2542,7 +2542,7 @@ bool MySQL_Thread::init() {
25422542
match_regexes[0]=new Session_Regex((char *)"^SET (|SESSION |@@|@@session.)SQL_LOG_BIN( *)(:|)=( *)");
25432543

25442544
std::stringstream ss;
2545-
ss << "^SET (|SESSION |@@|@@session.)(" << mysql_variables.variables_regexp << "SESSION_TRACK_GTIDS|TX_ISOLATION( *)(:|)=( *))";
2545+
ss << "^SET (|SESSION |@@|@@session.)`?(" << mysql_variables.variables_regexp << "SESSION_TRACK_GTIDS|TX_ISOLATION)`?( *)(:|)=( *)";
25462546
match_regexes[1]=new Session_Regex((char *)ss.str().c_str());
25472547

25482548
match_regexes[2]=new Session_Regex((char *)"^SET(?: +)(|SESSION +)TRANSACTION(?: +)(?:(?:(ISOLATION(?: +)LEVEL)(?: +)(REPEATABLE(?: +)READ|READ(?: +)COMMITTED|READ(?: +)UNCOMMITTED|SERIALIZABLE))|(?:(READ)(?: +)(WRITE|ONLY)))");

lib/set_parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ std::map<std::string,std::vector<std::string>> SetParser::parse1() {
3434
std::map<std::string,std::vector<std::string>> result;
3535

3636
#define SESSION_P1 "(?:|SESSION +|@@|@@session.)"
37-
#define VAR_P1 "(@\\w+|\\w+)"
37+
#define VAR_P1 "`?(@\\w+|\\w+)`?"
3838
//#define VAR_VALUE "((?:[\\w/\\d:\\+\\-]|,)+)"
3939
//#define VAR_VALUE "((?:CONCAT\\((?:(REPLACE|CONCAT)\\()+@@sql_mode,(?:(?:'|\\w|,| |\"|\\))+(?:\\)))|(?:[@\\w/\\d:\\+\\-]|,)+|(?:)))"
4040
#define VAR_VALUE_P1 "((?:\\()*(?:SELECT)*(?: )*(?:CONCAT\\()*(?:(?:(?: )*REPLACE|IFNULL|CONCAT)\\()+(?: )*(?:NULL|@OLD_SQL_MODE|@@SQL_MODE),(?:(?:'|\\w|,| |\"|\\))+(?:\\))*)(?:\\))|(?:NULL)|(?:[@\\w/\\d:\\+\\-]|,)+|(?:(?:'{1}|\"{1})(?:)(?:'{1}|\"{1})))"
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* @file reg_test_3493-USE_with_comment-t.cpp
3+
* @brief This test verifies that a 'USE' statement is properly tracked by
4+
* ProxySQL, even when executed with a leading comment.
5+
* @details For being sure that the feature is properly supported the
6+
* test performs the following actions:
7+
*
8+
* 1. Open a MYSQL connection to ProxySQL.
9+
* 2. Drops and creates a new database called 'reg_test_3493_use_comment'.
10+
* 3. Checks the currently selected database in **a new backend database
11+
* connection** by means of the connection annotation
12+
* "create_new_connection=1". This way it's ensured that ProxySQL is
13+
* properly keeping track of the database selected in the issued 'USE'
14+
* statement.
15+
*/
16+
17+
#include <cstring>
18+
#include <vector>
19+
#include <string>
20+
#include <stdio.h>
21+
22+
#include <mysql.h>
23+
24+
#include "tap.h"
25+
#include "command_line.h"
26+
#include "utils.h"
27+
#include "json.hpp"
28+
29+
using nlohmann::json;
30+
31+
void parse_result_json_column(MYSQL_RES *result, json& j) {
32+
if(!result) return;
33+
MYSQL_ROW row;
34+
35+
while ((row = mysql_fetch_row(result))) {
36+
j = json::parse(row[0]);
37+
}
38+
}
39+
40+
int main(int argc, char** argv) {
41+
CommandLine cl;
42+
43+
if (cl.getEnv()) {
44+
diag("Failed to get the required environmental variables.");
45+
return -1;
46+
}
47+
48+
MYSQL* proxysql_mysql = mysql_init(NULL);
49+
50+
if (
51+
!mysql_real_connect(
52+
proxysql_mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0
53+
)
54+
) {
55+
fprintf(
56+
stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__,
57+
mysql_error(proxysql_mysql)
58+
);
59+
return EXIT_FAILURE;
60+
}
61+
62+
// Prepare the DB for the test
63+
MYSQL_QUERY(proxysql_mysql, "DROP DATABASE IF EXISTS reg_test_3493_use_comment");
64+
MYSQL_QUERY(proxysql_mysql, "CREATE DATABASE reg_test_3493_use_comment");
65+
66+
int err = mysql_query(proxysql_mysql, "/*+ placeholder_comment */ USE reg_test_3493_use_comment");
67+
if (err) {
68+
diag(
69+
"'USE' command failed with error code '%d' and error '%s'",
70+
err, mysql_error(proxysql_mysql)
71+
);
72+
return EXIT_FAILURE;
73+
}
74+
75+
// Perform the 'SELECT DATABASE()' query in a new backend connection, to
76+
// verify that ProxySQL is properly tracking the previously performed 'USE'
77+
// statement.
78+
MYSQL_QUERY(proxysql_mysql, "/*+ ;create_new_connection=1 */ SELECT DATABASE()");
79+
MYSQL_RES* result = mysql_store_result(proxysql_mysql);
80+
if (result == nullptr) {
81+
diag("Invalid 'MYSQL_RES' returned from 'SELECT DATABASE()'");
82+
return EXIT_FAILURE;
83+
}
84+
85+
MYSQL_ROW row = mysql_fetch_row(result);
86+
if (row == nullptr) {
87+
diag("Invalid 'MYSQL_ROW' returned from 'SELECT DATABASE()'");
88+
return EXIT_FAILURE;
89+
}
90+
91+
std::string database_name { row[0] };
92+
93+
ok(
94+
database_name == "reg_test_3493_use_comment",
95+
"Selected DB name should be equal to actual DB name: (Exp: '%s') == (Act: '%s')",
96+
"reg_test_3493_use_comment",
97+
database_name.c_str()
98+
);
99+
100+
// Drop created database
101+
MYSQL_QUERY(proxysql_mysql, "DROP DATABASE IF EXISTS reg_test_3493_use_comment");
102+
103+
mysql_close(proxysql_mysql);
104+
105+
return exit_status();
106+
}

test/tap/tests/set_testing-t.csv

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,11 @@
9292
"SET time_zone='+04:00', sql_mode='NO_ENGINE_SUBSTITUTION', max_join_size=10000; SET CHARSET 'latin1'", "{'time_zone':'+04:00', 'sql_mode':'NO_ENGINE_SUBSTITUTION', 'max_join_size':'10000', 'character_set_results':'latin1', 'character_set_client':'latin1'}", "['character_set_connection', 'collation_connection']"
9393
"SET session_track_gtids=ALL_GTIDS", "{'session_track_gtids':'ALL_GTIDS'}"
9494
"SET sql_safe_updates=1, session_track_schema=1, sql_mode = concat(@@sql_mode,',STRICT_TRANS_TABLES')", "{'sql_safe_updates':'ON', 'sql_mode':'concat(@@sql_mode,\',STRICT_TRANS_TABLES\')'}"
95+
"set `group_concat_max_len`=4096", "{'group_concat_max_len':'4096'}"
96+
"SET `sql_select_limit`=3030; ", "{'sql_select_limit':'3030'}"
97+
"SET `time_zone`='+04:00', `sql_mode`='NO_ENGINE_SUBSTITUTION', `max_join_size`=10000; SET CHARACTER SET 'latin1'", "{'time_zone':'+04:00', 'sql_mode':'NO_ENGINE_SUBSTITUTION', 'max_join_size':'10000', 'character_set_results':'latin1', 'character_set_client':'latin1'}", "['character_set_connection', 'collation_connection']"
98+
"SET `character_set_results`='binary', `sql_mode`='NO_ENGINE_SUBSTITUTION', `character_set_client`='latin1', `max_join_size`=10000", "{'character_set_results':'binary', 'sql_mode':'NO_ENGINE_SUBSTITUTION', 'character_set_client':'latin1', 'max_join_size':'10000'}"
99+
"set `tx_isolation`='READ-COMMITTED', `group_concat_max_len`=4096", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'4096'}"
100+
"set `net_write_timeout`=30", "{'net_write_timeout':'30'}"
101+
"SET `sql_auto_is_null`=ON", "{'sql_auto_is_null':'ON'}"
102+
"set `character_set_results`=null; set names latin7; set `character_set_client`='utf8mb4';", "{'character_set_results':'latin7', 'collation_connection':'latin7_general_ci', 'character_set_connection':'latin7', 'character_set_client':'utf8mb4'}"

test/tap/tests/test_filtered_set_statements-t.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ std::vector<std::pair<std::string, std::string>> filtered_set_queries {
4848
{ "net_read_timeout", "28801" },
4949
// NOTE: This variable has been temporarily ignored. Check issues #3442 and #3441.
5050
{ "session_track_schema", "1" },
51+
// Added several variables to be set using `grave accents`. See issue #3479.
52+
{ "`wait_timeout`", "28801" },
53+
{ "`character_set_results`", "latin1" },
54+
{ "`character_set_results`", "latin1" },
55+
{ "`autocommit`", "1" },
56+
{ "`max_join_size`", "18446744073709551615" },
5157
};
5258

5359
std::vector<std::string> get_valid_set_query_set(const std::string& set_query, const std::string param) {

0 commit comments

Comments
 (0)