diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index 3c2ada851..435c04d5d 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -90,7 +90,7 @@ class MySQL_Session * But since it was change for handling 'USE' statements which are preceded by * comments, it's called after 'QueryProcessor' has processed the query. */ - void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt, const char* query_digest); + void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_PING(PtrSize_t *); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_CHANGE_USER(PtrSize_t *, bool *); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 7ffc2dc27..f269a5b30 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -3538,8 +3538,18 @@ __get_pkts_from_client: // For more context check issue: #3493. // =================================================== if (session_type != PROXYSQL_SESSION_CLICKHOUSE) { - if (strncasecmp((char *)"USE ",CurrentQuery.get_digest_text(),4)==0) { - handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(&pkt, CurrentQuery.get_digest_text()); + fprintf(stderr,"%s\n", CurrentQuery.get_digest_text()); + const char *qd = CurrentQuery.get_digest_text(); + if ( + (strncasecmp((char *)"USE",qd,3)==0) + && + ( + (strncasecmp((char *)"USE ",qd,4)==0) + || + (strncasecmp((char *)"USE`",qd,4)==0) + ) + ) { + handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(&pkt); if (mirror == false) { break; @@ -5186,18 +5196,25 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C // this function was introduced due to isseu #718 // some application (like the one written in Perl) do not use COM_INIT_DB , but COM_QUERY with USE dbname -void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt, const char* query_digest) { +void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(PtrSize_t *pkt) { gtid_hid=-1; proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUERY with USE dbname\n"); if (session_type == PROXYSQL_SESSION_MYSQL) { __sync_fetch_and_add(&MyHGM->status.frontend_use_db, 1); - char *schemaname=strndup(query_digest+4,strlen(query_digest)-4); + string nq=string((char *)pkt->ptr+sizeof(mysql_hdr)+1,pkt->size-sizeof(mysql_hdr)-1); + RE2::GlobalReplace(&nq,(char *)"(?U)/\\*.*\\*/",(char *)" "); + char *sn_tmp = (char *)nq.c_str(); + while (sn_tmp < ( nq.c_str() + nq.length() - 4 ) && *sn_tmp == ' ') + sn_tmp++; + //char *schemaname=strdup(nq.c_str()+4); + char *schemaname=strdup(sn_tmp+3); char *schemanameptr=trim_spaces_and_quotes_in_place(schemaname); // handle cases like "USE `schemaname` if(schemanameptr[0]=='`' && schemanameptr[strlen(schemanameptr)-1]=='`') { schemanameptr[strlen(schemanameptr)-1]='\0'; schemanameptr++; } + proxy_info("%s\n", schemanameptr); client_myds->myconn->userinfo->set_schemaname(schemanameptr,strlen(schemanameptr)); free(schemaname); if (mirror==false) { diff --git a/lib/c_tokenizer.c b/lib/c_tokenizer.c index 4f6085907..2177b4674 100644 --- a/lib/c_tokenizer.c +++ b/lib/c_tokenizer.c @@ -484,6 +484,11 @@ char *mysql_query_digest_and_first_comment(char *s, int _len, char **first_comme } cmd=0; } + if (flag == 1 && prev_char == '*' && *s == '/') { + if (r != p_r && *p_r != ' ') { // not at the beginning, and previous char is not ' ' + *p_r++ = ' '; + } + } prev_char = ' '; flag = 0; s++; diff --git a/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp b/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp index 5c412387e..79c89f0e8 100644 --- a/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp +++ b/test/tap/tests/reg_test_3493-USE_with_comment-t.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -37,6 +38,8 @@ void parse_result_json_column(MYSQL_RES *result, json& j) { } } +std::vector> db_query; + int main(int argc, char** argv) { CommandLine cl; @@ -47,6 +50,24 @@ int main(int argc, char** argv) { MYSQL* proxysql_mysql = mysql_init(NULL); + db_query.push_back(std::make_pair("reg_test_3493_use_comment", "/*+ placeholder_comment */ USE reg_test_3493_use_comment")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-a1`", "USE /*+ placeholder_comment */ `reg_test_3493_use_comment-a1`")); + db_query.push_back(std::make_pair("reg_test_3493_use_comment_1", " USE /*+ placeholder_comment */ `reg_test_3493_use_comment_1`")); + db_query.push_back(std::make_pair("reg_test_3493_use_comment_2", "USE/*+ placeholder_comment */ `reg_test_3493_use_comment_2`")); + db_query.push_back(std::make_pair("reg_test_3493_use_comment_3", "USE /*+ placeholder_comment */`reg_test_3493_use_comment_3`")); + db_query.push_back(std::make_pair("reg_test_3493_use_comment_4", " USE /*+ placeholder_comment */ reg_test_3493_use_comment_4")); + db_query.push_back(std::make_pair("reg_test_3493_use_comment_5", "USE/*+ placeholder_comment */ reg_test_3493_use_comment_5")); + db_query.push_back(std::make_pair("reg_test_3493_use_comment_6", "USE /*+ placeholder_comment */reg_test_3493_use_comment_6")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-1`", " USE /*+ placeholder_comment */ `reg_test_3493_use_comment-1`")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-2`", "USE/*+ placeholder_comment */ `reg_test_3493_use_comment-2`")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-3`", "USE /*+ placeholder_comment */`reg_test_3493_use_comment-3`")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-4`", "/*+ placeholder_comment */USE `reg_test_3493_use_comment-4`")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-5`", "USE/*+ placeholder_comment */`reg_test_3493_use_comment-5`")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-6`", "/* comment */USE`reg_test_3493_use_comment-6`")); + db_query.push_back(std::make_pair("`reg_test_3493_use_comment-7`", "USE`reg_test_3493_use_comment-7`")); + + plan(db_query.size()); + if ( !mysql_real_connect( proxysql_mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0 @@ -60,46 +81,81 @@ int main(int argc, char** argv) { } // Prepare the DB for the test - MYSQL_QUERY(proxysql_mysql, "DROP DATABASE IF EXISTS reg_test_3493_use_comment"); - MYSQL_QUERY(proxysql_mysql, "CREATE DATABASE reg_test_3493_use_comment"); - - int err = mysql_query(proxysql_mysql, "/*+ placeholder_comment */ USE reg_test_3493_use_comment"); - if (err) { - diag( - "'USE' command failed with error code '%d' and error '%s'", - err, mysql_error(proxysql_mysql) - ); - return EXIT_FAILURE; - } - - // Perform the 'SELECT DATABASE()' query in a new backend connection, to - // verify that ProxySQL is properly tracking the previously performed 'USE' - // statement. - MYSQL_QUERY(proxysql_mysql, "/*+ ;create_new_connection=1 */ SELECT DATABASE()"); - MYSQL_RES* result = mysql_store_result(proxysql_mysql); - if (result == nullptr) { - diag("Invalid 'MYSQL_RES' returned from 'SELECT DATABASE()'"); - return EXIT_FAILURE; + for (std::vector>::iterator it = db_query.begin(); it != db_query.end() ; it++) { + // MYSQL_QUERY(proxysql_mysql, "DROP DATABASE IF EXISTS reg_test_3493_use_comment"); + // MYSQL_QUERY(proxysql_mysql, "CREATE DATABASE reg_test_3493_use_comment"); + std::string s = ""; + s = "DROP DATABASE IF EXISTS " + it->first; + MYSQL_QUERY(proxysql_mysql, s.c_str()); + s = "CREATE DATABASE " + it->first; + MYSQL_QUERY(proxysql_mysql, s.c_str()); } - MYSQL_ROW row = mysql_fetch_row(result); - if (row == nullptr) { - diag("Invalid 'MYSQL_ROW' returned from 'SELECT DATABASE()'"); - return EXIT_FAILURE; + for (std::vector>::iterator it = db_query.begin(); it != db_query.end() ; it++) { + int i = 0; + int err = mysql_query(proxysql_mysql, it->second.c_str()); + if (err) { + diag( + "'USE' command failed with error code '%d' and error '%s' for query: %s", + err, mysql_error(proxysql_mysql), it->second.c_str() + ); + return EXIT_FAILURE; + } + + // Perform the 'SELECT DATABASE()' query in a new backend connection, to + // verify that ProxySQL is properly tracking the previously performed 'USE' + // statement. + switch (i%5) { + case 0: + MYSQL_QUERY(proxysql_mysql, "/*+ ;create_new_connection=1 */ SELECT DATABASE()"); + break; + case 1: + MYSQL_QUERY(proxysql_mysql, "/*+ ;create_new_connection=1 */SELECT DATABASE()"); + break; + case 2: + MYSQL_QUERY(proxysql_mysql, "SELECT /*+ ;create_new_connection=1 */ DATABASE()"); + break; + case 3: + MYSQL_QUERY(proxysql_mysql, "SELECT/*+ ;create_new_connection=1 */ DATABASE()"); + break; + case 4: + MYSQL_QUERY(proxysql_mysql, "SELECT /*+ ;create_new_connection=1 */DATABASE()"); + break; + default: + assert(0); + } + i++; + MYSQL_RES* result = mysql_store_result(proxysql_mysql); + if (result == nullptr) { + diag("Invalid 'MYSQL_RES' returned from 'SELECT DATABASE()'"); + return EXIT_FAILURE; + } + + MYSQL_ROW row = mysql_fetch_row(result); + if (row == nullptr) { + diag("Invalid 'MYSQL_ROW' returned from 'SELECT DATABASE()'"); + return EXIT_FAILURE; + } + + std::string database_name { row[0] }; + + if (it->first[0] == '`') { + database_name = "`" + database_name + "`"; + } + ok( + database_name == it->first, + "Selected DB name should be equal to actual DB name: (Exp: '%s') == (Act: '%s')", + it->first.c_str(), + database_name.c_str() + ); } - std::string database_name { row[0] }; - - ok( - database_name == "reg_test_3493_use_comment", - "Selected DB name should be equal to actual DB name: (Exp: '%s') == (Act: '%s')", - "reg_test_3493_use_comment", - database_name.c_str() - ); - // Drop created database - MYSQL_QUERY(proxysql_mysql, "DROP DATABASE IF EXISTS reg_test_3493_use_comment"); - + for (std::vector>::iterator it = db_query.begin(); it != db_query.end() ; it++) { + std::string s = ""; + s = "DROP DATABASE IF EXISTS " + it->first; + MYSQL_QUERY(proxysql_mysql, s.c_str()); + } mysql_close(proxysql_mysql); return exit_status();