diff --git a/include/set_parser.h b/include/set_parser.h index bd89bbab22..73ff9e085f 100644 --- a/include/set_parser.h +++ b/include/set_parser.h @@ -42,7 +42,7 @@ class SetParser { // First implemenation of the parser for TRANSACTION ISOLATION LEVEL and TRANSACTION READ/WRITE std::map> parse2(); std::string parse_character_set(); - std::string parse_USE_query(); + std::string parse_USE_query(std::string& errmsg); std::string remove_comments(const std::string& q); #ifdef DEBUG // built-in testing diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 42930c9b92..e4ba12f440 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -6139,7 +6139,8 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C __sync_fetch_and_add(&MyHGM->status.frontend_use_db, 1); string nq=string((char *)pkt->ptr+sizeof(mysql_hdr)+1,pkt->size-sizeof(mysql_hdr)-1); SetParser parser(nq); - string schemaname = parser.parse_USE_query(); + string errmsg = ""; + string schemaname = parser.parse_USE_query(errmsg); if (schemaname != "") { client_myds->myconn->userinfo->set_schemaname((char *)schemaname.c_str(),schemaname.length()); if (mirror==false) { @@ -6156,6 +6157,9 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C l_free(pkt->size,pkt->ptr); client_myds->setDSS_STATE_QUERY_SENT_NET(); std::string msg = "Unable to parse: " + nq; + if (errmsg != "") { + msg = errmsg + ": " + nq; + } client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1,1148,(char *)"42000", msg.c_str()); RequestEnd(NULL); } @@ -6475,7 +6479,10 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C RE2::GlobalReplace(&nq,(char *)"^/\\*!\\d\\d\\d\\d\\d SET(.*)\\*/",(char *)"SET\\1"); RE2::GlobalReplace(&nq,(char *)"(?U)/\\*.*\\*/",(char *)""); // remove trailing space and semicolon if present. See issue#4380 - nq.erase(nq.find_last_not_of(" ;") + 1); + size_t pos = nq.find_last_not_of(" ;"); + if (pos != nq.npos) { + nq.erase(pos + 1); // remove trailing spaces and semicolumns + } /* // we do not threat SET SQL_LOG_BIN as a special case if (match_regexes && match_regexes[0]->match(dig)) { diff --git a/lib/set_parser.cpp b/lib/set_parser.cpp index 7abef9adf1..2d28da923a 100644 --- a/lib/set_parser.cpp +++ b/lib/set_parser.cpp @@ -11,6 +11,7 @@ using namespace std; +#define MULTI_STATEMENTS_USE "Unable to parse multi-statements command with USE statement" static void remove_quotes(string& v) { if (v.length() > 2) { @@ -118,7 +119,10 @@ VALGRIND_ENABLE_ERROR_REPORTING; } else if (strcasecmp("transaction_read_only", value4.c_str()) == 0) { value4 = "tx_read_only"; } - value5.erase(value5.find_last_not_of(" \n\r\t,")+1); + size_t pos = value5.find_last_not_of(" \n\r\t,"); + if (pos != value5.npos) { + value5.erase(pos+1); + } key = value4; if (value5 == "''" || value5 == "\"\"") { op.push_back(""); @@ -404,7 +408,10 @@ VALGRIND_ENABLE_ERROR_REPORTING; } else if (strcasecmp("transaction_read_only", value4.c_str()) == 0) { value4 = "tx_read_only"; } - value5.erase(value5.find_last_not_of(" \n\r\t,")+1); + size_t pos = value5.find_last_not_of(" \n\r\t,"); + if (pos != value5.npos) { + value5.erase(pos+1); + } key = value4; if (value5 == "''" || value5 == "\"\"") { op.push_back(""); @@ -508,7 +515,7 @@ std::string SetParser::parse_character_set() { return value4; } -std::string SetParser::parse_USE_query() { +std::string SetParser::parse_USE_query(std::string& errmsg) { #ifdef DEBUG proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Parsing query %s\n", query.c_str()); #endif // DEBUG @@ -518,6 +525,10 @@ std::string SetParser::parse_USE_query() { opt2.set_longest_match(false); std::string dbname = remove_comments(query); + size_t pos = dbname.find_last_not_of(" ;"); + if (pos != dbname.npos) { + dbname.erase(pos + 1); // remove trailing spaces and semicolumns + } re2::RE2 re0("^\\s*", opt2); re2::RE2::Replace(&dbname, re0, ""); if (dbname.size() >= 4) { @@ -537,18 +548,22 @@ std::string SetParser::parse_USE_query() { dbname.erase(0, 1); // Remove the last character dbname.erase(dbname.length() - 1); - return dbname; } } - } else { - return dbname; } + } else { + dbname = ""; } } else { - return ""; + dbname = ""; } - return ""; + if (dbname.find_first_of(';') != std::string::npos) { + errmsg = MULTI_STATEMENTS_USE; + dbname = ""; + } + + return dbname; } @@ -638,18 +653,24 @@ void SetParser::test_parse_USE_query() { {"USE/*+ placeholder_comment */`test_use_comment-5`", "test_use_comment-5"}, {"/* comment */USE`test_use_comment-6`", "test_use_comment-6"}, {"USE`test_use_comment-7`", "test_use_comment-7"}, + {"USE test_use_comment-7 ;", "test_use_comment-7"}, + {"USE`test_use_comment-2` ; ", "test_use_comment-2"}, + {"USE`test_use_comment-2` ; -- comment", "test_use_comment-2"}, + {"USE test_use_comment-7 /* comment */ ; ", "test_use_comment-7"}, + {"USE /* comment */ test_use_comment-7 ; ", "test_use_comment-7"}, + {"USE dbame ; SELECT 1", ""}, }; // Run tests for each pair for (const auto& p : testCases) { set_query(p.first); - std::string dbname = parse_USE_query(); + std::string errmsg = ""; + std::string dbname = parse_USE_query(errmsg); if (dbname != p.second) { // we call parse_USE_query() again just to make it easier to create a breakpoint - std::string s = parse_USE_query(); + std::string s = parse_USE_query(errmsg); assert(s == p.second); } } - } #endif // DEBUG