diff --git a/.gitignore b/.gitignore index b2c1d96264..339f9ba764 100644 --- a/.gitignore +++ b/.gitignore @@ -116,6 +116,8 @@ test/tap/tap/cpp-dotenv/cpp-dotenv-* test/tap/tests/galera_1_timeout_count test/tap/tests/galera_2_timeout_no_count test/tap/tests/setparser_test +test/tap/tests/setparser_test2 +test/tap/tests/setparser_test3 test/tap/tests/reg_test_3504-change_user_libmariadb_helper test/tap/tests/reg_test_3504-change_user_libmysql_helper test/tap/tests/generate_set_session_csv diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index 0049321ecd..2b60ebb033 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -14,6 +14,8 @@ #include "prometheus_helpers.h" +#include "set_parser.h" + #define MIN_POLL_LEN 8 #define MIN_POLL_DELETE_RATIO 8 #define MY_EPOLL_THREAD_MAXEVENTS 128 @@ -214,11 +216,15 @@ class __attribute__((aligned(64))) MySQL_Thread bool query_cache_stores_empty_result; } variables; - pthread_mutex_t thread_mutex; - MySQL_Thread(); - ~MySQL_Thread(); - MySQL_Session * create_new_session_and_client_data_stream(int _fd); - bool init(); + pthread_mutex_t thread_mutex; + + // if set_parser_algorithm == 2 , a single thr_SetParser is used + SetParser *thr_SetParser; + + MySQL_Thread(); + ~MySQL_Thread(); + MySQL_Session * create_new_session_and_client_data_stream(int _fd); + bool init(); void run___get_multiple_idle_connections(int& num_idles); void run___cleanup_mirror_queue(); void ProcessAllMyDS_BeforePoll(); @@ -538,6 +544,7 @@ class MySQL_Threads_Handler int query_processor_iterations; int query_processor_regex; int set_query_lock_on_hostgroup; + int set_parser_algorithm; int reset_connection_algorithm; int auto_increment_delay_multiplex; int auto_increment_delay_multiplex_timeout_ms; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index d6c3b3f3ee..4352380c4b 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -815,6 +815,7 @@ __thread int mysql_thread___connect_timeout_server_max; __thread int mysql_thread___query_processor_iterations; __thread int mysql_thread___query_processor_regex; __thread int mysql_thread___set_query_lock_on_hostgroup; +__thread int mysql_thread___set_parser_algorithm; __thread int mysql_thread___reset_connection_algorithm; __thread uint32_t mysql_thread___server_capabilities; __thread int mysql_thread___auto_increment_delay_multiplex; @@ -981,6 +982,7 @@ extern __thread int mysql_thread___connect_timeout_server_max; extern __thread int mysql_thread___query_processor_iterations; extern __thread int mysql_thread___query_processor_regex; extern __thread int mysql_thread___set_query_lock_on_hostgroup; +extern __thread int mysql_thread___set_parser_algorithm; extern __thread int mysql_thread___reset_connection_algorithm; extern __thread uint32_t mysql_thread___server_capabilities; extern __thread int mysql_thread___auto_increment_delay_multiplex; diff --git a/include/set_parser.h b/include/set_parser.h index 5f8a8b1da1..36391d15ae 100644 --- a/include/set_parser.h +++ b/include/set_parser.h @@ -4,14 +4,44 @@ #include #include +#include "re2/re2.h" +#include "re2/regexp.h" + + class SetParser { private: + // parse1v2 variables used for compile the RE only once + bool parse1v2_init; + re2::RE2::Options * parse1v2_opt2; + re2::RE2 * parse1v2_re; + std::string parse1v2_pattern; std::string query; +#ifdef PARSERDEBUG + int verbosity; + public: + SetParser(std::string q, int verb = 0); +#else public: SetParser(std::string q); +#endif + // set_query() allows to change the query associated to a SetParser. + // This allow to parse multiple queries using just a single SetParser. + // At the moment this makes sense only when using parse1v2() because it + // allows to compile the regular expression only once + void set_query(const std::string& q); + // First implementation of the general parser + // It uses a single complex RE pattern that is hardcoded std::map> parse1(); + // Second implementation of the general parser . + // It uses a RE pattern that is built at runtime . + // The final pattern used by parse1v2() is a lot longer than the one used by parse1() + // making it very difficult to read, but the code generating it should be clear + std::map> parse1v2(); + void generateRE_parse1v2(); + // First implemenation of the parser for TRANSACTION ISOLATION LEVEL and TRANSACTION READ/WRITE std::map> parse2(); std::string parse_character_set(); + ~SetParser(); }; diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index ccb62521ae..57b8255687 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -6,7 +6,6 @@ #include "re2/re2.h" #include "re2/regexp.h" #include "mysqld_error.h" -#include "set_parser.h" #include "MySQL_Data_Stream.h" #include "query_processor.h" @@ -6022,7 +6021,15 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Parsing SET command %s\n", nq.c_str()); proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 5, "Parsing SET command = %s\n", nq.c_str()); SetParser parser(nq); - std::map> set = parser.parse1(); + std::map> set = {}; + if (mysql_thread___set_parser_algorithm == 1) { // legacy behavior + set = parser.parse1(); + } else if (mysql_thread___set_parser_algorithm == 2) { // we use a single SetParser per thread + thread->thr_SetParser->set_query(nq); // replace the query + set = thread->thr_SetParser->parse1v2(); // use algorithm v2 + } else { + assert(0); + } // Flag to be set if any variable within the 'SET' statement fails to be tracked, // due to being unknown or because it's an user defined variable. bool failed_to_parse_var = false; diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index bb8838e850..f8a20c843b 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -546,6 +546,7 @@ static char * mysql_thread_variables_names[]= { (char *)"query_processor_iterations", (char *)"query_processor_regex", (char *)"set_query_lock_on_hostgroup", + (char *)"set_parser_algorithm", (char *)"reset_connection_algorithm", (char *)"auto_increment_delay_multiplex", (char *)"auto_increment_delay_multiplex_timeout_ms", @@ -1140,6 +1141,7 @@ MySQL_Threads_Handler::MySQL_Threads_Handler() { variables.query_processor_iterations=0; variables.query_processor_regex=1; variables.set_query_lock_on_hostgroup=1; + variables.set_parser_algorithm=1; // in 2.6.0 this must become 2 variables.reset_connection_algorithm=2; variables.auto_increment_delay_multiplex=5; variables.auto_increment_delay_multiplex_timeout_ms=10000; @@ -2234,6 +2236,7 @@ char ** MySQL_Threads_Handler::get_variables_list() { VariablesPointers_int["query_processor_regex"] = make_tuple(&variables.query_processor_regex, 1, 2, false); VariablesPointers_int["query_retries_on_failure"] = make_tuple(&variables.query_retries_on_failure, 0, 1000, false); VariablesPointers_int["set_query_lock_on_hostgroup"] = make_tuple(&variables.set_query_lock_on_hostgroup, 0, 1, false); + VariablesPointers_int["set_parser_algorithm"] = make_tuple(&variables.set_parser_algorithm, 1, 2, false); // throttle VariablesPointers_int["throttle_connections_per_sec_to_hostgroup"] = make_tuple(&variables.throttle_connections_per_sec_to_hostgroup, 1, 100*1000*1000, false); @@ -2856,6 +2859,10 @@ MySQL_Thread::~MySQL_Thread() { free(match_regexes); match_regexes=NULL; } + if (thr_SetParser != NULL) { + delete thr_SetParser; + thr_SetParser = NULL; + } } @@ -2962,6 +2969,7 @@ bool MySQL_Thread::init() { mypolls.add(POLLIN, pipefd[0], NULL, 0); assert(i==0); + thr_SetParser = new SetParser(""); match_regexes=(Session_Regex **)malloc(sizeof(Session_Regex *)*4); // match_regexes[0]=new Session_Regex((char *)"^SET (|SESSION |@@|@@session.)SQL_LOG_BIN( *)(:|)=( *)"); match_regexes[0] = NULL; // NOTE: historically we used match_regexes[0] for SET SQL_LOG_BIN . Not anymore @@ -4016,6 +4024,7 @@ void MySQL_Thread::refresh_variables() { mysql_thread___query_processor_iterations=GloMTH->get_variable_int((char *)"query_processor_iterations"); mysql_thread___query_processor_regex=GloMTH->get_variable_int((char *)"query_processor_regex"); mysql_thread___set_query_lock_on_hostgroup=GloMTH->get_variable_int((char *)"set_query_lock_on_hostgroup"); + mysql_thread___set_parser_algorithm=GloMTH->get_variable_int((char *)"set_parser_algorithm"); mysql_thread___reset_connection_algorithm=GloMTH->get_variable_int((char *)"reset_connection_algorithm"); mysql_thread___auto_increment_delay_multiplex=GloMTH->get_variable_int((char *)"auto_increment_delay_multiplex"); mysql_thread___auto_increment_delay_multiplex_timeout_ms=GloMTH->get_variable_int((char *)"auto_increment_delay_multiplex_timeout_ms"); @@ -4251,6 +4260,7 @@ MySQL_Thread::MySQL_Thread() { mysql_thread___default_variables[i] = NULL; } shutdown=0; + thr_SetParser = NULL; } void MySQL_Thread::register_session_connection_handler(MySQL_Session *_sess, bool _new) { diff --git a/lib/set_parser.cpp b/lib/set_parser.cpp index 1fff9f6d20..4b4be6ec35 100644 --- a/lib/set_parser.cpp +++ b/lib/set_parser.cpp @@ -1,12 +1,46 @@ #include "set_parser.h" -#include "re2/re2.h" -#include "re2/regexp.h" #include "gen_utils.h" #include #include #include +#ifdef PARSERDEBUG +#include +#endif +using namespace std; + + +static void remove_quotes(string& v) { + if (v.length() > 2) { + char firstChar = v[0]; + char lastChar = v[v.length()-1]; + if (firstChar == lastChar) { + if (firstChar == '\'' || firstChar == '"' || firstChar == '`') { + v.erase(v.length()-1, 1); + v.erase(0, 1); + } + } + } +} + +#ifdef PARSERDEBUG +SetParser::SetParser(std::string nq, int verb) { + verbosity = verb; +#else SetParser::SetParser(std::string nq) { +#endif + parse1v2_init = false; + set_query(nq); +} + +SetParser::~SetParser() { + if (parse1v2_init == true) { + delete parse1v2_opt2; + delete parse1v2_re; + } +} + +void SetParser::set_query(const std::string& nq) { int query_no_space_length = nq.length(); char *query_no_space=(char *)malloc(query_no_space_length+1); memcpy(query_no_space,nq.c_str(),query_no_space_length); @@ -16,11 +50,26 @@ SetParser::SetParser(std::string nq) { free(query_no_space); } + #define QUOTES "(?:'|\"|`)?" #define SPACES " *" #define NAMES "(NAMES)" #define NAME_VALUE "((?:\\w|\\d)+)" +#define SESSION_P1 "(?:|SESSION +|@@|@@session.|@@local.)" +#define VAR_P1 "`?(@\\w+|\\w+)`?" +//#define VAR_VALUE "((?:[\\w/\\d:\\+\\-]|,)+)" +//#define VAR_VALUE "((?:CONCAT\\((?:(REPLACE|CONCAT)\\()+@@sql_mode,(?:(?:'|\\w|,| |\"|\\))+(?:\\)))|(?:[@\\w/\\d:\\+\\-]|,)+|(?:)))" + +// added (?:[\\w]+=(?:on|off)|,)+ for optimizer_switch +#define VAR_VALUE_P1_1 "(?:\\()*(?:SELECT)*(?: )*(?:CONCAT\\()*(?:(?:(?: )*REPLACE|IFNULL|CONCAT)\\()+(?: )*(?:NULL|@OLD_SQL_MODE|@@SQL_MODE),(?:(?:'|\\w|,| |\"|\\))+(?:\\))*)(?:\\))" +#define VAR_VALUE_P1_2 "|(?:NULL)" +#define VAR_VALUE_P1_3 "|(?:[\\w]+=(?:on|off)|,)+" +#define VAR_VALUE_P1_4 "|(?:[@\\w/\\d:\\+\\-]|,)+" +#define VAR_VALUE_P1_5 "|(?:(?:'{1}|\"{1})(?:)(?:'{1}|\"{1}))" +#define VAR_VALUE_P1_6 "|(?: )+" +#define VAR_VALUE_P1 "(" VAR_VALUE_P1_1 VAR_VALUE_P1_2 VAR_VALUE_P1_3 VAR_VALUE_P1_4 VAR_VALUE_P1_5 VAR_VALUE_P1_6 ")" + std::map> SetParser::parse1() { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Parsing query %s\n", query.c_str()); @@ -33,12 +82,48 @@ std::map> SetParser::parse1() { std::map> result; -#define SESSION_P1 "(?:|SESSION +|@@|@@session.|@@local.)" -#define VAR_P1 "`?(@\\w+|\\w+)`?" -//#define VAR_VALUE "((?:[\\w/\\d:\\+\\-]|,)+)" -//#define VAR_VALUE "((?:CONCAT\\((?:(REPLACE|CONCAT)\\()+@@sql_mode,(?:(?:'|\\w|,| |\"|\\))+(?:\\)))|(?:[@\\w/\\d:\\+\\-]|,)+|(?:)))" + const std::string pattern="(?:" NAMES SPACES QUOTES NAME_VALUE QUOTES "(?: +COLLATE +" QUOTES NAME_VALUE QUOTES "|)" "|" SESSION_P1 VAR_P1 SPACES "(?:|:)=" SPACES QUOTES VAR_VALUE_P1 QUOTES ") *,? *"; +VALGRIND_DISABLE_ERROR_REPORTING; + re2::RE2 re(pattern, *opt2); +VALGRIND_ENABLE_ERROR_REPORTING; + std::string var; + std::string value1, value2, value3, value4, value5; + re2::StringPiece input(query); + while (re2::RE2::Consume(&input, re, &value1, &value2, &value3, &value4, &value5)) { + std::vector op; +#ifdef DEBUG + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "SET parsing: v1='%s' , v2='%s' , v3='%s' , v4='%s' , v5='%s'\n", value1.c_str(), value2.c_str(), value3.c_str(), value4.c_str(), value5.c_str()); +#endif // DEBUG + std::string key; + if (value1 != "") { + // NAMES + key = value1; + op.push_back(value2); + if (value3 != "") { + op.push_back(value3); + } + } else if (value4 != "") { + // VARIABLE + value5.erase(value5.find_last_not_of(" \n\r\t,")+1); + key = value4; + if (value5 == "''" || value5 == "\"\"") { + op.push_back(""); + } else { + op.push_back(value5); + } + } -// added (?:[\\w]+=(?:on|off)|,)+ for optimizer_switch + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + result[key] = op; + } + if (input.size() != 0) { + result = {}; + } + delete opt2; + return result; +} + +/* #define VAR_VALUE_P1_1 "(?:\\()*(?:SELECT)*(?: )*(?:CONCAT\\()*(?:(?:(?: )*REPLACE|IFNULL|CONCAT)\\()+(?: )*(?:NULL|@OLD_SQL_MODE|@@SQL_MODE),(?:(?:'|\\w|,| |\"|\\))+(?:\\))*)(?:\\))" #define VAR_VALUE_P1_2 "|(?:NULL)" #define VAR_VALUE_P1_3 "|(?:[\\w]+=(?:on|off)|,)+" @@ -46,43 +131,254 @@ std::map> SetParser::parse1() { #define VAR_VALUE_P1_5 "|(?:(?:'{1}|\"{1})(?:)(?:'{1}|\"{1}))" #define VAR_VALUE_P1_6 "|(?: )+" #define VAR_VALUE_P1 "(" VAR_VALUE_P1_1 VAR_VALUE_P1_2 VAR_VALUE_P1_3 VAR_VALUE_P1_4 VAR_VALUE_P1_5 VAR_VALUE_P1_6 ")" +*/ - const std::string pattern="(?:" NAMES SPACES QUOTES NAME_VALUE QUOTES "(?: +COLLATE +" QUOTES NAME_VALUE QUOTES "|)" "|" SESSION_P1 VAR_P1 SPACES "(?:|:)=" SPACES QUOTES VAR_VALUE_P1 QUOTES ") *,? *"; + +void SetParser::generateRE_parse1v2() { + vector quote_symbol = {"\"", "'", "`"}; + vector var_patterns = {}; + { + // this block needs to be added at the very beginning, otherwise REPLACE|IFNULL|CONCAT may be considered simple words + // sw0 matches: + // - single word, quoted or not quoted + // - variable name , with double @ (session variable) or single @ (user defiend variable) + // - strings that includes words, spaces and commas + // - single quote string + string sw0 = "(?:\\w+|\"[\\w, ]+\"|\'[\\w, ]+\'|@(?:|@)\\w+|\'\')"; + string mw0 = "(?:" + sw0 + "(?: *, *" + sw0 + ")*)"; // multiple words, separated by comma and random spaces + string fww = "(?:(?:REPLACE|IFNULL|CONCAT)\\( *" + mw0 + "\\))"; // functions REPLACE|IFNULL|CONCAT having argument multiple words + string rfww2 = "(?:(?:REPLACE|IFNULL|CONCAT)\\( *" + fww + " *, *" + mw0 + "\\))"; //functions REPLACE|IFNULL|CONCAT calling the same functions + string rfww3 = "(?:(?:REPLACE|IFNULL|CONCAT)\\( *" + rfww2 + " *, *" + mw0 + "\\))"; //functions REPLACE|IFNULL|CONCAT calling the same functions + string rfww4 = "(?:(?:REPLACE|IFNULL|CONCAT)\\( *" + rfww3 + " *, *" + mw0 + "\\))"; //functions REPLACE|IFNULL|CONCAT calling the same functions + // all the above function allows space after the open parenthesis + string Selfww = "(?:\\(SELECT *" + fww + "\\))"; // for calls like SET sql_mode=(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION')); + // FIXME: add error handling in case rfww4 is removed +#ifdef PARSERDEBUG + if (verbosity > 0) { + cout << fww << endl; + cout << rfww2 << endl; + cout << rfww3 << endl; + cout << rfww4 << endl; + cout << Selfww << endl; + } +#endif + var_patterns.push_back(rfww4); // add first function calling function , otherwise functions will be considered simple names + var_patterns.push_back(rfww3); // add first function calling function , otherwise functions will be considered simple names + var_patterns.push_back(rfww2); // add first function calling function + var_patterns.push_back(fww); + var_patterns.push_back(Selfww); + } + + string vp = "NULL"; // NULL + var_patterns.push_back(vp); + vp = "\\w+"; // single word + var_patterns.push_back(vp); + for (auto it = quote_symbol.begin(); it != quote_symbol.end(); it++) { + string s = *it + vp + *it; + var_patterns.push_back(s); // add with quote + } + + vp = "\\w+(?:,\\w+)+"; // multiple words separated by commas, WITHOUT any spaces between words + // NOTE: we do not use multiple words without quotes + for (auto it = quote_symbol.begin(); it != quote_symbol.end(); it++) { + string s = *it + vp + *it; + var_patterns.push_back(s); // add with quote + } + + // regex for optimizer_switch + { + string v1 = "(?:on|off)"; // on|off + string v2 = "\\w+=" + v1; // "\\w+=(?:on|off)" , example: index_merge_sort_union=on + string v3 = v2 + "(?:," + v2 + ")*"; // "\\w+=(?:on|off)(?:,\\w+=(?:on|off))*" + // example index_merge=on,index_merge_union=on,index_merge_sort_union=off + // note: spaces are not allowed + // NOTE: the whole set of flags must be quoted + for (auto it = quote_symbol.begin(); it != quote_symbol.end(); it++) { + string s = *it + v3 + *it; + var_patterns.push_back(s); // add with quote + } + } + + +// DO NOT REMOVE THIS COMMENTED CODE +// It helps understanding how a regex was built + +// vp = "\\d+"; // a number integer N1 +// var_patterns.push_back(vp); +// vp = "\\d+\\.\\d+"; // a decimal N2 +// var_patterns.push_back(vp); +// vp = "\\d+(?:|\\.\\d+)"; // an integer or decimal N3 , merge of N1 and N2 +// var_patterns.push_back(vp); + +// vp = " *(?:\\+|\\-) *\\d+"; // a signed number integer with spaces before and after the sign . N4 = sign + N1 +// var_patterns.push_back(vp); +// vp = " *(?:\\+|\\-) *\\d+\\.\\d+"; // a signed decimal with spaces before and after the sign . N5 = sign + N2 +// var_patterns.push_back(vp); + +// vp = " *(?:\\+|\\-) *\\d+(?:|\\.\\d+)"; // a signed integer or decimal , N6 = N4 + N5 +// var_patterns.push_back(vp); + + vp = "(?:| *(?:\\+|\\-) *)\\d+(?:|\\.\\d+)"; // a signed or unsigned integer or decimal , N7 = merge of N3 and N6 + var_patterns.push_back(vp); + + + { + // time_zone in numeric format: + // - +/- sign + // 1 or 2 digits + // : + // 2 digits + string tzd = "(?:(?:\\+|\\-)(?:|\\d)\\d:\\d\\d)"; + // time_zone in string format: + // word / word + string tzw = "(?:\\w+/\\w+)"; + vp = "(?:" + tzd + "|" + tzw + ")"; // time_zone in numeric and string format + } + for (auto it = quote_symbol.begin(); it != quote_symbol.end(); it++) { + string s = *it + vp + *it; + var_patterns.push_back(s); // add with quote + } + + // add just variable name, for example SET time_zone = @old_time_zone + vp = "(?:@(?:|@)\\w+)"; + var_patterns.push_back(vp); + + + // add empty strings , with optional spaces + for (auto it = quote_symbol.begin(); it != quote_symbol.end(); it++) { + string s = *it + " *" + *it; + var_patterns.push_back(s); // add with quote + } + + + + string var_value = "("; + for (auto it = var_patterns.begin(); it != var_patterns.end(); it++) { + string s = "(?:" + *it + ")"; + auto it2 = it; + it2++; + if (it2 != var_patterns.end()) + s += "|"; + var_value += s; + } + var_value += ")"; + + + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "Parsing query %s\n", query.c_str()); + parse1v2_opt2 = new re2::RE2::Options(RE2::Quiet); + parse1v2_opt2->set_case_sensitive(false); + parse1v2_opt2->set_longest_match(false); + + + + + string var_1_0 = "(?:@\\w+|\\w+)"; // @name|name + string var_1 = "(" + var_1_0 + "|`" + var_1_0 + "`)"; // var_1_0|`var_1_0` + var_1 = SESSION_P1 + var_1; + + string charset_name = "(?:(?:\\w|\\d)+)"; + string name_value = "("; + for (auto it = quote_symbol.begin(); it != quote_symbol.end(); it++) { + string s = "(?:" + *it + charset_name + *it + ")"; + //auto it2 = it; + //it2++; + //if (it2 != quote_symbol.end()) + s += "|"; + name_value += s; + } + name_value += charset_name; // without quotes + name_value += ")"; + +#ifdef PARSERDEBUG + if (verbosity > 0) { + cout << var_value << endl; + cout << name_value << endl; + } +#endif + +#ifdef PARSERDEBUG +// delete opt2; +// return result; +#endif + +/* +#define QUOTES "(?:'|\"|`)?" +#define SPACES " *" +#define NAMES "(NAMES)" +#define NAME_VALUE "((?:\\w|\\d)+)" +*/ + + + //const std::string pattern="(?:" NAMES SPACES QUOTES NAME_VALUE QUOTES "(?: +COLLATE +" QUOTES NAME_VALUE QUOTES "|)" "|" SESSION_P1 VAR_P1 SPACES "(?:|:)=" SPACES QUOTES VAR_VALUE_P1 QUOTES ") *,? *"; + const std::string pattern="(?:" NAMES SPACES + name_value + "(?: +COLLATE +" + name_value + "|)" "|" + var_1 + SPACES "(?:|:)=" SPACES + var_value + ") *,? *"; + //const std::string pattern=var_1 + SPACES "(?:|:)=" SPACES + var_value; VALGRIND_DISABLE_ERROR_REPORTING; - re2::RE2 re(pattern, *opt2); +#ifdef PARSERDEBUG + if (verbosity > 0) { + cout << pattern << endl; + } +#endif + //re2::RE2 re(pattern, *opt2); + parse1v2_pattern = pattern; + parse1v2_re = new re2::RE2(parse1v2_pattern, *parse1v2_opt2); + parse1v2_init = true; +} + +std::map> SetParser::parse1v2() { + + std::map> result = {}; + + if (parse1v2_init == false) { + generateRE_parse1v2(); + } + + re2::RE2 re0("^\\s*SET\\s+", *parse1v2_opt2); + re2::RE2::Replace(&query, re0, ""); VALGRIND_ENABLE_ERROR_REPORTING; std::string var; std::string value1, value2, value3, value4, value5; re2::StringPiece input(query); - while (re2::RE2::Consume(&input, re, &value1, &value2, &value3, &value4, &value5)) { - std::vector op; + while (re2::RE2::Consume(&input, *parse1v2_re, &value1, &value2, &value3, &value4, &value5)) { + // FIXME: verify if we reached end of query. Did we parse everything? + std::vector op; #ifdef DEBUG - proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "SET parsing: v1='%s' , v2='%s' , v3='%s' , v4='%s' , v5='%s'\n", value1.c_str(), value2.c_str(), value3.c_str(), value4.c_str(), value5.c_str()); + proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "SET parsing: v1='%s' , v2='%s' , v3='%s' , v4='%s' , v5='%s'\n", value1.c_str(), value2.c_str(), value3.c_str(), value4.c_str(), value5.c_str()); #endif // DEBUG - std::string key; - if (value1 != "") { - // NAMES - key = value1; - op.push_back(value2); - if (value3 != "") { - op.push_back(value3); - } - } else if (value4 != "") { - // VARIABLE - value5.erase(value5.find_last_not_of(" \n\r\t,")+1); - key = value4; - if (value5 == "''" || value5 == "\"\"") { - op.push_back(""); - } else { - op.push_back(value5); - } - } - - std::transform(key.begin(), key.end(), key.begin(), ::tolower); - result[key] = op; - } - delete opt2; - return result; + std::string key; + if (value1 != "") { + // NAMES + key = value1; + remove_quotes(value2); + op.push_back(value2); + if (value3 != "") { + remove_quotes(value3); + op.push_back(value3); + } + } else if (value4 != "") { + // VARIABLE + value5.erase(value5.find_last_not_of(" \n\r\t,")+1); + key = value4; + if (value5 == "''" || value5 == "\"\"") { + op.push_back(""); + } else { + remove_quotes(value5); + op.push_back(value5); + } + } + + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + result[key] = op; + } + if (input.size() != 0) { +#ifdef PARSERDEBUG + if (verbosity > 0) { + cout << "Failed to parse: " << input << endl; + } +#endif + result = {}; + } + //delete opt2; + return result; } diff --git a/test/tap/tap/SQLite3_Server.cpp b/test/tap/tap/SQLite3_Server.cpp index 27f2605f84..cc7d0d7c4d 100644 --- a/test/tap/tap/SQLite3_Server.cpp +++ b/test/tap/tap/SQLite3_Server.cpp @@ -44,25 +44,6 @@ #endif // MSG_NOSIGNAL #endif // __APPLE__ -#define SAFE_SQLITE3_STEP(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc!=SQLITE_DONE) {\ - assert(rc==SQLITE_LOCKED);\ - usleep(100);\ - }\ - } while (rc!=SQLITE_DONE);\ -} while (0) - -#define SAFE_SQLITE3_STEP2(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) {\ - usleep(100);\ - }\ - } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY);\ -} while (0) - struct cpu_timer { cpu_timer() { diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 41b6d8b566..9a9afedb6e 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -589,7 +589,7 @@ int wait_for_replication( return result; } -MARIADB_CHARSET_INFO * proxysql_find_charset_collate(const char *collatename) { +MARIADB_CHARSET_INFO * proxysqlTap_find_charset_collate(const char *collatename) { MARIADB_CHARSET_INFO *c = (MARIADB_CHARSET_INFO *)mariadb_compiled_charsets; do { if (!strcasecmp(c->name, collatename)) { diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 78a4bc25ed..0863dd33d4 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -179,9 +179,9 @@ int wait_for_replication( * 'MySQL_Variables' is not a easy task due to its interdependeces with other ProxySQL modules. */ #ifdef LIBMYSQL_HELPER -MY_CHARSET_INFO * proxysql_find_charset_collate(const char *collatename); +MY_CHARSET_INFO * proxysqlTap_find_charset_collate(const char *collatename); #else -MARIADB_CHARSET_INFO * proxysql_find_charset_collate(const char *collatename); +MARIADB_CHARSET_INFO * proxysqlTap_find_charset_collate(const char *collatename); #endif /** * @brief Creates the new supplied user in ProxySQL with the provided diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 72807857aa..4acb9d280c 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -90,7 +90,8 @@ ifeq ($(UNAME_S),Linux) LDIRS+= -L$(COREDUMPER_LDIR) endif -MYLIBS=-Wl,--export-dynamic -Wl,-Bstatic -lconfig -lproxysql -ldaemon -ljemalloc -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt $(EXTRALINK) -lprometheus-cpp-pull -lprometheus-cpp-core -luuid +MYLIBS=-Wl,--export-dynamic -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -ltap -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt -ldl $(EXTRALINK) +MYLIBSJEMALLOC=-Wl,-Bstatic -ljemalloc STATIC_LIBS= $(SSL_LDIR)/libssl.a $(SSL_LDIR)/libcrypto.a $(CITYHASH_LDIR)/libcityhash.a LIBCOREDUMPERAR= @@ -127,6 +128,8 @@ debug: tests tests: tests-cpp tests-php tests-py \ setparser_test reg_test_3504-change_user_libmariadb_helper reg_test_3504-change_user_libmysql_helper \ + setparser_test2 setparser_test2-t \ + setparser_test3 setparser_test3-t \ set_testing-240.csv test_clickhouse_server_libmysql-t reg_test_stmt_resultset_err_no_rows_libmysql-t \ prepare_statement_err3024_libmysql-t prepare_statement_err3024_async-t reg_test_mariadb_stmt_store_result_libmysql-t \ reg_test_mariadb_stmt_store_result_async-t @@ -148,43 +151,58 @@ py-%: chmod +x $(patsubst py-%.py,%,$@) %-t: %-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) $^ $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o $@ + $(CXX) -std=c++11 $< $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ +# $(CXX) -std=c++11 $< $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl $(STATIC_LIBS) $(TAP_LIBDIR)/libtap.a -o $@ galera_1_timeout_count: galera_1_timeout_count.cpp $(TAP_LIBDIR)/libtap.a - g++ -DTEST_GALERA -DDEBUG galera_1_timeout_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) -lproxysql $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o galera_1_timeout_count -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DTEST_GALERA -DDEBUG galera_1_timeout_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o galera_1_timeout_count -DGITVERSION=\"$(GIT_VERSION)\" galera_2_timeout_no_count: galera_2_timeout_no_count.cpp $(TAP_LIBDIR)/libtap.a - g++ -DTEST_GALERA -DDEBUG galera_2_timeout_no_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) -lproxysql $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o galera_2_timeout_no_count -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DTEST_GALERA -DDEBUG galera_2_timeout_no_count.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o galera_2_timeout_no_count -DGITVERSION=\"$(GIT_VERSION)\" generate_set_session_csv: generate_set_session_csv.cpp g++ -o generate_set_session_csv generate_set_session_csv.cpp -O0 -ggdb aurora: aurora.cpp $(TAP_LIBDIR)/libtap.a - g++ -DTEST_AURORA -DDEBUG aurora.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) -lproxysql $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o aurora -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DTEST_AURORA -DDEBUG aurora.cpp ../tap/SQLite3_Server.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o aurora -DGITVERSION=\"$(GIT_VERSION)\" test_tokenizer-t: test_tokenizer-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_tokenizer-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -lproxysql -ltap -Wl,--no-as-needed -ldl -lpthread -o test_tokenizer-t -DGITVERSION=\"$(GIT_VERSION)\" + g++ test_tokenizer-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_tokenizer-t -DGITVERSION=\"$(GIT_VERSION)\" test_mysql_query_digests_stages-t: test_mysql_query_digests_stages-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_mysql_query_digests_stages-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -lproxysql -ltap -Wl,--no-as-needed -ldl -lpthread -o test_mysql_query_digests_stages-t -DGITVERSION=\"$(GIT_VERSION)\" + g++ test_mysql_query_digests_stages-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_mysql_query_digests_stages-t -DGITVERSION=\"$(GIT_VERSION)\" sqlite3-t: sqlite3-t.cpp $(TAP_LIBDIR)/libtap.a - g++ sqlite3-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -lproxysql $(MYLIBS) -ltap -Wl,--no-as-needed -ldl -lpthread $(LIBCOREDUMPERAR) -o sqlite3-t -DGITVERSION=\"$(GIT_VERSION)\" + g++ sqlite3-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(LIBCOREDUMPERAR) -o sqlite3-t -DGITVERSION=\"$(GIT_VERSION)\" test_gtid_forwarding-t: test_gtid_forwarding-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_gtid_forwarding-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -Wl,--no-as-needed -ldl -lpthread -o test_gtid_forwarding-t -DGITVERSION=\"$(GIT_VERSION)\" + g++ test_gtid_forwarding-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_gtid_forwarding-t -DGITVERSION=\"$(GIT_VERSION)\" test_admin_prometheus_metrics_dump-t: test_admin_prometheus_metrics_dump-t.cpp $(TAP_LIBDIR)/libtap.a - g++ test_admin_prometheus_metrics_dump-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -Wl,--no-as-needed -ldl -lpthread -o test_admin_prometheus_metrics_dump-t -DGITVERSION=\"$(GIT_VERSION)\" + g++ test_admin_prometheus_metrics_dump-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -o test_admin_prometheus_metrics_dump-t -DGITVERSION=\"$(GIT_VERSION)\" create_connection_annotation: test_connection_annotation-t.cpp - g++ -DTEST_AURORA -DDEBUG test_connection_annotation-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o test_connection_annotation-t -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DTEST_AURORA -DDEBUG test_connection_annotation-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(OBJ) $(MYLIBS) $(STATIC_LIBS) -o test_connection_annotation-t -DGITVERSION=\"$(GIT_VERSION)\" setparser_test: setparser_test.cpp $(TAP_LIBDIR)/libtap.a $(RE2_PATH)/util/test.cc $(LDIR)/set_parser.cpp $(LIBPROXYSQLAR) $(LIBCOREDUMPERAR) - g++ -DDEBUG setparser_test.cpp $(RE2_PATH)/util/test.cc $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -lproxysql $(MYLIBS) -ltap -ldl -lpthread $(WASAN) $(LIBCOREDUMPERAR) -o setparser_test -DGITVERSION=\"$(GIT_VERSION)\" + g++ -DDEBUG setparser_test.cpp $(RE2_PATH)/util/test.cc $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(WASAN) $(LIBCOREDUMPERAR) -o setparser_test -DGITVERSION=\"$(GIT_VERSION)\" + +setparser_test2-t: setparser_test2 + rm setparser_test2-t || true + ln -s setparser_test2 setparser_test2-t + +setparser_test2: setparser_test2.cpp $(TAP_LIBDIR)/libtap.a $(LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) + g++ -DDEBUG setparser_test2.cpp $(LDIR)/set_parser.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(WASAN) $(LIBCOREDUMPERAR) -o setparser_test2 -DGITVERSION=\"$(GIT_VERSION)\" + +setparser_test3-t: setparser_test3 + rm setparser_test3-t || true + ln -s setparser_test3 setparser_test3-t + +setparser_test3: setparser_test3.cpp $(TAP_LIBDIR)/libtap.a $(LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) + g++ -DDEBUG -DPARSERDEBUG setparser_test3.cpp $(LDIR)/set_parser.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) $(WASAN) $(LIBCOREDUMPERAR) -o setparser_test3 -DGITVERSION=\"$(GIT_VERSION)\" reg_test_3504-change_user_libmariadb_helper: reg_test_3504-change_user_helper.cpp - $(CXX) -DDEBUG reg_test_3504-change_user_helper.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o reg_test_3504-change_user_libmariadb_helper -DGITVERSION=\"$(GIT_VERSION)\" + $(CXX) -DDEBUG reg_test_3504-change_user_helper.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 $(STATIC_LIBS) -o reg_test_3504-change_user_libmariadb_helper -DGITVERSION=\"$(GIT_VERSION)\" reg_test_3504-change_user_libmysql_helper: reg_test_3504-change_user_helper.cpp $(CXX) -DLIBMYSQL_HELPER -DDEBUG reg_test_3504-change_user_helper.cpp -I/usr/include/mysql -I$(CURL_IDIR) -I$(SQLITE3_DIR) -I$(IDIR) -I$(JSON_IDIR) -I../tap -L$(TAP_LIBDIR) -lpthread -ldl -std=c++11 -ltap -lmysqlclient -o reg_test_3504-change_user_libmysql_helper -DGITVERSION=\"$(GIT_VERSION)\" diff --git a/test/tap/tests/aurora.cpp b/test/tap/tests/aurora.cpp index 938009a2c5..3ab69bdee9 100644 --- a/test/tap/tests/aurora.cpp +++ b/test/tap/tests/aurora.cpp @@ -41,25 +41,6 @@ extern SQLite3_Server *GloSQLite3Server; -#define SAFE_SQLITE3_STEP(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc!=SQLITE_DONE) {\ - assert(rc==SQLITE_LOCKED);\ - usleep(100);\ - }\ - } while (rc!=SQLITE_DONE);\ -} while (0) - -#define SAFE_SQLITE3_STEP2(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) {\ - usleep(100);\ - }\ - } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY);\ -} while (0) - void SQLite3_Server::init_aurora_ifaces_string(std::string& s) { if(!s.empty()) s += ";"; diff --git a/test/tap/tests/galera_1_timeout_count.cpp b/test/tap/tests/galera_1_timeout_count.cpp index 9426d4f94c..8915f30f80 100644 --- a/test/tap/tests/galera_1_timeout_count.cpp +++ b/test/tap/tests/galera_1_timeout_count.cpp @@ -52,25 +52,6 @@ extern MySQL_HostGroups_Manager *MyHGM; static bool init_tap=false; -#define SAFE_SQLITE3_STEP(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc!=SQLITE_DONE) {\ - assert(rc==SQLITE_LOCKED);\ - usleep(100);\ - }\ - } while (rc!=SQLITE_DONE);\ -} while (0) - -#define SAFE_SQLITE3_STEP2(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) {\ - usleep(100);\ - }\ - } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY);\ -} while (0) - void SQLite3_Server::init_galera_ifaces_string(std::string& s) { if(!s.empty()) s += ";"; diff --git a/test/tap/tests/galera_2_timeout_no_count.cpp b/test/tap/tests/galera_2_timeout_no_count.cpp index 0a64c86aa4..2154c0c429 100644 --- a/test/tap/tests/galera_2_timeout_no_count.cpp +++ b/test/tap/tests/galera_2_timeout_no_count.cpp @@ -53,25 +53,6 @@ extern MySQL_HostGroups_Manager *MyHGM; static bool init_tap=false; static std::vector timeouts; -#define SAFE_SQLITE3_STEP(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc!=SQLITE_DONE) {\ - assert(rc==SQLITE_LOCKED);\ - usleep(100);\ - }\ - } while (rc!=SQLITE_DONE);\ -} while (0) - -#define SAFE_SQLITE3_STEP2(_stmt) do {\ - do {\ - rc=sqlite3_step(_stmt);\ - if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) {\ - usleep(100);\ - }\ - } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY);\ -} while (0) - void SQLite3_Server::init_galera_ifaces_string(std::string& s) { if(!s.empty()) s += ";"; diff --git a/test/tap/tests/set_testing-240-t.cpp b/test/tap/tests/set_testing-240-t.cpp index 6566e02e9b..7660774515 100644 --- a/test/tap/tests/set_testing-240-t.cpp +++ b/test/tap/tests/set_testing-240-t.cpp @@ -47,7 +47,7 @@ char *schema=(char *)"information_schema"; int silent = 0; int sysbench = 0; int local=0; -int queries=4000; +int queries=3000; int uniquequeries=0; int histograms=-1; @@ -480,7 +480,9 @@ int main(int argc, char *argv[]) { //queries = 3000; //queries = testCases.size(); - plan(queries * num_threads); + unsigned int p = queries * num_threads; + p *= 2; // number of algorithms + plan(p); if (strcmp(host,"localhost")==0) { local = 1; @@ -492,16 +494,25 @@ int main(int argc, char *argv[]) { uniquequeries=(int)sqrt(uniquequeries); } - pthread_t *thi=(pthread_t *)malloc(sizeof(pthread_t)*num_threads); - if (thi==NULL) - return exit_status(); - - for (unsigned int i=0; i::iterator it = vars_counters.begin(); it!=vars_counters.end(); it++) { diag("Unknown variable %s:\t Count: %d , unknown: %d", it->first.c_str(), it->second.count, it->second.unknown); diff --git a/test/tap/tests/setparser_test.cpp b/test/tap/tests/setparser_test.cpp index b2be4cc6ff..8bc210ec4a 100644 --- a/test/tap/tests/setparser_test.cpp +++ b/test/tap/tests/setparser_test.cpp @@ -224,7 +224,7 @@ TEST(TestParse, SET_VARIOUS) { static Test multiple[] = { { "SET time_zone = 'Europe/Paris', sql_mode = 'TRADITIONAL'", { Expected("time_zone", {"Europe/Paris"}), Expected("sql_mode", {"TRADITIONAL"}) } }, { "SET time_zone = 'Europe/Paris', sql_mode = IFNULL(NULL,\"STRICT_TRANS_TABLES\")", { Expected("time_zone", {"Europe/Paris"}), Expected("sql_mode", {"IFNULL(NULL,\"STRICT_TRANS_TABLES\")"}) } }, - { "SET sql_mode = 'TRADITIONAL', NAMES 'utf8 COLLATE 'unicode_ci'", { Expected("sql_mode", {"TRADITIONAL"}), Expected("names", {"utf8", "unicode_ci"}) } }, + { "SET sql_mode = 'TRADITIONAL', NAMES 'utf8 COLLATE 'unicode_ci'", { Expected("sql_mode", {"TRADITIONAL"}), Expected("names", {"utf8", "unicode_ci"}) } }, // FIXME: typo { "SET @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483", { Expected("sql_mode", {"CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO')"}), Expected("sql_auto_is_null", {"0"}), Expected("wait_timeout", {"2147483"}) } }, diff --git a/test/tap/tests/setparser_test2.cpp b/test/tap/tests/setparser_test2.cpp new file mode 100644 index 0000000000..215d8b8d72 --- /dev/null +++ b/test/tap/tests/setparser_test2.cpp @@ -0,0 +1,57 @@ +/** + * @file setparser_test.cpp + * @brief Test file for unit testing 'SetParser' type, responsible of parsing + * non-trivial 'SET' statements. + */ + +#include "setparser_test_common.h" + +void TestParse(const Test* tests, int ntests, const std::string& title) { + for (int i = 0; i < ntests; i++) { + std::map> data; + for(auto it = std::begin(tests[i].results); it != std::end(tests[i].results); ++it) { + data[it->var] = it->values; + } + + cout << "Processing query: " << tests[i].query << endl; + SetParser parser(tests[i].query); + std::map> result = parser.parse1(); + + cout << endl; + printMap("result", result); + cout << endl; + printMap("expected", data); + cout << endl; + + CHECK_EQ(result.size(), data.size()); + ok(result.size() == data.size() , "Sizes match: %lu, %lu" , result.size() , data.size()); + CHECK(std::equal(std::begin(result), std::end(result), std::begin(data))); + ok(std::equal(std::begin(result), std::end(result), std::begin(data)) == true, "Elements match"); + } +} + + +int main(int argc, char** argv) { + unsigned int p = 0; + p += arraysize(sql_mode); + p += arraysize(time_zone); + p += arraysize(session_track_gtids); + p += arraysize(character_set_results); + p += arraysize(names); + p += arraysize(various); + p += arraysize(multiple); + p += arraysize(Set1_v1); + p += arraysize(syntax_errors); + p *= 2; + plan(p); + TestParse(sql_mode, arraysize(sql_mode), "sql_mode"); + TestParse(time_zone, arraysize(time_zone), "time_zone"); + TestParse(session_track_gtids, arraysize(session_track_gtids), "session_track_gtids"); + TestParse(character_set_results, arraysize(character_set_results), "character_set_results"); + TestParse(names, arraysize(names), "names"); + TestParse(various, arraysize(various), "various"); + TestParse(multiple, arraysize(multiple), "multiple"); + TestParse(Set1_v1, arraysize(Set1_v1), "Set1_v1"); + TestParse(syntax_errors, arraysize(syntax_errors), "syntax_errors"); + return exit_status(); +} diff --git a/test/tap/tests/setparser_test3.cpp b/test/tap/tests/setparser_test3.cpp new file mode 100644 index 0000000000..b184b6d0e6 --- /dev/null +++ b/test/tap/tests/setparser_test3.cpp @@ -0,0 +1,64 @@ +/** + * @file setparser_test.cpp + * @brief Test file for unit testing 'SetParser' type, responsible of parsing + * non-trivial 'SET' statements. + */ + +#include "setparser_test_common.h" + +SetParser *parser = NULL; + +void TestParse(const Test* tests, int ntests, const std::string& title) { + for (int i = 0; i < ntests; i++) { + std::map> data; + for(auto it = std::begin(tests[i].results); it != std::end(tests[i].results); ++it) { + data[it->var] = it->values; + } + + //SetParser parser(tests[i].query, 1); + //std::map> result = parser.parse1(); + //std::map> result = parser.parse1v2(); + + cout << "Processing query: " << tests[i].query << endl; + parser->set_query(tests[i].query); + std::map> result = parser->parse1v2(); + + cout << endl; + printMap("result", result); + cout << endl; + printMap("expected", data); + cout << endl; + + CHECK_EQ(result.size(), data.size()); + ok(result.size() == data.size() , "Sizes match: %lu, %lu" , result.size() , data.size()); + CHECK(std::equal(std::begin(result), std::end(result), std::begin(data))); + ok(std::equal(std::begin(result), std::end(result), std::begin(data)) == true, "Elements match"); + } +} + + +int main(int argc, char** argv) { + unsigned int p = 0; + p += arraysize(sql_mode); + p += arraysize(time_zone); + p += arraysize(session_track_gtids); + p += arraysize(character_set_results); + p += arraysize(names); + p += arraysize(various); + p += arraysize(multiple); + p += arraysize(Set1_v2); + p += arraysize(syntax_errors); + p *= 2; + plan(p); + parser = new SetParser("", 1); + TestParse(sql_mode, arraysize(sql_mode), "sql_mode"); + TestParse(time_zone, arraysize(time_zone), "time_zone"); + TestParse(session_track_gtids, arraysize(session_track_gtids), "session_track_gtids"); + TestParse(character_set_results, arraysize(character_set_results), "character_set_results"); + TestParse(names, arraysize(names), "names"); + TestParse(various, arraysize(various), "various"); + TestParse(multiple, arraysize(multiple), "multiple"); + TestParse(Set1_v2, arraysize(Set1_v2), "Set1_v2"); + TestParse(syntax_errors, arraysize(syntax_errors), "syntax_errors"); + return exit_status(); +} diff --git a/test/tap/tests/setparser_test_common.h b/test/tap/tests/setparser_test_common.h new file mode 100644 index 0000000000..b7f34ae670 --- /dev/null +++ b/test/tap/tests/setparser_test_common.h @@ -0,0 +1,266 @@ +// NOTE: Avoids the definition of 'global_variables glovars' in 'proxysql_structs.h' +#define PROXYSQL_EXTERN +// NOTE: Avoids definition of 'proxy_sqlite3_*' functions as 'extern' +#define MAIN_PROXY_SQLITE3 + +#include "command_line.h" +#include "tap.h" + +#include + +#include "re2/re2.h" +#include "re2/regexp.h" +#include "util/test.h" +#include "set_parser.h" +#include +#include +#include +#include +#include + +#include "openssl/ssl.h" +#include "mysql.h" +#include "proxysql_structs.h" +#include "sqlite3db.h" +#include "MySQL_LDAP_Authentication.hpp" + +using namespace std; + +MySQL_LDAP_Authentication *GloMyLdapAuth = nullptr; +// ****************************************************************************************** + +bool iequals(const std::string& a, const std::string& b) +{ + unsigned int sz = a.size(); + if (b.size() != sz) + return false; + for (unsigned int i = 0; i < sz; ++i) + if (tolower(a[i]) != tolower(b[i])) + return false; + return true; +} + + +void printMap(const std::string query, std::map> map) { + std::cout << "Query: " << query << endl; + for (const auto& entry : map) { + std::cout << " - Key: " << entry.first << endl; + + for (const auto& value : entry.second) { + std::cout << " + Value: " << value << endl; + } + } +} + + +struct Expected { + const char* var; + std::vector values; + Expected(const char* var, std::vector values): var(var), values(values){}; +}; + +struct Test { + const char* query; + std::vector results; +}; + +static Test sql_mode[] = { + { "SET @@sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET SESSION sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET @@session.sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET @@local.sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET SQL_MODE ='TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET SQL_MODE = \"TRADITIONAL\"", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET SQL_MODE = TRADITIONAL", { Expected("sql_mode", {"TRADITIONAL"}) } }, + { "set sql_mode = IFNULL(NULL,\"STRICT_TRANS_TABLES\")", { Expected("sql_mode", {"IFNULL(NULL,\"STRICT_TRANS_TABLES\")"}) } }, + { "set sql_mode = IFNULL(NULL,'STRICT_TRANS_TABLES')", { Expected("sql_mode", {"IFNULL(NULL,'STRICT_TRANS_TABLES')"}) } }, + { "SET @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ', STRICT_ALL_TABLES'), ', NO_AUTO_VALUE_ON_ZERO')", { Expected("sql_mode", {"CONCAT(CONCAT(@@sql_mode, ', STRICT_ALL_TABLES'), ', NO_AUTO_VALUE_ON_ZERO')"}) } }, + { "SET @@LOCAL.sql_mode = CONCAT(CONCAT(@@sql_mode, ', STRICT_ALL_TABLES'), ', NO_AUTO_VALUE_ON_ZERO')", { Expected("sql_mode", {"CONCAT(CONCAT(@@sql_mode, ', STRICT_ALL_TABLES'), ', NO_AUTO_VALUE_ON_ZERO')"}) } }, + { "set session sql_mode = 'ONLY_FULL_GROUP_BY'" , { Expected("sql_mode", {"ONLY_FULL_GROUP_BY"}) } }, + { "SET sql_mode = 'NO_ZERO_DATE,STRICT_ALL_TABLES,ONLY_FULL_GROUP_BY'" , { Expected("sql_mode", {"NO_ZERO_DATE,STRICT_ALL_TABLES,ONLY_FULL_GROUP_BY"}) } }, + { "SET @@sql_mode = CONCAT(@@sql_mode, ',', 'ONLY_FULL_GROUP_BY')" , { Expected("sql_mode", {"CONCAT(@@sql_mode, ',', 'ONLY_FULL_GROUP_BY')"}) } }, + { "SET @@sql_mode = REPLACE(REPLACE(REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY,', ''),',ONLY_FULL_GROUP_BY', ''),'ONLY_FULL_GROUP_BY', '')" , { Expected("sql_mode", {"REPLACE(REPLACE(REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY,', ''),',ONLY_FULL_GROUP_BY', ''),'ONLY_FULL_GROUP_BY', '')"}) } }, + { "SET @@sql_mode = REPLACE( REPLACE( REPLACE( @@sql_mode, 'ONLY_FULL_GROUP_BY,', ''),',ONLY_FULL_GROUP_BY', ''),'ONLY_FULL_GROUP_BY', '')" , { Expected("sql_mode", {"REPLACE( REPLACE( REPLACE( @@sql_mode, 'ONLY_FULL_GROUP_BY,', ''),',ONLY_FULL_GROUP_BY', ''),'ONLY_FULL_GROUP_BY', '')"}) } }, +// { "SET @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ', STRICT_ALL_TABLES'), ', NO_AUTO_VALUE_ON_ZERO')", { Expected("sql_mode", {"CONCAT(CONCAT(@@sql_mode, ', STRICT_ALL_TABLES'), ', NO_AUTO_VALUE_ON_ZERO')"}) } }, + { "SET SQL_MODE=IFNULL(@@sql_mode,'')", { Expected("sql_mode", { "IFNULL(@@sql_mode,'')" } ) } }, + { "SET SQL_MODE=IFNULL(@old_sql_mode,'')", { Expected("sql_mode", { "IFNULL(@old_sql_mode,'')" } ) } }, + { "SET SQL_MODE=IFNULL(@OLD_SQL_MODE,'')", { Expected("sql_mode", { "IFNULL(@OLD_SQL_MODE,'')" } ) } }, + // Complex queries involving 'SELECT' and surrounding parenthesis should be parsed properly + { "SET sql_mode=(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'))", { Expected("sql_mode", { "(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'))" } ) } }, + { "SET sql_mode=(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION')), time_zone = '+00:00', NAMES utf8mb4 COLLATE utf8mb4_unicode_ci", + { + Expected("sql_mode", { "(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'))" } ), + Expected("time_zone", { "+00:00" } ), + Expected("names", {"utf8mb4", "utf8mb4_unicode_ci"} ) + } + }, + // Empty set of 'sql_mode' should result into an empty value + { "SET sql_mode=''", { Expected("sql_mode", { "" } ) } }, + // Invalid 'non-matching' versions of 'sql_mode' should result into 'non-matching' + { "SET sql_mode=(SELECT CONCA(@@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'))", {} }, + { "SET sql_mode=(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT[,NO_ENGINE_SUBSTITUTION'))", {} }, + { "SET sql_mode=(SELCT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT[,NO_ENGINE_SUBSTITUTION'))", {} } +}; + +static Test Set1_v1[] = { + { "SET sql_mode=(SELECT CONCAT(@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'))", {} }, // parse1v2 SHOULD process it + { "SET sql_mode = 'TRADITIONAL', NAMES 'utf8 COLLATE 'unicode_ci'", { Expected("sql_mode", {"TRADITIONAL"}), Expected("names", {"utf8", "unicode_ci"}) } }, // FIXME: this should return an error + { "SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(REPLACE(REPLACE(REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''), 'STRICT_ALL_TABLES', ''), 'TRADITIONAL', ''), ',NO_AUTO_VALUE_ON_ZERO'), ',NO_ENGINE_SUBSTITUTION'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 3600", + { + Expected("names", {"utf8"}), + Expected("sql_mode", {"CONCAT(CONCAT(REPLACE(REPLACE(REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''), 'STRICT_ALL_TABLES', ''), 'TRADITIONAL', ''), ',NO_AUTO_VALUE_ON_ZERO'), ',NO_ENGINE_SUBSTITUTION')"}), + Expected("sql_auto_is_null", {"0"}), + Expected("wait_timeout", {"3600"}) } }, // v2 is not able to parse this, because it can process only up to 4 functions + { "SET character_set_connection=utf8,character_set_results=utf8,character_set_client=binary", {} }, // v1 can't parse this +}; + +static Test Set1_v2[] = { + //{ "SET sql_mode=(SELECT CONCAT(@sql_mode, ',PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'))", {} }, // parse1v2 SHOULD process it + //{ "SET sql_mode = 'TRADITIONAL', NAMES 'utf8 COLLATE 'unicode_ci'", { Expected("sql_mode", {"TRADITIONAL"}), Expected("names", {"utf8", "unicode_ci"}) } }, // FIXME: this should return an error + { "SET sql_mode='TRADITIONAL' , whatever = , autocommit=1", {} }, // v1 is not able to process this + { "SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(REPLACE(REPLACE(REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''), 'STRICT_ALL_TABLES', ''), 'TRADITIONAL', ''), ',NO_AUTO_VALUE_ON_ZERO'), ',NO_ENGINE_SUBSTITUTION'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 3600", + {} }, // v2 is not able to parse this, because it can process only up to 4 functions + { "SET character_set_connection=utf8,character_set_results=utf8,character_set_client=binary", + { + Expected("character_set_connection", {"utf8"}), + Expected("character_set_results", {"utf8"}), + Expected("character_set_client", {"binary"}), + }, + } +}; + +static Test syntax_errors[] = { + { "SET sql_mode='TRADITIONAL' , whatever", {} }, + { "SET sql_mode='TRADITIONAL' , whatever = ", {} }, +}; + +static Test time_zone[] = { + { "SET @@time_zone = 'Europe/Paris'", { Expected("time_zone", {"Europe/Paris"}) } }, + { "SET @@time_zone = '+00:00'", { Expected("time_zone", {"+00:00"}) } }, + { "SET @@time_zone = \"Europe/Paris\"", { Expected("time_zone", {"Europe/Paris"}) } }, + { "SET @@time_zone = \"+00:00\"", { Expected("time_zone", {"+00:00"}) } }, + { "SET @@time_zone = @OLD_TIME_ZONE", { Expected("time_zone", {"@OLD_TIME_ZONE"}) } }, + { "SET @@TIME_ZONE = @OLD_TIME_ZONE", { Expected("time_zone", {"@OLD_TIME_ZONE"}) } }, + { "SET @@TIME_ZONE := 'SYSTEM'", { Expected("time_zone", {"SYSTEM"}) } }, + { "SET time_zone := 'SYSTEM'", { Expected("time_zone", {"SYSTEM"}) } }, +}; + +static Test session_track_gtids[] = { + { "SET @@session_track_gtids = OFF", { Expected("session_track_gtids", {"OFF"}) } }, + { "SET @@session_track_gtids = OWN_GTID", { Expected("session_track_gtids", {"OWN_GTID"}) } }, + { "SET @@SESSION.session_track_gtids = OWN_GTID", { Expected("session_track_gtids", {"OWN_GTID"}) } }, + { "SET @@LOCAL.session_track_gtids = OWN_GTID", { Expected("session_track_gtids", {"OWN_GTID"}) } }, + { "SET SESSION session_track_gtids = OWN_GTID", { Expected("session_track_gtids", {"OWN_GTID"}) } }, + { "SET @@session_track_gtids = ALL_GTIDS", { Expected("session_track_gtids", {"ALL_GTIDS"}) } }, + { "SET @@SESSION.session_track_gtids = ALL_GTIDS", { Expected("session_track_gtids", {"ALL_GTIDS"}) } }, + { "SET @@LOCAL.session_track_gtids = ALL_GTIDS", { Expected("session_track_gtids", {"ALL_GTIDS"}) } }, + { "SET SESSION session_track_gtids = ALL_GTIDS", { Expected("session_track_gtids", {"ALL_GTIDS"}) } }, +}; + +static Test character_set_results[] = { + { "SET @@character_set_results = utf8", { Expected("character_set_results", {"utf8"}) } }, + { "SET @@character_set_results = NULL", { Expected("character_set_results", {"NULL"}) } }, + { "SET character_set_results = NULL", { Expected("character_set_results", {"NULL"}) } }, + { "SET @@session.character_set_results = NULL", { Expected("character_set_results", {"NULL"}) } }, + { "SET @@local.character_set_results = NULL", { Expected("character_set_results", {"NULL"}) } }, + { "SET session character_set_results = NULL", { Expected("character_set_results", {"NULL"}) } }, +}; + +static Test names[] = { + { "SET NAMES utf8", { Expected("names", {"utf8"}) } }, + { "SET NAMES 'utf8'", { Expected("names", {"utf8"}) } }, + { "SET NAMES \"utf8\"", { Expected("names", {"utf8"}) } }, + { "SET NAMES utf8 COLLATE unicode_ci", { Expected("names", {"utf8", "unicode_ci"}) } }, +}; + +static Test various[] = { + { "SET @@SESSION.SQL_SELECT_LIMIT= DEFAULT", { Expected("sql_select_limit", {"DEFAULT"}) } }, + { "SET @@LOCAL.SQL_SELECT_LIMIT= DEFAULT", { Expected("sql_select_limit", {"DEFAULT"}) } }, + { "SET @@SQL_SELECT_LIMIT= DEFAULT", { Expected("sql_select_limit", {"DEFAULT"}) } }, + { "SET SESSION SQL_SELECT_LIMIT = DEFAULT", { Expected("sql_select_limit", {"DEFAULT"}) } }, + { "SET @@SESSION.SQL_SELECT_LIMIT= 1234", { Expected("sql_select_limit", {"1234"}) } }, + { "SET @@LOCAL.SQL_SELECT_LIMIT= 1234", { Expected("sql_select_limit", {"1234"}) } }, + { "SET @@SQL_SELECT_LIMIT= 1234", { Expected("sql_select_limit", {"1234"}) } }, + { "SET SESSION SQL_SELECT_LIMIT = 1234", { Expected("sql_select_limit", {"1234"}) } }, + { "SET @@SESSION.SQL_SELECT_LIMIT= 1234", { Expected("sql_select_limit", {"1234"}) } }, + { "SET @@LOCAL.SQL_SELECT_LIMIT= 1234", { Expected("sql_select_limit", {"1234"}) } }, + { "SET @@SESSION.SQL_SELECT_LIMIT= @old_sql_select_limit", { Expected("sql_select_limit", {"@old_sql_select_limit"}) } }, + { "SET @@LOCAL.SQL_SELECT_LIMIT= @old_sql_select_limit", { Expected("sql_select_limit", {"@old_sql_select_limit"}) } }, + { "SET SQL_SELECT_LIMIT= @old_sql_select_limit", { Expected("sql_select_limit", {"@old_sql_select_limit"}) } }, + { "SET @@SESSION.sql_auto_is_null = 0", { Expected("sql_auto_is_null", {"0"}) } }, + { "SET @@LOCAL.sql_auto_is_null = 0", { Expected("sql_auto_is_null", {"0"}) } }, + { "SET SESSION sql_auto_is_null = 1", { Expected("sql_auto_is_null", {"1"}) } }, + { "SET sql_auto_is_null = OFF", { Expected("sql_auto_is_null", {"OFF"}) } }, + { "SET @@sql_auto_is_null = ON", { Expected("sql_auto_is_null", {"ON"}) } }, + { "SET @@SESSION.sql_safe_updates = 0", { Expected("sql_safe_updates", {"0"}) } }, + { "SET @@LOCAL.sql_safe_updates = 0", { Expected("sql_safe_updates", {"0"}) } }, + { "SET SESSION sql_safe_updates = 1", { Expected("sql_safe_updates", {"1"}) } }, + { "SET SQL_SAFE_UPDATES = OFF", { Expected("sql_safe_updates", {"OFF"}) } }, + { "SET @@sql_safe_updates = ON", { Expected("sql_safe_updates", {"ON"}) } }, + { "SET optimizer_switch=`index_merge=OFF`" , { Expected("optimizer_switch", {"index_merge=OFF"}) } }, + { "SET optimizer_switch='index_merge=on,index_merge_union=off,index_merge_sort_union=on'" , { Expected("optimizer_switch", {"index_merge=on,index_merge_union=off,index_merge_sort_union=on"}) } }, +}; + +static Test multiple[] = { + { "SET time_zone = 'Europe/Paris', sql_mode = 'TRADITIONAL'", { Expected("time_zone", {"Europe/Paris"}), Expected("sql_mode", {"TRADITIONAL"}) } }, + { "SET time_zone = 'Europe/Paris', sql_mode = IFNULL(NULL,\"STRICT_TRANS_TABLES\")", { Expected("time_zone", {"Europe/Paris"}), Expected("sql_mode", {"IFNULL(NULL,\"STRICT_TRANS_TABLES\")"}) } }, + { "SET sql_mode = 'TRADITIONAL', NAMES 'utf8' COLLATE 'unicode_ci'", { Expected("sql_mode", {"TRADITIONAL"}), Expected("names", {"utf8", "unicode_ci"}) } }, + { "SET @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483", + { Expected("sql_mode", {"CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO')"}), Expected("sql_auto_is_null", {"0"}), + Expected("wait_timeout", {"2147483"}) } }, + { "SET @@LOCAL.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483", + { Expected("sql_mode", {"CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO')"}), Expected("sql_auto_is_null", {"0"}), + Expected("wait_timeout", {"2147483"}) } }, + { "set autocommit=1, sql_mode = concat(@@sql_mode,',STRICT_TRANS_TABLES')", { Expected("autocommit", {"1"}), Expected("sql_mode", {"concat(@@sql_mode,',STRICT_TRANS_TABLES')"}) } }, + { "SET NAMES utf8, @@SESSION.sql_mode = CONCAT(REPLACE(REPLACE(REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''), 'STRICT_ALL_TABLES', ''), 'TRADITIONAL', ''), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 3600", + { Expected("names", {"utf8"}), Expected("sql_mode", {"CONCAT(REPLACE(REPLACE(REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''), 'STRICT_ALL_TABLES', ''), 'TRADITIONAL', ''), ',NO_AUTO_VALUE_ON_ZERO')"}), Expected("sql_auto_is_null", {"0"}), + Expected("wait_timeout", {"3600"}) } }, + { "SET NAMES utf8, @@LOCAL.sql_mode = CONCAT(REPLACE(REPLACE(REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''), 'STRICT_ALL_TABLES', ''), 'TRADITIONAL', ''), ',NO_AUTO_VALUE_ON_ZERO'), @@LOCAL.sql_auto_is_null = 0, @@LOCAL.wait_timeout = 3600", + { Expected("names", {"utf8"}), Expected("sql_mode", {"CONCAT(REPLACE(REPLACE(REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', ''), 'STRICT_ALL_TABLES', ''), 'TRADITIONAL', ''), ',NO_AUTO_VALUE_ON_ZERO')"}), Expected("sql_auto_is_null", {"0"}), + Expected("wait_timeout", {"3600"}) } }, + { "set autocommit=1, session_track_schema=1, sql_mode = concat(@@sql_mode,',STRICT_TRANS_TABLES'), @@SESSION.net_write_timeout=7200", { Expected("autocommit", {"1"}), Expected("session_track_schema", {"1"}), Expected("sql_mode", {"concat(@@sql_mode,',STRICT_TRANS_TABLES')"}), + Expected("net_write_timeout", {"7200"}) } }, + { "set autocommit=1, session_track_schema=1, sql_mode = concat(@@sql_mode,',STRICT_TRANS_TABLES'), @@LOCAL.net_write_timeout=7200", { Expected("autocommit", {"1"}), Expected("session_track_schema", {"1"}), Expected("sql_mode", {"concat(@@sql_mode,',STRICT_TRANS_TABLES')"}), + Expected("net_write_timeout", {"7200"}) } }, + // Mutiple set queries involving 'NULL' values should be properly parsed with and without spaces + { "set character_set_results=null, names latin7, character_set_client='utf8mb4'", + { + Expected("character_set_results", { "null" } ), + Expected("names", { "latin7" } ), + Expected("character_set_client", { "utf8mb4" } ), + } + }, + { "SET character_set_results=NULL, NAMES latin7, character_set_client='utf8mb4'", + { + Expected("character_set_results", { "NULL" } ), + Expected("names", { "latin7" } ), + Expected("character_set_client", { "utf8mb4" } ), + } + }, + { "set character_set_results=null,names latin7,character_set_client='utf8mb4'", + { + Expected("character_set_results", { "null" } ), + Expected("names", { "latin7" } ), + Expected("character_set_client", { "utf8mb4" } ), + } + }, + { "SET character_set_results=NULL,NAMES latin7,character_set_client='utf8mb4'", + { + Expected("character_set_results", { "NULL" } ), + Expected("names", { "latin7" } ), + Expected("character_set_client", { "utf8mb4" } ), + } + }, + { "SET @@autocommit := 0 , NAMES \"utf8mb3\"", { Expected("autocommit", {"0"}) , Expected("names",{"utf8mb3"}) } }, + { "SET character_set_results=NULL,NAMES latin7,character_set_client='utf8mb4', autocommit := 1 , time_zone = 'Europe/Paris'", + { + Expected("character_set_results", { "NULL" } ), + Expected("names", { "latin7" } ), + Expected("character_set_client", { "utf8mb4" } ), + Expected("autocommit", { "1" } ), + Expected("time_zone", { "Europe/Paris" } ), + } + }, +}; + diff --git a/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp b/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp index 0be69da672..bc6e27f2f3 100644 --- a/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp +++ b/test/tap/tests/test_com_reset_connection_com_change_user-t.cpp @@ -965,7 +965,7 @@ int test_mysql_server_variables(MYSQL*, const CommandLine& cl, const std::vector MYSQL* mysql = mysql_init(NULL); // Use a known default charset for the connection - MARIADB_CHARSET_INFO* latin2_charset = proxysql_find_charset_collate("latin2_general_ci"); + MARIADB_CHARSET_INFO* latin2_charset = proxysqlTap_find_charset_collate("latin2_general_ci"); mysql->charset = latin2_charset; if (!mysql_real_connect(mysql, cl.host, "root", "root", NULL, 13306, NULL, 0)) { @@ -1060,7 +1060,7 @@ int test_mysql_server_variables(MYSQL*, const CommandLine& cl, const std::vector std::string username = std::get<0>(user_configs[1]); std::string password = std::get<1>(user_configs[1]); - MARIADB_CHARSET_INFO* charset = proxysql_find_charset_collate("latin2_general_ci"); + MARIADB_CHARSET_INFO* charset = proxysqlTap_find_charset_collate("latin2_general_ci"); mysql->charset = charset; int err_code = mysql_change_user(mysql, username.c_str(), password.c_str(), NULL); @@ -1127,7 +1127,7 @@ int main(int argc, char** argv) { MYSQL* proxysql = mysql_init(NULL); // Use a known default charset for the connection - MARIADB_CHARSET_INFO* latin2_charset = proxysql_find_charset_collate("latin2_general_ci"); + MARIADB_CHARSET_INFO* latin2_charset = proxysqlTap_find_charset_collate("latin2_general_ci"); proxysql->charset = latin2_charset; if ( diff --git a/test/tap/tests/test_set_collation-t.cpp b/test/tap/tests/test_set_collation-t.cpp index b4f7b5de65..090711655e 100644 --- a/test/tap/tests/test_set_collation-t.cpp +++ b/test/tap/tests/test_set_collation-t.cpp @@ -33,7 +33,7 @@ int create_proxysql_connections(const CommandLine& cl, const std::vectorcharset = charset; if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { @@ -57,7 +57,7 @@ int run_change_user_on_all(const CommandLine& cl, const std::vector for (int i = 0; i < conns.size(); i++) { MYSQL* mysql = conns[i]; MYSQL_QUERY(mysql, "START TRANSACTION"); - const MARIADB_CHARSET_INFO* charset = proxysql_find_charset_collate(collations[i].c_str()); + const MARIADB_CHARSET_INFO* charset = proxysqlTap_find_charset_collate(collations[i].c_str()); mysql->charset = charset; if (mysql_change_user(mysql,cl.username, cl.password, NULL)) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql));