diff --git a/.gitignore b/.gitignore index 610766c9e..d088ef0db 100644 --- a/.gitignore +++ b/.gitignore @@ -189,3 +189,4 @@ test/tap/tests/galera_1_timeout_count test/tap/tests/galera_2_timeout_no_count test/tap/tests/setparser_test test/tap/tests/generate_set_session_csv +test/tap/tests/set_testing-240.csv diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index 5ff381e6f..35c90d21f 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -252,7 +252,7 @@ class MySQL_Session void *ldap_ctx; // this variable is relevant only if status == SETTING_VARIABLE - enum variable_name changing_variable_idx; + enum mysql_variable_name changing_variable_idx; MySQL_Session(); ~MySQL_Session(); diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 36728f1b2..b002b9851 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -150,10 +150,10 @@ enum MySQL_DS_type { }; /* NOTE: - make special ATTENTION that the order in variable_name + make special ATTENTION that the order in mysql_variable_name and mysql_tracked_variables[] is THE SAME */ -enum variable_name { +enum mysql_variable_name { SQL_CHARACTER_SET, SQL_CHARACTER_ACTION, SQL_SET_NAMES, @@ -164,24 +164,43 @@ enum variable_name { SQL_ISOLATION_LEVEL, SQL_TRANSACTION_READ, SQL_COLLATION_CONNECTION, - SQL_NET_WRITE_TIMEOUT, SQL_LOG_BIN, SQL_WSREP_SYNC_WAIT, SQL_NAME_LAST_LOW_WM, - SQL_SAFE_UPDATES, - SQL_SELECT_LIMIT, - SQL_SQL_MODE, - SQL_TIME_ZONE, - SQL_SQL_AUTO_IS_NULL, - SQL_MAX_JOIN_SIZE, - SQL_GROUP_CONCAT_MAX_LEN, + SQL_AUTO_INCREMENT_INCREMENT, + SQL_AUTO_INCREMENT_OFFSET, + SQL_BIG_TABLES, SQL_DEFAULT_STORAGE_ENGINE, SQL_DEFAULT_TMP_STORAGE_ENGINE, SQL_FOREIGN_KEY_CHECKS, - SQL_UNIQUE_CHECKS, - SQL_AUTO_INCREMENT_INCREMENT, - SQL_AUTO_INCREMENT_OFFSET, + SQL_GROUP_CONCAT_MAX_LEN, + SQL_GTID_NEXT, + SQL_INNODB_LOCK_WAIT_TIMEOUT, + SQL_INNODB_STRICT_MODE, + SQL_INNODB_TABLE_LOCKS, + SQL_JOIN_BUFFER_SIZE, + SQL_LC_MESSAGES, + SQL_LC_TIME_NAMES, + SQL_LOCK_WAIT_TIMEOUT, + SQL_LONG_QUERY_TIME, + SQL_MAX_EXECUTION_TIME, + SQL_MAX_HEAP_TABLE_SIZE, + SQL_MAX_JOIN_SIZE, + SQL_MAX_SORT_LENGTH, + SQL_OPTIMIZER_PRUNE_LEVEL, + SQL_OPTIMIZER_SEARCH_DEPTH, + SQL_OPTIMIZER_SWITCH, + SQL_PROFILING, + SQL_SORT_BUFFER_SIZE, + SQL_SQL_AUTO_IS_NULL, + SQL_SQL_BIG_SELECTS, + SQL_SQL_MODE, + SQL_SQL_SAFE_UPDATES, + SQL_SQL_SELECT_LIMIT, + SQL_TIME_ZONE, SQL_TIMESTAMP, + SQL_TMP_TABLE_SIZE, + SQL_UNIQUE_CHECKS, SQL_NAME_LAST_HIGH_WM, }; @@ -218,7 +237,7 @@ enum session_status { #ifdef __cplusplus typedef struct { - enum variable_name idx; // index number + enum mysql_variable_name idx; // index number enum session_status status; // what status should be changed after setting this variables bool quote; // if the variable needs to be quoted bool set_transaction; // if related to SET TRANSACTION statement . if false , it will be execute "SET varname = varvalue" . If true, "SET varname varvalue" @@ -226,6 +245,8 @@ typedef struct { bool is_bool; // if true, the variable is a boolean. Special cases should be checked char * set_variable_name; // what variable name (or string) will be used when setting it to backend char * internal_variable_name; // variable name as displayed in admin , WITHOUT "default_" + // Also used in INTERNAL SESSION + // if NULL , MySQL_Variables::MySQL_Variables will set it to set_variable_name during initialization char * default_value; // default value bool is_global_variable; // is it a global variable? } mysql_variable_st; @@ -1027,7 +1048,7 @@ extern __thread unsigned int g_seed; #ifdef PROXYSQL_EXTERN /* typedef struct { - enum variable_name idx; // index number + enum mysql_variable_name idx; // index number enum session_status status; // what status should be changed after setting this variables bool quote; // if the variable needs to be quoted bool set_transaction; // if related to SET TRANSACTION statement . if false , it will be execute "SET varname = varvalue" . If true, "SET varname varvalue" @@ -1035,44 +1056,83 @@ typedef struct { bool is_bool; // if true, the variable is a boolean. Special cases should be checked char * set_variable_name; // what variable name (or string) will be used when setting it to backend char * internal_variable_name; // variable name as displayed in admin , WITHOUT "default_" + // Also used in INTERNAL SESSION + // if NULL , MySQL_Variables::MySQL_Variables will set it to set_variable_name during initialization char * default_value; // default value bool is_global_variable; // is it a global variable? } mysql_variable_st; */ /* NOTE: - make special ATTENTION that the order in variable_name + make special ATTENTION that the order in mysql_variable_name and mysql_tracked_variables[] is THE SAME + NOTE: + MySQL_Variables::MySQL_Variables() has a built-in check to make sure that the order is correct, + and that variables are in alphabetical order */ mysql_variable_st mysql_tracked_variables[] { - { SQL_CHARACTER_SET, SETTING_CHARSET, false, true, false, false, (char *)"charset", (char *)"charset", (char *)"utf8" , true} , // should be before SQL_CHARACTER_SET_RESULTS - { SQL_CHARACTER_ACTION, session_status___NONE,false, false, false, false, (char *)"action", (char *)"action", (char *)"1" , false} , - { SQL_SET_NAMES, SETTING_SET_NAMES, false, false, false, false, (char *)"names", (char *)"names", (char *)"DEFAULT" , false} , + { SQL_CHARACTER_SET, SETTING_CHARSET, false, true, false, false, (char *)"charset", (char *)"charset", (char *)"utf8" , true} , // should be before SQL_CHARACTER_SET_RESULTS + { SQL_CHARACTER_ACTION, session_status___NONE,false, false, false, false, (char *)"action", (char *)"action", (char *)"1" , false} , + { SQL_SET_NAMES, SETTING_SET_NAMES, false, false, false, false, (char *)"names", (char *)"names", (char *)"DEFAULT" , false} , { SQL_CHARACTER_SET_RESULTS, SETTING_VARIABLE, false, false, false, false, (char *)"character_set_results", (char *)"character_set_results", (char *)"utf8" , false} , { SQL_CHARACTER_SET_CONNECTION, SETTING_VARIABLE, false, false, false, false, (char *)"character_set_connection", (char *)"character_set_connection", (char *)"utf8", false } , { SQL_CHARACTER_SET_CLIENT, SETTING_VARIABLE, false, false, false, false, (char *)"character_set_client", (char *)"character_set_client", (char *)"utf8" , false} , { SQL_CHARACTER_SET_DATABASE, SETTING_VARIABLE, false, false, false, false, (char *)"character_set_database", (char *)"character_set_database", (char *)"utf8" , false} , { SQL_ISOLATION_LEVEL, SETTING_ISOLATION_LEVEL, false, true, false, false, (char *)"SESSION TRANSACTION ISOLATION LEVEL", (char *)"isolation_level", (char *)"READ COMMITTED" , false} , + // NOTE: we also need support for transaction_read_only session variable { SQL_TRANSACTION_READ, SETTING_TRANSACTION_READ, false, true, false, false, (char *)"SESSION TRANSACTION READ", (char *)"transaction_read", (char *)"WRITE" , false} , - { SQL_COLLATION_CONNECTION, SETTING_VARIABLE, true, false, false, false, (char *)"collation_connection", (char *)"collation_connection", (char *)"utf8_general_ci" , true} , - { SQL_NET_WRITE_TIMEOUT, SETTING_VARIABLE, false, false, true, false, (char *)"net_write_timeout", (char *)"net_write_timeout", (char *)"60" , false} , - { SQL_LOG_BIN, SETTING_VARIABLE, false, false, false, false, (char *)"sql_log_bin", (char *)"sql_log_bin", (char *)"1" , false} , - { SQL_WSREP_SYNC_WAIT, SETTING_VARIABLE, false, false, true, false, (char *)"wsrep_sync_wait", (char *)"wsrep_sync_wait", (char *)"0" , false} , - { SQL_NAME_LAST_LOW_WM, SETTING_VARIABLE, false, false, true, false, (char *)"placeholder", (char *)"placeholder", (char *)"0" , false} , // this is just a placeholder to separate the previous index from the next block - { SQL_SAFE_UPDATES, SETTING_VARIABLE, true, false, false, true, (char *)"sql_safe_updates", (char *)"sql_safe_updates", (char *)"OFF" , false} , - { SQL_SELECT_LIMIT, SETTING_VARIABLE, false, false, true, false, (char *)"sql_select_limit", (char *)"sql_select_limit", (char *)"DEFAULT" , false} , - { SQL_SQL_MODE, SETTING_VARIABLE, true, false, false, false, (char *)"sql_mode" , (char *)"sql_mode" , (char *)"" , false} , - { SQL_TIME_ZONE, SETTING_VARIABLE, true, false, false, false, (char *)"time_zone", (char *)"time_zone", (char *)"SYSTEM" , false} , - { SQL_SQL_AUTO_IS_NULL, SETTING_VARIABLE, true, false, false, true, (char *)"sql_auto_is_null", (char *)"sql_auto_is_null", (char *)"OFF" , false} , - { SQL_MAX_JOIN_SIZE, SETTING_VARIABLE, false, false, true, false, (char *)"max_join_size", (char *)"max_join_size", (char *)"18446744073709551615" , false} , - { SQL_GROUP_CONCAT_MAX_LEN, SETTING_VARIABLE, false, false, true, false, (char *)"group_concat_max_len", (char *)"group_concat_max_len", (char *)"1024" , false} , - { SQL_DEFAULT_STORAGE_ENGINE, SETTING_VARIABLE, true, false, false, false, (char *)"default_storage_engine", NULL, (char *)"" , false} , - { SQL_DEFAULT_TMP_STORAGE_ENGINE, SETTING_VARIABLE, true, false, false, false, (char *)"default_tmp_storage_engine", NULL, (char *)"" , false} , - { SQL_FOREIGN_KEY_CHECKS, SETTING_VARIABLE, true, false, false, true, (char *)"foreign_key_checks", NULL, (char *)"" , false} , - { SQL_UNIQUE_CHECKS, SETTING_VARIABLE, true, false, false, true, (char *)"unique_checks", NULL, (char *)"" , false} , - { SQL_AUTO_INCREMENT_INCREMENT, SETTING_VARIABLE, true, false, true, false, (char *)"auto_increment_increment", NULL, (char *)"" , false} , - { SQL_AUTO_INCREMENT_OFFSET, SETTING_VARIABLE, true, false, true, false, (char *)"auto_increment_offset", NULL, (char *)"" , false} , - { SQL_TIMESTAMP, SETTING_VARIABLE, true, false, true, false, (char *)"timestamp", NULL, (char *)"" , false} , - //{ SQL_, SETTING_VARIABLE, true, false, false, false, (char *)"", NULL, (char *)"" , false} , + { SQL_COLLATION_CONNECTION, SETTING_VARIABLE, true, false, false, false, (char *)"collation_connection", (char *)"collation_connection", (char *)"utf8_general_ci" , true} , +// { SQL_NET_WRITE_TIMEOUT, SETTING_VARIABLE, false, false, true, false, (char *)"net_write_timeout", (char *)"net_write_timeout", (char *)"60" , false} , + { SQL_LOG_BIN, SETTING_VARIABLE, false, false, false, false, (char *)"sql_log_bin", (char *)"sql_log_bin", (char *)"1" , false} , + { SQL_WSREP_SYNC_WAIT, SETTING_VARIABLE, false, false, true, false, (char *)"wsrep_sync_wait", (char *)"wsrep_sync_wait", (char *)"0" , false} , + { SQL_NAME_LAST_LOW_WM, SETTING_VARIABLE, false, false, true, false, (char *)"placeholder", (char *)"placeholder", (char *)"0" , false} , // this is just a placeholder to separate the previous index from the next block + { SQL_AUTO_INCREMENT_INCREMENT, SETTING_VARIABLE, false, false, true, false, (char *)"auto_increment_increment", NULL, (char *)"" , false} , + { SQL_AUTO_INCREMENT_OFFSET, SETTING_VARIABLE, false, false, true, false, (char *)"auto_increment_offset", NULL, (char *)"" , false} , + { SQL_BIG_TABLES, SETTING_VARIABLE, true, false, false, true, ( char *)"big_tables", NULL, (char *)"" , false} , + { SQL_DEFAULT_STORAGE_ENGINE, SETTING_VARIABLE, true, false, false, false, (char *)"default_storage_engine", NULL, (char *)"" , false} , + { SQL_DEFAULT_TMP_STORAGE_ENGINE, SETTING_VARIABLE, true, false, false, false, (char *)"default_tmp_storage_engine", NULL, (char *)"" , false} , + { SQL_FOREIGN_KEY_CHECKS, SETTING_VARIABLE, true, false, false, true, (char *)"foreign_key_checks", NULL, (char *)"" , false} , + { SQL_GROUP_CONCAT_MAX_LEN, SETTING_VARIABLE, false, false, true, false, (char *)"group_concat_max_len", NULL, (char *)"" , false} , + { SQL_GTID_NEXT, SETTING_VARIABLE, true, false, false, false, (char *)"gtid_next", NULL, (char *)"" , true} , + { SQL_INNODB_LOCK_WAIT_TIMEOUT, SETTING_VARIABLE, false, false, true, false, (char *)"innodb_lock_wait_timeout", NULL, (char *)"" , false} , + { SQL_INNODB_STRICT_MODE, SETTING_VARIABLE, true, false, false, true, ( char *)"innodb_strict_mode", NULL, (char *)"" , false} , + { SQL_INNODB_TABLE_LOCKS, SETTING_VARIABLE, true, false, false, true, ( char *)"innodb_table_locks", NULL, (char *)"" , false} , + { SQL_JOIN_BUFFER_SIZE, SETTING_VARIABLE, false, false, true, false, (char *)"join_buffer_size", NULL, (char *)"" , false} , + { SQL_LC_MESSAGES, SETTING_VARIABLE, true, false, false, false, (char *)"lc_messages", NULL, (char *)"" , false} , + { SQL_LC_TIME_NAMES, SETTING_VARIABLE, true, false, false, false, (char *)"lc_time_names", NULL, (char *)"" , false} , + { SQL_LOCK_WAIT_TIMEOUT, SETTING_VARIABLE, false, false, true, false, (char *)"lock_wait_timeout", NULL, (char *)"" , false} , + { SQL_LONG_QUERY_TIME, SETTING_VARIABLE, false, false, true, false, (char *)"long_query_time", NULL, (char *)"" , false} , + { SQL_MAX_EXECUTION_TIME, SETTING_VARIABLE, false, false, true, false, (char *)"max_execution_time", NULL, (char *)"" , false} , + { SQL_MAX_HEAP_TABLE_SIZE, SETTING_VARIABLE, false, false, true, false, (char *)"max_heap_table_size", NULL, (char *)"18446744073709547520" , false} , + { SQL_MAX_JOIN_SIZE, SETTING_VARIABLE, false, false, true, false, (char *)"max_join_size", NULL, (char *)"18446744073709551615" , false} , + { SQL_MAX_SORT_LENGTH, SETTING_VARIABLE, false, false, true, false, (char *)"max_sort_length", NULL, (char *)"" , false} , + { SQL_OPTIMIZER_PRUNE_LEVEL, SETTING_VARIABLE, false, false, true, false, (char *)"optimizer_prune_level", NULL, (char *)"" , false} , + { SQL_OPTIMIZER_SEARCH_DEPTH, SETTING_VARIABLE, false, false, true, false, (char *)"optimizer_search_depth", NULL, (char *)"" , false} , + { SQL_OPTIMIZER_SWITCH, SETTING_VARIABLE, true, false, false, false, (char *)"optimizer_switch", NULL, (char *)"" , false} , + { SQL_PROFILING, SETTING_VARIABLE, true, false, false, true, ( char *)"profiling", NULL, (char *)"" , false} , + { SQL_SORT_BUFFER_SIZE, SETTING_VARIABLE, false, false, true, false, (char *)"sort_buffer_size", NULL, (char *)"18446744073709551615" , false} , + { SQL_SQL_AUTO_IS_NULL, SETTING_VARIABLE, true, false, false, true, (char *)"sql_auto_is_null", NULL, (char *)"OFF" , false} , + { SQL_SQL_BIG_SELECTS, SETTING_VARIABLE, true, false, false, true, (char *)"sql_big_selects", NULL, (char *)"OFF" , true} , + { SQL_SQL_MODE, SETTING_VARIABLE, true, false, false, false, (char *)"sql_mode" , NULL, (char *)"" , false} , + { SQL_SQL_SAFE_UPDATES, SETTING_VARIABLE, true, false, false, true, (char *)"sql_safe_updates", NULL, (char *)"OFF" , false} , + { SQL_SQL_SELECT_LIMIT, SETTING_VARIABLE, false, false, true, false, (char *)"sql_select_limit", NULL, (char *)"DEFAULT" , false} , + { SQL_TIME_ZONE, SETTING_VARIABLE, true, false, false, false, (char *)"time_zone", NULL, (char *)"SYSTEM" , false} , + { SQL_TIMESTAMP, SETTING_VARIABLE, true, false, true, false, (char *)"timestamp", NULL, (char *)"" , false} , + { SQL_TMP_TABLE_SIZE, SETTING_VARIABLE, false, false, true, false, (char *)"tmp_table_size", NULL, (char *)"" , false} , + { SQL_UNIQUE_CHECKS, SETTING_VARIABLE, true, false, false, true, (char *)"unique_checks", NULL, (char *)"" , false} , + + /* + variables that will need input validation: + binlog_row_image + + variables that needs special handling: + max_allowed_packet + max_execution_time + session_track_state_change + session_track_system_variables + session_track_transaction_info + */ + + }; #else extern mysql_variable_st mysql_tracked_variables[]; diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 92f89714d..7e3e80d00 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -5480,8 +5480,43 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C } proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection SQL Mode to %s\n", value1.c_str()); } + } else if ( + (var == "default_storage_engine") + || (var == "default_tmp_storage_engine") + ) { + std::string value1 = *values; + std::size_t found_at = value1.find("@"); + if (found_at != std::string::npos) { + unable_to_parse_set_statement(lock_hostgroup); + return false; + } + int idx = SQL_NAME_LAST_HIGH_WM; + for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { + if (mysql_tracked_variables[i].is_number == false && mysql_tracked_variables[i].is_bool == false) { + if (!strcasecmp(var.c_str(), mysql_tracked_variables[i].set_variable_name)) { + idx = mysql_tracked_variables[i].idx; + break; + } + } + } + if (idx != SQL_NAME_LAST_HIGH_WM) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection %s to %s\n", var.c_str(), value1.c_str()); + uint32_t var_hash_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); + if (mysql_variables.client_get_hash(this, mysql_tracked_variables[idx].idx) != var_hash_int) { + if (!mysql_variables.client_set_value(this, mysql_tracked_variables[idx].idx, value1.c_str())) { + return false; + } + } + } // the following two blocks of code will be simplified later - } else if ((var == "sql_auto_is_null") || (var == "sql_safe_updates")) { + } else if ( + (var == "foreign_key_checks") + || (var == "innodb_strict_mode") + || (var == "innodb_table_locks") + || (var == "sql_auto_is_null") + || (var == "sql_safe_updates") + || (var == "unique_checks") + ) { int idx = SQL_NAME_LAST_HIGH_WM; for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { if (mysql_tracked_variables[i].is_bool) { @@ -5496,7 +5531,17 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C return false; } } - } else if ( (var == "sql_select_limit") || (var == "net_write_timeout") || (var == "max_join_size") || (var == "wsrep_sync_wait") || (var == "group_concat_max_len") ) { + } else if ( + (var == "auto_increment_increment") + || (var == "auto_increment_offset") + || (var == "group_concat_max_len") + || (var == "innodb_lock_wait_timeout") + || (var == "join_buffer_size") + || (var == "lock_wait_timeout") + || (var == "max_join_size") + || (var == "sql_select_limit") + || (var == "wsrep_sync_wait") + ) { int idx = SQL_NAME_LAST_HIGH_WM; for (int i = 0 ; i < SQL_NAME_LAST_HIGH_WM ; i++) { if (mysql_tracked_variables[i].is_number) { @@ -5577,6 +5622,19 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C return false; } proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET Time Zone value %s\n", value1.c_str()); + { + // reformat +1:23 to +01:23 + if (value1.length() == 5) { + if (value1[0]=='+' || value1[0]=='-') { + if (value1[2]==':') { + std::string s = std::string(value1,0,1); + s += "0"; + s += std::string(value1,1,4); + value1 = s; + } + } + } + } uint32_t time_zone_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); if (mysql_variables.client_get_hash(this, SQL_TIME_ZONE) != time_zone_int) { if (!mysql_variables.client_set_value(this, SQL_TIME_ZONE, value1.c_str())) diff --git a/lib/MySQL_Variables.cpp b/lib/MySQL_Variables.cpp index 3164c1ef6..aa80d0869 100644 --- a/lib/MySQL_Variables.cpp +++ b/lib/MySQL_Variables.cpp @@ -24,9 +24,33 @@ MySQL_Variables::MySQL_Variables() { ignore_vars.push_back("interactive_timeout"); ignore_vars.push_back("wait_timeout"); ignore_vars.push_back("net_read_timeout"); + ignore_vars.push_back("net_write_timeout"); + ignore_vars.push_back("net_buffer_length"); + ignore_vars.push_back("read_buffer_size"); + ignore_vars.push_back("read_rnd_buffer_size"); // NOTE: This variable has been temporarily ignored. Check issues #3442 and #3441. ignore_vars.push_back("session_track_schema"); variables_regexp = ""; + for (auto i = 0; i < SQL_NAME_LAST_HIGH_WM; i++) { + // we initialized all the internal_variable_name if set to NULL + if (mysql_tracked_variables[i].internal_variable_name == NULL) { + mysql_tracked_variables[i].internal_variable_name = mysql_tracked_variables[i].set_variable_name; + } + } +/* + NOTE: + make special ATTENTION that the order in mysql_variable_name + and mysql_tracked_variables[] is THE SAME + NOTE: + MySQL_Variables::MySQL_Variables() has a built-in check to make sure that the order is correct, + and that variables are in alphabetical order +*/ + for (int i = SQL_NAME_LAST_LOW_WM; i < SQL_NAME_LAST_HIGH_WM; i++) { + assert(i == mysql_tracked_variables[i].idx); + if (i > SQL_NAME_LAST_LOW_WM+1) { + assert(strcmp(mysql_tracked_variables[i].set_variable_name, mysql_tracked_variables[i-1].set_variable_name) > 0); + } + } for (auto i = 0; i < SQL_NAME_LAST_HIGH_WM; i++) { if (i == SQL_CHARACTER_SET || i == SQL_CHARACTER_ACTION || i == SQL_SET_NAMES) { MySQL_Variables::updaters[i] = NULL; @@ -476,7 +500,7 @@ inline bool verify_server_variable(MySQL_Session* session, int idx, uint32_t cli } } // this variable is relevant only if status == SETTING_VARIABLE - session->changing_variable_idx = (enum variable_name)idx; + session->changing_variable_idx = (enum mysql_variable_name)idx; switch(session->status) { // this switch can be replaced with a simple previous_status.push(status), but it is here for readibility case PROCESSING_QUERY: session->previous_status.push(PROCESSING_QUERY); diff --git a/lib/set_parser.cpp b/lib/set_parser.cpp index 99d59779a..e1a83d8a6 100644 --- a/lib/set_parser.cpp +++ b/lib/set_parser.cpp @@ -16,7 +16,7 @@ SetParser::SetParser(std::string nq) { free(query_no_space); } -#define QUOTES "(?:'|\")?" +#define QUOTES "(?:'|\"|`)?" #define SPACES " *" #define NAMES "(NAMES)" #define NAME_VALUE "((?:\\w|\\d)+)" diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 9993439c6..dee3dd58d 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -81,7 +81,7 @@ all: tests .PHONY: clean clean: - rm -f *-t galera_1_timeout_count galera_2_timeout_no_count aurora || true + rm -f *-t galera_1_timeout_count galera_2_timeout_no_count aurora generate_set_session_csv || true WITHGCOVVAR := $(shell echo $(WITHGCOV)) ifeq ($(WITHGCOVVAR),1) @@ -98,7 +98,10 @@ tests: $(patsubst %.cpp,%,$(wildcard *-t.cpp)) setparser_test testgalera: galera_1_timeout_count galera_2_timeout_no_count testaurora: aurora -%-t:%-t.cpp $(TAP_LIBDIR)/libtap.a +set_testing-240.csv: generate_set_session_csv + ./generate_set_session_csv > set_testing-240.csv + +%-t:%-t.cpp $(TAP_LIBDIR)/libtap.a set_testing-240.csv $(CXX) $^ $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o $@ galera_1_timeout_count: galera_1_timeout_count.cpp $(TAP_LIBDIR)/libtap.a @@ -107,6 +110,9 @@ galera_1_timeout_count: galera_1_timeout_count.cpp $(TAP_LIBDIR)/libtap.a 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) $(MYLIBS) -ltap -ldl $(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) $(MYLIBS) -ltap -ldl $(STATIC_LIBS) -o aurora -DGITVERSION=\"$(GIT_VERSION)\" diff --git a/test/tap/tests/generate_set_session_csv.cpp b/test/tap/tests/generate_set_session_csv.cpp index 2ac80243d..dff0b4067 100644 --- a/test/tap/tests/generate_set_session_csv.cpp +++ b/test/tap/tests/generate_set_session_csv.cpp @@ -1,8 +1,9 @@ #include +#include #include #include #include - +#include std::vector bool_values = { "0", "1", @@ -11,10 +12,15 @@ std::vector bool_values = { "`ON`", "`OFF`", "\"ON\"", "\"OFF\"", }; + std::vector int_values = { 10, 20, 100, 1010, 1234, 3456, 7890, 34564, 68100, 123456, 456123 }; +std::vector int_values_small = { + 13, 32, 110, 310, 434, 558, 789 +}; + std::unordered_map fixed_sets = { { "SET session transaction read only", "{'transaction_read_only':'ON'}" }, { "SET session transaction read write", "{'transaction_read_only':'OFF'}" }, @@ -28,13 +34,15 @@ class variable { public: std::string name; int uses_quotes = 0; - bool mix = false; + bool mix = false; // the variable can be set together with other variables bool number = false; + bool is_bool = false; std::vector values; - variable(const std::string& n, bool m, bool is_n) { + variable(const std::string& n, bool m, bool is_n, bool is_b) { name = n; mix = m; number = is_n; + is_bool = is_b; } void add(const std::string& v) { values.push_back(v); @@ -47,7 +55,8 @@ class variable { void add(const std::vector& v, int inc) { for (std::vector::const_iterator it = v.begin() ; it != v.end() ; it++) { int a = *it; - a += inc; + a += inc; // "inc" is a random number passed as argument different for each variable + // to try to avoid that two variables get identical values values.push_back(std::to_string(a)); } } @@ -56,40 +65,201 @@ class variable { std::unordered_map vars; + +void add_value_j(std::string& j, std::string& s, variable *v) { + if (v->is_bool == true) { + if ( + (strcasestr(s.c_str(), "OFF") != NULL) + || + (strcasestr(s.c_str(), "0") != NULL) + ) { + j+= "OFF"; + } else { + if ( + (strcasestr(s.c_str(), "ON") != NULL) + || + (strcasestr(s.c_str(), "1") != NULL) + ) { + j+= "ON"; + } else { + j+= ""; + } + } + return; + } + for (int i=0; iname == "time_zone") { + if (s[i] == '+' || s[i] == '-') { + if (s[i+2] == ':') { + j += s[i]; + j += '0'; + i++; + } + } + } + if (s[i] == '"') + j+= "\\"; + j+= s[i]; + } +} + + int main() { srand(1); - vars["sql_log_bin"] = new variable("sql_log_bin", false, false); + vars["sql_log_bin"] = new variable("sql_log_bin", false, false, true); vars["sql_log_bin"]->add(bool_values); - vars["sql_safe_updates"] = new variable("sql_safe_updates", true, false); + vars["sql_safe_updates"] = new variable("sql_safe_updates", true, false, true); vars["sql_safe_updates"]->add(bool_values); - vars["sql_auto_is_null"] = new variable("sql_auto_is_null", true, false); +// vars["wsrep_sync_wait"] = new variable("wsrep_sync_wait", true, false); +// vars["wsrep_sync_wait"]->add(bool_values); + vars["sql_auto_is_null"] = new variable("sql_auto_is_null", true, false, true); vars["sql_auto_is_null"]->add(bool_values); - vars["foreign_key_checks"] = new variable("foreign_key_checks", true, false); + vars["foreign_key_checks"] = new variable("foreign_key_checks", true, false, true); vars["foreign_key_checks"]->add(bool_values); - vars["unique_checks"] = new variable("unique_checks", true, false); + vars["unique_checks"] = new variable("unique_checks", true, false, true); vars["unique_checks"]->add(bool_values); + vars["innodb_lock_wait_timeout"] = new variable("innodb_lock_wait_timeout", true, true, false); + vars["innodb_lock_wait_timeout"]->add(int_values_small, 34); + vars["innodb_lock_wait_timeout"]->add(int_values, 117); + vars["innodb_strict_mode"] = new variable("innodb_strict_mode", true, false, true); + vars["innodb_strict_mode"]->add(bool_values); + vars["innodb_table_locks"] = new variable("innodb_table_locks", true, false, true); + vars["innodb_table_locks"]->add(bool_values); + //vars[""] = new variable(""); //vars[""]->add(bool_values); - vars["sql_select_limit"] = new variable("sql_select_limit", true, true); - vars["sql_select_limit"]->add(int_values, 5); - vars["group_concat_max_len"] = new variable("group_concat_max_len", true, true); + + vars["auto_increment_increment"] = new variable("auto_increment_increment", true, true, false); + vars["auto_increment_increment"]->add(int_values_small, 10); + vars["auto_increment_offset"] = new variable("auto_increment_offset", true, true, false); + vars["auto_increment_offset"]->add(int_values_small, 20); + vars["sql_select_limit"] = new variable("sql_select_limit", true, true, false); + vars["sql_select_limit"]->add(int_values_small, 50); + vars["sql_select_limit"]->add(int_values, 50); + vars["group_concat_max_len"] = new variable("group_concat_max_len", true, true, false); + vars["group_concat_max_len"]->add(int_values_small, 123); vars["group_concat_max_len"]->add(int_values, 123); - vars["max_join_size"] = new variable("max_join_size", true, true); + vars["join_buffer_size"] = new variable("join_buffer_size", true, true, false); + vars["join_buffer_size"]->add(int_values, 1028); + { + // join_buffer_size uses blocks of 128 , so we need to round it + std::vector& vals = vars["join_buffer_size"]->values; + for (std::vector::iterator it = vals.begin(); it != vals.end(); it++) { + int a = std::stoi(*it); + a = a/128; + a *= 128; + *it = std::to_string(a); + } + } + vars["lock_wait_timeout"] = new variable("lock_wait_timeout", true, true, false); + vars["lock_wait_timeout"]->add(int_values_small, 321); + vars["max_join_size"] = new variable("max_join_size", true, true, false); vars["max_join_size"]->add(int_values, 1000); vars["max_join_size"]->add("18446744073709551615"); + vars["session_track_gtids"] = new variable("session_track_gtids", true, true, false); + vars["session_track_gtids"]->add("OWN_GTID"); +// vars["session_track_gtids"]->add("OFF"); +// vars["session_track_gtids"]->add("ALL_GTID"); - vars["time_zone"] = new variable("time_zone", true, false); - vars["time_zone"]->add(std::vector {"+01:00", "`+02:15`", "\"+03:30\""}); - vars["time_zone"]->add(std::vector {"+04:45", "`+05:00`", "\"+06:10\""}); - vars["time_zone"]->add(std::vector {"-1:10", "-03:33", "\"-04:56\""}); - vars["sql_mode"] = new variable("sql_mode", true, false); + vars["time_zone"] = new variable("time_zone", true, false, false); + vars["time_zone"]->add(std::vector {"'+01:00'", "`+02:15`", "\"+03:30\""}); + vars["time_zone"]->add(std::vector {"'+04:45'", "`+05:00`", "\"+06:10\""}); + vars["time_zone"]->add(std::vector {"'-1:10'", "`-03:33`", "\"-04:56\""}); + vars["time_zone"]->add(std::vector {"'+02:45'", "`+7:00`", "\"+06:10\""}); + vars["time_zone"]->add(std::vector {"'-11:10'", "`-11:33`", "\"-04:56\""}); + vars["sql_mode"] = new variable("sql_mode", true, false, false); + vars["sql_mode"]->add(std::vector {"'traditional'", "TRADITIONAL", "''"}); + vars["sql_mode"]->add(std::vector {"'PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'", "\"PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION\""}); + vars["sql_mode"]->add(std::vector {"ALLOW_INVALID_DATES", "'ALLOW_INVALID_DATES'", "\"ALLOW_INVALID_DATES\""}); + vars["sql_mode"]->add(std::vector {"NO_ENGINE_SUBSTITUTION", "'NO_ENGINE_SUBSTITUTION'", "\"NO_ENGINE_SUBSTITUTION\""}); + vars["sql_mode"]->add(std::vector {"concat(@@sql_mode,',STRICT_TRANS_TABLES')"}); + vars["sql_mode"]->add(std::vector {"CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO')"}); - for (int i=0; i<1000; i++) { + + vars["default_storage_engine"] = new variable("default_storage_engine", true, false, false); + { + std::vector engines = {"InnoDB", "MEMORY", "MyISAM", "BLACKHOLE"}; + for (std::vector::iterator it = engines.begin(); it != engines.end(); it++) { + vars["default_storage_engine"]->add(*it); + std::string s; + s = "\"" + *it + "\""; + vars["default_storage_engine"]->add(s); + s = "'" + *it + "'"; + vars["default_storage_engine"]->add(s); + s = "`" + *it + "`"; + vars["default_storage_engine"]->add(s); + } + } + vars["default_tmp_storage_engine"] = new variable("default_tmp_storage_engine", true, false, false); + vars["default_tmp_storage_engine"]->add(vars["default_storage_engine"]->values); + +/* +example: +"SET sql_mode='NO_ENGINE_SUBSTITUTION', sql_select_limit=3030, session_track_gtids=OWN_GTID; SET max_join_size=10000; ", "{'sql_mode':'NO_ENGINE_SUBSTITUTION','sql_select_limit':'3030', 'max_join_size':'10000', 'session_track_gtids':'OWN_GTID'}" +*/ + + variable * vararray[vars.size()]; + { + unsigned int i = 0; + for (std::unordered_map::iterator it = vars.begin() ; it != vars.end() ; it++) { + vararray[i] = it->second; + i++; + } + } + for (int i=0; i<10000; i++) { int ne = rand()%4+1; - int r1 = rand()%vars.size(); + variable * va[ne]; + for (int ine = 0; ine < ne; ) { + int r1 = rand()%vars.size(); + variable *v = vararray[r1]; + bool there = false; + for (unsigned int tl = 0; tl < ine && there == false; tl++) { + if (va[tl] == v) there = true; + } + if (there == false) { + if (v->mix == false) { + if (ne == 1) { + va[ine++] = v; + } + } else { + va[ine++] = v; + } + } + } + std::string query = "SET "; + std::string j = "{"; + //std::cout << "\"SET "; + for (int ine = 0; ine < ne; ine++) { + variable *v = va[ine]; + int r = rand()%(v->values.size()); + std::string s = v->values[r]; + //std::cout << v->name << "="; + query += v->name + "="; + if (s[0] == '"') { + std::string s1 = std::string(s,0,s.length()-1); + //std::cout << "\\" << s1 << "\\\""; + query+= "\\" + s1 + "\\\""; + } else { + query += s; + } + j += "\"" + v->name + "\":\""; + add_value_j(j,s,v); + if (ine != ne -1) { + query+= ", "; + j+= "\", "; + } else { + query+= ";"; + j+= "\"}"; + } + } + std::cout << "{\"query\":\"" << query << "\", \"expected\":" << j << ", \"reset\":{}}" << std::endl; } return 0; diff --git a/test/tap/tests/set_testing-240-t.cpp b/test/tap/tests/set_testing-240-t.cpp new file mode 100644 index 000000000..b279733bd --- /dev/null +++ b/test/tap/tests/set_testing-240-t.cpp @@ -0,0 +1,410 @@ +/** + * @file set_testing-t.cpp + * @brief This file tests multiple settings combinations for MySQL variables, and checks that they are + * actually being tracked correctly. + * @details The test input is a 'csv' file with name 'set_testing-t.csv'. The file format consists in + * two primary columns which specifies the variables to set (first) and the expected result of setting + * those variables (second), and an optional third column which hold variables that shouldn't be checked + * anymore after the 'SET STATEMENTS' from the same line are executed. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json.hpp" +#include "re2/re2.h" +#include "re2/regexp.h" + +#include "tap.h" +#include "utils.h" +#include "command_line.h" + + + +int queries_per_connections=1; +unsigned int num_threads=1; +int count=0; +char *username=NULL; +char *password=NULL; +char *host=(char *)"localhost"; +int port=3306; +int multiport=1; +char *schema=(char *)"information_schema"; +int silent = 0; +int sysbench = 0; +int local=0; +int queries=0; +int uniquequeries=0; +int histograms=-1; + +bool is_mariadb = false; +bool is_cluster = false; +unsigned int g_connect_OK=0; +unsigned int g_connect_ERR=0; +unsigned int g_select_OK=0; +unsigned int g_select_ERR=0; + +unsigned int g_passed=0; +unsigned int g_failed=0; + +unsigned int status_connections = 0; +unsigned int connect_phase_completed = 0; +unsigned int query_phase_completed = 0; + +__thread int g_seed; +std::mutex mtx_; + +std::vector forgotten_vars {}; + +#include "set_testing.h" + + +/* TODO + add support for variables with values out of range, + for example setting auto_increment_increment to 100000 +*/ + +void * my_conn_thread(void *arg) { + g_seed = time(NULL) ^ getpid() ^ pthread_self(); + unsigned int select_OK=0; + unsigned int select_ERR=0; + int i, j; + MYSQL **mysqlconns=(MYSQL **)malloc(sizeof(MYSQL *)*count); + std::vector varsperconn(count); + + if (mysqlconns==NULL) { + exit(EXIT_FAILURE); + } + + std::vector cs = {"latin1", "utf8", "utf8mb4", "latin2", "latin7"}; + + for (i=0; i lock(mtx_); + skip(1, "connections mysql[%p] proxysql[%s], command [%s]", mysql, paddress.c_str(), testCases[r2].command.c_str()); + continue; + } + if (strstr(testCases[r2].command.c_str(),"sql_log_bin")) { + std::lock_guard lock(mtx_); + skip(1, "connections: mysql[%p] proxysql[%s], command [%s]", mysql, paddress.c_str(), testCases[r2].command.c_str()); + continue; + } + } + diag("Thread_id: %lu, random number: %d . Query/ies: %s", mysql->thread_id, r2, testCases[r2].command.c_str()); + std::vector commands = split(testCases[r2].command.c_str(), ';'); + for (auto c : commands) { + if (mysql_query(mysql, c.c_str())) { + if (silent==0) { + fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", c.c_str(), mysql_errno(mysql), mysql_error(mysql)); + } + } else { + MYSQL_RES *result = mysql_store_result(mysql); + mysql_free_result(result); + select_OK++; + __sync_fetch_and_add(&g_select_OK,1); + } + } + + for (auto& el : testCases[r2].expected_vars.items()) { + if (el.key() == "transaction_isolation") { + if (is_mariadb) { + vars["tx_isolation"] = el.value(); + } + else { + vars[el.key()] = el.value(); + } + } + else if (el.key() == "session_track_gtids") { + if (!is_mariadb) { + vars[el.key()] = el.value(); + } + } + else if (el.key() == "wsrep_sync_wait") { + if (is_cluster) { + vars[el.key()] = el.value(); + } + } + else if (el.key() == "transaction_read_only") { + if (is_mariadb) { + vars["tx_read_only"] = el.value(); + } else { + vars[el.key()] = el.value(); + } + } + else { + vars[el.key()] = el.value(); + } + } + + int sleepDelay = fastrand()%100; + usleep(sleepDelay * 1000); + + char query[128]; + sprintf(query, "SELECT /* %p %s */ %d;", mysql, paddress.c_str(), sleepDelay); + if (mysql_query(mysql,query)) { + select_ERR++; + __sync_fetch_and_add(&g_select_ERR,1); + } else { + MYSQL_RES *result = mysql_store_result(mysql); + mysql_free_result(result); + select_OK++; + __sync_fetch_and_add(&g_select_OK,1); + } + + json mysql_vars; + queryVariables(mysql, mysql_vars, paddress); + + json proxysql_vars; + queryInternalStatus(mysql, proxysql_vars, paddress); + + //diag("MySQL vars: %lu , ProxySQL vars: %lu" , mysql_vars.size(), proxysql_vars.size()); + + if (!testCases[r2].reset_vars.empty()) { + for (const auto& var : testCases[r2].reset_vars) { + if (std::find(forgotten_vars.begin(), forgotten_vars.end(), var) == forgotten_vars.end()) { + forgotten_vars.push_back(var); + } + } + } + + bool testPassed = true; + int variables_tested = 0; + for (auto& el : vars.items()) { + auto k = mysql_vars.find(el.key()); + auto s = proxysql_vars["conn"].find(el.key()); + + if (std::find(forgotten_vars.begin(), forgotten_vars.end(), el.key()) != forgotten_vars.end()) { + continue; + } + + if (k == mysql_vars.end()) + fprintf(stderr, "Variable %s->%s in mysql resultset was not found.\nmysql data : %s\nproxysql data: %s\ncsv data %s\n", + el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str()); + + if (s == proxysql_vars["conn"].end()) + fprintf(stderr, "Variable %s->%s in proxysql resultset was not found.\nmysql data : %s\nproxysql data: %s\ncsv data %s\n", + el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str()); + + bool verified_special_sqlmode = false; + bool special_sqlmode = false; + + if (el.key() == "sql_mode") { + if (!el.value().is_string()) { + diag("Invalid value for 'sql_mode' found. Provided value should be of 'string' type"); + exit(EXIT_FAILURE); + } + + if (k.value() != el.value()) { // different in mysql + std::string e_val { el.value() }; + std::string k_val { k.value() }; + std::string s_val { s.value() }; + if (el.value() == s.value()) { // but same in proxysql + std::string str_val { el.value() }; + if (strcasecmp(str_val.c_str(), "TRADITIONAL")==0) { + if (k.value() == "STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION") { + special_sqlmode = true; + verified_special_sqlmode = true; + } + } else { + if (strcasestr(e_val.c_str(), "sql_mode") != NULL) { + // simplified + special_sqlmode = true; + verified_special_sqlmode = true; + } +/* + re2::RE2::Options options(RE2::Quiet); + options.set_case_sensitive(false); + options.set_longest_match(false); + re2::RE2 concat_re("^CONCAT\\((|@@|@@session\\.)SQL_MODE,\"(.*)\"\\)", options); + re2::StringPiece sp_input(str_val); + + std::string f_match {}; + std::string s_match {}; + + re2::RE2::Consume(&sp_input, concat_re, &f_match, &s_match); + + if (!s_match.empty()) { + special_sqlmode = true; + + // remove the initial 'comma' if exists + if (s_match[0] == ',') { + s_match = s_match.substr(1, std::string::npos); + } + + std::string k_str_val { k.value() }; + verified_special_sqlmode = + strcasestr(k_str_val.c_str(), s_match.c_str()) != NULL; + } +*/ + } + } + } + } + + if ( + (special_sqlmode == true && verified_special_sqlmode == false) || + (k == mysql_vars.end()) || + (s == proxysql_vars["conn"].end()) || + (special_sqlmode == false && + (el.key() != "session_track_gtids" && (k.value() != el.value() || s.value() != el.value())) || + (el.key() == "session_track_gtids" && !check_session_track_gtids(el.value(), s.value(), k.value())) + ) + ) { + if (el.key() == "wsrep_sync_wait" && k == mysql_vars.end() && (s.value() == el.value())) { + variables_tested++; + } else { + __sync_fetch_and_add(&g_failed, 1); + testPassed = false; + fprintf(stderr, "Test failed for this case %s->%s.\n\nmysql data [%lu]: %s\n\n proxysql data [%lu]: %s\n\n csv data %s\n\n\n", + el.value().dump().c_str(), el.key().c_str(), + mysql_vars.size(), mysql_vars.dump().c_str(), + proxysql_vars.size(), proxysql_vars.dump().c_str(), + vars.dump().c_str()); + diag("FAILED FOR: connections mysql[%p] proxysql[%s], thread_id [%lu], command [%s]", mysql, paddress.c_str(), mysql->thread_id, testCases[r2].command.c_str()); + //ok(testPassed, "connections mysql[%p] proxysql[%s], thread_id [%lu], command [%s]", mysql, paddress.c_str(), mysql->thread_id, testCases[r2].command.c_str()); + // In case of failing test, exit completely. + //exit(EXIT_FAILURE); + } + } else { + variables_tested++; + } + } + { + std::lock_guard lock(mtx_); + ok(testPassed, "connections mysql[%p] proxysql[%s], thread_id [%lu], variables_tested [%d], command [%s]", mysql, paddress.c_str(), mysql->thread_id, variables_tested, testCases[r2].command.c_str()); + } + } + __sync_fetch_and_add(&query_phase_completed,1); + + return NULL; +} + + +int main(int argc, char *argv[]) { + CommandLine cl; + + if(cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return exit_status(); + } + + std::string fileName2(std::string(cl.workdir) + "/set_testing-240.csv"); + + MYSQL* mysql = mysql_init(NULL); + if (!mysql) + return exit_status(); + if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysql)); + return exit_status(); + } + MYSQL_QUERY(mysql, "select @@version"); + MYSQL_RES *result = mysql_store_result(mysql); + MYSQL_ROW row; + while ((row = mysql_fetch_row(result))) + { + if (strstr(row[0], "Maria")) { + is_mariadb = true; + } + else { + is_mariadb = false; + } + + char* first_dash = strstr(row[0], "-"); + if (!first_dash || !strstr(first_dash+1, "-")) { + is_cluster = false; + } else { + // FIXME: we need a better version detection + is_cluster = true; + } + } + + mysql_free_result(result); + mysql_close(mysql); + + num_threads = 10; + queries_per_connections = 10; + count = 10; + username = cl.username; + password = cl.password; + host = cl.host; + port = cl.port; + + if (!readTestCasesJSON(fileName2)) { + fprintf(stderr, "Cannot read %s\n", fileName2.c_str()); + return exit_status(); + } + queries = 1500; + //queries = testCases.size(); + plan(queries * num_threads); + + if (strcmp(host,"localhost")==0) { + local = 1; + } + if (uniquequeries == 0) { + if (queries) uniquequeries=queries; + } + if (uniquequeries) { + 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 split(const std::string& s, char delimiter) { std::vector tokens; @@ -96,7 +97,7 @@ struct cpu_timer }; unsigned long long begin; }; - +*/ std::string bn = ""; int queries_per_connections=1; unsigned int num_threads=1; @@ -131,283 +132,9 @@ unsigned int query_phase_completed = 0; __thread int g_seed; std::mutex mtx_; - -inline int fastrand() { - g_seed = (214013*g_seed+2531011); - return (g_seed>>16)&0x7FFF; -} - -void parseResultJsonColumn(MYSQL_RES *result, json& j) { - if(!result) return; - MYSQL_ROW row; - - while ((row = mysql_fetch_row(result))) - j = json::parse(row[0]); -} - -void parseResult(MYSQL_RES *result, json& j) { - if(!result) return; - MYSQL_ROW row; - - while ((row = mysql_fetch_row(result))) { - j[row[0]] = row[1]; - } -} - -void dumpResult(MYSQL_RES *result) { - if(!result) return; - MYSQL_ROW row; - - int num_fields = mysql_num_fields(result); - - while ((row = mysql_fetch_row(result))) - { - for(int i = 0; i < num_fields; i++) - { - printf("%s ", row[i] ? row[i] : "NULL"); - } - printf("\n"); - } -} - -void queryVariables(MYSQL *mysql, json& j) { - std::stringstream query; - if (is_mariadb) { - query << "SELECT /* mysql " << mysql << " */ lower(variable_name), variable_value FROM information_schema.session_variables WHERE variable_name IN " - " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " - " 'sql_safe_updates', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " - " 'sql_select_limit', 'character_set_results', 'tx_isolation', 'tx_read_only', " - " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'group_concat_max_len');"; - } - if (is_cluster) { - query << "SELECT /* mysql " << mysql << " */ * FROM performance_schema.session_variables WHERE variable_name IN " - " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " - " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " - " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', " - " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'wsrep_sync_wait', 'group_concat_max_len');"; - } - if (!is_mariadb && !is_cluster) { - query << "SELECT /* mysql " << mysql << " */ * FROM performance_schema.session_variables WHERE variable_name IN " - " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " - " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " - " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', " - " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'group_concat_max_len');"; - } - //fprintf(stderr, "TRACE : QUERY 3 : variables %s\n", query.str().c_str()); - if (mysql_query(mysql, query.str().c_str())) { - if (silent==0) { - fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query.str().c_str(), mysql_errno(mysql), mysql_error(mysql)); - } - } else { - MYSQL_RES *result = mysql_store_result(mysql); - parseResult(result, j); - - mysql_free_result(result); - __sync_fetch_and_add(&g_select_OK,1); - } -} - -void queryInternalStatus(MYSQL *mysql, json& j) { - char *query = (char*)"PROXYSQL INTERNAL SESSION"; - - //fprintf(stderr, "TRACE : QUERY 4 : variables %s\n", query); - if (mysql_query(mysql, query)) { - if (silent==0) { - fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query, mysql_errno(mysql), mysql_error(mysql)); - } - } else { - MYSQL_RES *result = mysql_store_result(mysql); - parseResultJsonColumn(result, j); - - mysql_free_result(result); - __sync_fetch_and_add(&g_select_OK,1); - } - - // value types in mysql and in proxysql are different - // we should convert proxysql values to mysql format to compare - for (auto& el : j.items()) { - if (el.key() == "conn") { - std::string sql_log_bin_value; - - // sql_log_bin {0|1} - if (el.value()["sql_log_bin"] == 1) { - el.value().erase("sql_log_bin"); - j["conn"]["sql_log_bin"] = "ON"; - } - else if (el.value()["sql_log_bin"] == 0) { - el.value().erase("sql_log_bin"); - j["conn"]["sql_log_bin"] = "OFF"; - } - - // sql_auto_is_null {true|false} - if (!el.value()["sql_auto_is_null"].dump().compare("ON") || - !el.value()["sql_auto_is_null"].dump().compare("1") || - !el.value()["sql_auto_is_null"].dump().compare("on") || - el.value()["sql_auto_is_null"] == 1) { - el.value().erase("sql_auto_is_null"); - j["conn"]["sql_auto_is_null"] = "ON"; - } - else if (!el.value()["sql_auto_is_null"].dump().compare("OFF") || - !el.value()["sql_auto_is_null"].dump().compare("0") || - !el.value()["sql_auto_is_null"].dump().compare("off") || - el.value()["sql_auto_is_null"] == 0) { - el.value().erase("sql_auto_is_null"); - j["conn"]["sql_auto_is_null"] = "OFF"; - } - - // completely remove autocommit test -/* - // autocommit {true|false} - if (!el.value()["autocommit"].dump().compare("ON") || - !el.value()["autocommit"].dump().compare("1") || - !el.value()["autocommit"].dump().compare("on") || - el.value()["autocommit"] == 1) { - el.value().erase("autocommit"); - j["conn"]["autocommit"] = "ON"; - } - else if (!el.value()["autocommit"].dump().compare("OFF") || - !el.value()["autocommit"].dump().compare("0") || - !el.value()["autocommit"].dump().compare("off") || - el.value()["autocommit"] == 0) { - el.value().erase("autocommit"); - j["conn"]["autocommit"] = "OFF"; - } -*/ - // sql_safe_updates - if (!el.value()["sql_safe_updates"].dump().compare("\"ON\"") || - !el.value()["sql_safe_updates"].dump().compare("\"1\"") || - !el.value()["sql_safe_updates"].dump().compare("\"on\"") || - el.value()["sql_safe_updates"] == 1) { - el.value().erase("sql_safe_updates"); - j["conn"]["sql_safe_updates"] = "ON"; - } - else if (!el.value()["sql_safe_updates"].dump().compare("\"OFF\"") || - !el.value()["sql_safe_updates"].dump().compare("\"0\"") || - !el.value()["sql_safe_updates"].dump().compare("\"off\"") || - el.value()["sql_safe_updates"] == 0) { - el.value().erase("sql_safe_updates"); - j["conn"]["sql_safe_updates"] = "OFF"; - } - - std::stringstream ss; - ss << 0xFFFFFFFFFFFFFFFF; - // sql_select_limit - if (!el.value()["sql_select_limit"].dump().compare("\"DEFAULT\"")) { - el.value().erase("sql_select_limit"); - j["conn"]["sql_select_limit"] = strdup(ss.str().c_str()); - } - - if (!is_mariadb) { - // transaction_isolation (level) - if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "REPEATABLE-READ"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "READ-COMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "READ-UNCOMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "SERIALIZABLE"; - } - } - else { - // transaction_isolation (level) - if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "REPEATABLE-READ"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "READ-COMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "READ-UNCOMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "SERIALIZABLE"; - } - } - - if (!is_mariadb) { - // transaction_read (write|only) - if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { - el.value().erase("transaction_read"); - j["conn"]["transaction_read_only"] = "ON"; - } - else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { - el.value().erase("transaction_read"); - j["conn"]["transaction_read_only"] = "OFF"; - } - } else { - // transaction_read (write|only) - if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { - el.value().erase("transaction_read"); - j["conn"]["tx_read_only"] = "ON"; - } - else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { - el.value().erase("transaction_read"); - j["conn"]["tx_read_only"] = "OFF"; - } - } - - if (!is_mariadb) { - // session_track_gtids - if (!el.value()["session_track_gtids"].dump().compare("\"OFF\"")) { - el.value().erase("session_track_gtids"); - j["conn"]["session_track_gtids"] = "OFF"; - } - else if (!el.value()["session_track_gtids"].dump().compare("\"OWN_GTID\"")) { - el.value().erase("session_track_gtids"); - j["conn"]["session_track_gtids"] = "OWN_GTID"; - } - else if (!el.value()["session_track_gtids"].dump().compare("\"ALL_GTIDS\"")) { - el.value().erase("session_track_gtids"); - j["conn"]["session_track_gtids"] = "ALL_GTIDS"; - } - } - - } - } -} - std::vector forgotten_vars {}; -/** - * @brief Checks that after setting 'session_track_gtids', the new set value follows ProxySQL rules - * for this particular variable. This is: - * - backend connections are by default set to `mysql-default_session_track_gtids`. - * - if `mysql-default_session_track_gtids=OFF` (the default), `session_track_gtids` is not changed on backend. - * - if the client asks for `session_track_gtids=OFF`, proxysql ignores it (it just acknowledge it). - * - if the client asks for `session_track_gtids=OWN_GTID`, proxysql will apply it. - * - if the client asks for `session_track_gtids=ALL_GTIDS`, proxysql will switch to OWN_GTID and generate a warning. - * - if the backend doesn't support `session_track_gtids` (for example in MySQL 5.5 and MySQL 5.6), proxysql won't apply it. It knows checking server capabilities - * - * @param expVal The value to which 'session_track_gtids' have been set. - * @param sVal The ProxySQL session value for 'session_track_gtids'. - * @param mVal The MySQL session value for 'session_track_gtids'. - * @return True in case the check succeed, false otherwise. - */ -bool check_session_track_gtids(const std::string& expVal, const std::string& sVal, const std::string& mVal) { - bool res = false; - - if (expVal == "OFF") { - res = expVal == sVal; - } else if (expVal == "OWN_GTID" && (sVal == mVal && sVal == "OWN_GTID")) { - res = true; - } else if (expVal == "ALL_GTIDS" && (sVal == mVal && sVal == "OWN_GTID")) { - res = true; - } - - return res; -} +#include "set_testing.h" void * my_conn_thread(void *arg) { g_seed = time(NULL) ^ getpid() ^ pthread_self(); @@ -456,6 +183,7 @@ void * my_conn_thread(void *arg) { } MYSQL *mysql=NULL; json vars; + std::string paddress = ""; for (j=0; j split(const std::string& s, char delimiter) -{ - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - while (std::getline(tokenStream, token, delimiter)) - { - tokens.push_back(token); - } - return tokens; -} - -using nlohmann::json; - -struct TestCase { - std::string command; - json expected_vars; - json reset_vars; -}; - -std::vector testCases; - -#define MAX_LINE 1024 - -int readTestCases(const std::string& fileName) { - FILE* fp = fopen(fileName.c_str(), "r"); - if (!fp) return 0; - - char buf[MAX_LINE], col1[MAX_LINE], col2[MAX_LINE], col3[MAX_LINE] = {0}; - int n = 0; - for(;;) { - if (fgets(buf, sizeof(buf), fp) == NULL) break; - n = sscanf(buf, " \"%[^\"]\", \"%[^\"]\", \"%[^\"]\"", col1, col2, col3); - if (n == 0) break; - - char *p = col2; - while(*p++) if(*p == '\'') *p = '\"'; - - json vars = json::parse(col2); - - p = col3; - while(col3[0] != 0 && *p++) if(*p == '\'') *p = '\"'; - - json reset_vars; - if (p != col3) { - reset_vars = json::parse(col3); - } - - testCases.push_back({col1, vars, reset_vars}); - } - fclose(fp); - return 1; -} - -unsigned long long monotonic_time() { - struct timespec ts; - //clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); // this is faster, but not precise - clock_gettime(CLOCK_MONOTONIC, &ts); - return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); -} - -struct cpu_timer -{ - cpu_timer() { - begin = monotonic_time(); - } - ~cpu_timer() - { - unsigned long long end = monotonic_time(); - std::cerr << double( end - begin ) / 1000000 << " secs.\n" ; - begin=end-begin; - }; - unsigned long long begin; -}; int queries_per_connections=1; unsigned int num_threads=1; @@ -140,287 +66,15 @@ unsigned int query_phase_completed = 0; __thread int g_seed; std::mutex mtx_; -inline int fastrand() { - g_seed = (214013*g_seed+2531011); - return (g_seed>>16)&0x7FFF; -} - -void parseResultJsonColumn(MYSQL_RES *result, json& j) { - if(!result) return; - MYSQL_ROW row; - - while ((row = mysql_fetch_row(result))) - j = json::parse(row[0]); -} - -void parseResult(MYSQL_RES *result, json& j) { - if(!result) return; - MYSQL_ROW row; - - while ((row = mysql_fetch_row(result))) { - j[row[0]] = row[1]; - } -} - -void dumpResult(MYSQL_RES *result) { - if(!result) return; - MYSQL_ROW row; - - int num_fields = mysql_num_fields(result); - - while ((row = mysql_fetch_row(result))) - { - for(int i = 0; i < num_fields; i++) - { - printf("%s ", row[i] ? row[i] : "NULL"); - } - printf("\n"); - } -} - -void queryVariables(MYSQL *mysql, json& j, std::string& paddress) { - std::stringstream query; - if (is_mariadb) { - query << "SELECT /* mysql " << mysql << " " << paddress << " */ lower(variable_name), variable_value FROM information_schema.session_variables WHERE variable_name IN " - " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " - " 'sql_safe_updates', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " - " 'sql_select_limit', 'character_set_results', 'tx_isolation', 'tx_read_only', " - " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'group_concat_max_len');"; - } - if (is_cluster) { - query << "SELECT /* mysql " << mysql << " " << paddress << " */ * FROM performance_schema.session_variables WHERE variable_name IN " - " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " - " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " - " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', " - " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'wsrep_sync_wait', 'group_concat_max_len');"; - } - if (!is_mariadb && !is_cluster) { - query << "SELECT /* mysql " << mysql << " " << paddress << " */ * FROM performance_schema.session_variables WHERE variable_name IN " - " ('hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null', " - " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " - " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', " - " 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'group_concat_max_len');"; - } - //fprintf(stderr, "TRACE : QUERY 3 : variables %s\n", query.str().c_str()); - if (mysql_query(mysql, query.str().c_str())) { - if (silent==0) { - fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query.str().c_str(), mysql_errno(mysql), mysql_error(mysql)); - } - } else { - MYSQL_RES *result = mysql_store_result(mysql); - parseResult(result, j); - - mysql_free_result(result); - __sync_fetch_and_add(&g_select_OK,1); - } -} - -void queryInternalStatus(MYSQL *mysql, json& j, std::string& paddress) { - char *query = (char*)"PROXYSQL INTERNAL SESSION"; - - //fprintf(stderr, "TRACE : QUERY 4 : variables %s\n", query); - if (mysql_query(mysql, query)) { - if (silent==0) { - fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query, mysql_errno(mysql), mysql_error(mysql)); - } - } else { - MYSQL_RES *result = mysql_store_result(mysql); - parseResultJsonColumn(result, j); - - mysql_free_result(result); - __sync_fetch_and_add(&g_select_OK,1); - } - - // value types in mysql and in proxysql are different - // we should convert proxysql values to mysql format to compare - for (auto& el : j.items()) { - if (paddress.length() == 0) { - if (el.key() == "address") { - paddress = el.value(); - } - } - if (el.key() == "conn") { - std::string sql_log_bin_value; +std::vector forgotten_vars {}; - // sql_log_bin {0|1} - if (el.value()["sql_log_bin"] == 1) { - el.value().erase("sql_log_bin"); - j["conn"]["sql_log_bin"] = "ON"; - } - else if (el.value()["sql_log_bin"] == 0) { - el.value().erase("sql_log_bin"); - j["conn"]["sql_log_bin"] = "OFF"; - } +#include "set_testing.h" - // sql_auto_is_null {true|false} - if (!el.value()["sql_auto_is_null"].dump().compare("ON") || - !el.value()["sql_auto_is_null"].dump().compare("1") || - !el.value()["sql_auto_is_null"].dump().compare("on") || - el.value()["sql_auto_is_null"] == 1) { - el.value().erase("sql_auto_is_null"); - j["conn"]["sql_auto_is_null"] = "ON"; - } - else if (!el.value()["sql_auto_is_null"].dump().compare("OFF") || - !el.value()["sql_auto_is_null"].dump().compare("0") || - !el.value()["sql_auto_is_null"].dump().compare("off") || - el.value()["sql_auto_is_null"] == 0) { - el.value().erase("sql_auto_is_null"); - j["conn"]["sql_auto_is_null"] = "OFF"; - } - // completely remove autocommit test -/* - // autocommit {true|false} - if (!el.value()["autocommit"].dump().compare("ON") || - !el.value()["autocommit"].dump().compare("1") || - !el.value()["autocommit"].dump().compare("on") || - el.value()["autocommit"] == 1) { - el.value().erase("autocommit"); - j["conn"]["autocommit"] = "ON"; - } - else if (!el.value()["autocommit"].dump().compare("OFF") || - !el.value()["autocommit"].dump().compare("0") || - !el.value()["autocommit"].dump().compare("off") || - el.value()["autocommit"] == 0) { - el.value().erase("autocommit"); - j["conn"]["autocommit"] = "OFF"; - } +/* TODO + add support for variables with values out of range, + for example setting auto_increment_increment to 100000 */ - // sql_safe_updates - if (!el.value()["sql_safe_updates"].dump().compare("\"ON\"") || - !el.value()["sql_safe_updates"].dump().compare("\"1\"") || - !el.value()["sql_safe_updates"].dump().compare("\"on\"") || - el.value()["sql_safe_updates"] == 1) { - el.value().erase("sql_safe_updates"); - j["conn"]["sql_safe_updates"] = "ON"; - } - else if (!el.value()["sql_safe_updates"].dump().compare("\"OFF\"") || - !el.value()["sql_safe_updates"].dump().compare("\"0\"") || - !el.value()["sql_safe_updates"].dump().compare("\"off\"") || - el.value()["sql_safe_updates"] == 0) { - el.value().erase("sql_safe_updates"); - j["conn"]["sql_safe_updates"] = "OFF"; - } - - std::stringstream ss; - ss << 0xFFFFFFFFFFFFFFFF; - // sql_select_limit - if (!el.value()["sql_select_limit"].dump().compare("\"DEFAULT\"")) { - el.value().erase("sql_select_limit"); - j["conn"]["sql_select_limit"] = strdup(ss.str().c_str()); - } - - if (!is_mariadb) { - // transaction_isolation (level) - if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "REPEATABLE-READ"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "READ-COMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "READ-UNCOMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { - el.value().erase("isolation_level"); - j["conn"]["transaction_isolation"] = "SERIALIZABLE"; - } - } - else { - // transaction_isolation (level) - if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "REPEATABLE-READ"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "READ-COMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "READ-UNCOMMITTED"; - } - else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { - el.value().erase("isolation_level"); - j["conn"]["tx_isolation"] = "SERIALIZABLE"; - } - } - - if (!is_mariadb) { - // transaction_read (write|only) - if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { - el.value().erase("transaction_read"); - j["conn"]["transaction_read_only"] = "ON"; - } - else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { - el.value().erase("transaction_read"); - j["conn"]["transaction_read_only"] = "OFF"; - } - } else { - // transaction_read (write|only) - if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { - el.value().erase("transaction_read"); - j["conn"]["tx_read_only"] = "ON"; - } - else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { - el.value().erase("transaction_read"); - j["conn"]["tx_read_only"] = "OFF"; - } - } - - if (!is_mariadb) { - // session_track_gtids - if (!el.value()["session_track_gtids"].dump().compare("\"OFF\"")) { - el.value().erase("session_track_gtids"); - j["conn"]["session_track_gtids"] = "OFF"; - } - else if (!el.value()["session_track_gtids"].dump().compare("\"OWN_GTID\"")) { - el.value().erase("session_track_gtids"); - j["conn"]["session_track_gtids"] = "OWN_GTID"; - } - else if (!el.value()["session_track_gtids"].dump().compare("\"ALL_GTIDS\"")) { - el.value().erase("session_track_gtids"); - j["conn"]["session_track_gtids"] = "ALL_GTIDS"; - } - } - - } - } -} - -std::vector forgotten_vars {}; - -/** - * @brief Checks that after setting 'session_track_gtids', the new set value follows ProxySQL rules - * for this particular variable. This is: - * - backend connections are by default set to `mysql-default_session_track_gtids`. - * - if `mysql-default_session_track_gtids=OFF` (the default), `session_track_gtids` is not changed on backend. - * - if the client asks for `session_track_gtids=OFF`, proxysql ignores it (it just acknowledge it). - * - if the client asks for `session_track_gtids=OWN_GTID`, proxysql will apply it. - * - if the client asks for `session_track_gtids=ALL_GTIDS`, proxysql will switch to OWN_GTID and generate a warning. - * - if the backend doesn't support `session_track_gtids` (for example in MySQL 5.5 and MySQL 5.6), proxysql won't apply it. It knows checking server capabilities - * - * @param expVal The value to which 'session_track_gtids' have been set. - * @param sVal The ProxySQL session value for 'session_track_gtids'. - * @param mVal The MySQL session value for 'session_track_gtids'. - * @return True in case the check succeed, false otherwise. - */ -bool check_session_track_gtids(const std::string& expVal, const std::string& sVal, const std::string& mVal) { - bool res = false; - - if (expVal == "OFF") { - res = expVal == sVal; - } else if (expVal == "OWN_GTID" && (sVal == mVal && sVal == "OWN_GTID")) { - res = true; - } else if (expVal == "ALL_GTIDS" && (sVal == mVal && sVal == "OWN_GTID")) { - res = true; - } - - return res; -} void * my_conn_thread(void *arg) { g_seed = time(NULL) ^ getpid() ^ pthread_self(); @@ -464,7 +118,8 @@ void * my_conn_thread(void *arg) { for (j=0; jthread_id, r2, testCases[r2].command.c_str()); std::vector commands = split(testCases[r2].command.c_str(), ';'); for (auto c : commands) { if (mysql_query(mysql, c.c_str())) { @@ -548,6 +204,8 @@ void * my_conn_thread(void *arg) { json proxysql_vars; queryInternalStatus(mysql, proxysql_vars, paddress); + //diag("MySQL vars: %lu , ProxySQL vars: %lu" , mysql_vars.size(), proxysql_vars.size()); + if (!testCases[r2].reset_vars.empty()) { for (const auto& var : testCases[r2].reset_vars) { if (std::find(forgotten_vars.begin(), forgotten_vars.end(), var) == forgotten_vars.end()) { @@ -583,47 +241,77 @@ void * my_conn_thread(void *arg) { exit(EXIT_FAILURE); } - std::string str_val { el.value() }; + if (k.value() != el.value()) { // different in mysql + std::string e_val { el.value() }; + std::string k_val { k.value() }; + std::string s_val { s.value() }; + if (el.value() == s.value()) { // but same in proxysql + std::string str_val { el.value() }; + if (strcasecmp(str_val.c_str(), "TRADITIONAL")==0) { + if (k.value() == "STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION") { + special_sqlmode = true; + verified_special_sqlmode = true; + } + } else { + if (strcasestr(e_val.c_str(), "sql_mode") != NULL) { + // simplified + special_sqlmode = true; + verified_special_sqlmode = true; + } +/* + re2::RE2::Options options(RE2::Quiet); + options.set_case_sensitive(false); + options.set_longest_match(false); + re2::RE2 concat_re("^CONCAT\\((|@@|@@session\\.)SQL_MODE,\"(.*)\"\\)", options); + re2::StringPiece sp_input(str_val); - re2::RE2::Options options(RE2::Quiet); - options.set_case_sensitive(false); - options.set_longest_match(false); - re2::RE2 concat_re("^CONCAT\\((|@@|@@session\\.)SQL_MODE,\"(.*)\"\\)", options); - re2::StringPiece sp_input(str_val); + std::string f_match {}; + std::string s_match {}; - std::string f_match {}; - std::string s_match {}; + re2::RE2::Consume(&sp_input, concat_re, &f_match, &s_match); - re2::RE2::Consume(&sp_input, concat_re, &f_match, &s_match); + if (!s_match.empty()) { + special_sqlmode = true; - if (!s_match.empty()) { - special_sqlmode = true; + // remove the initial 'comma' if exists + if (s_match[0] == ',') { + s_match = s_match.substr(1, std::string::npos); + } - // remove the initial 'comma' if exists - if (s_match[0] == ',') { - s_match = s_match.substr(1, std::string::npos); + std::string k_str_val { k.value() }; + verified_special_sqlmode = + strcasestr(k_str_val.c_str(), s_match.c_str()) != NULL; + } +*/ + } } - - std::string k_str_val { k.value() }; - verified_special_sqlmode = - strcasestr(k_str_val.c_str(), s_match.c_str()) != NULL; } } if ( (special_sqlmode == true && verified_special_sqlmode == false) || + (k == mysql_vars.end()) || + (s == proxysql_vars["conn"].end()) || (special_sqlmode == false && (el.key() != "session_track_gtids" && (k.value() != el.value() || s.value() != el.value())) || (el.key() == "session_track_gtids" && !check_session_track_gtids(el.value(), s.value(), k.value())) ) ) { - __sync_fetch_and_add(&g_failed, 1); - testPassed = false; - fprintf(stderr, "Test failed for this case %s->%s.\n\nmysql data %s\n\n proxysql data %s\n\n csv data %s\n\n\n", - el.value().dump().c_str(), el.key().c_str(), mysql_vars.dump().c_str(), proxysql_vars.dump().c_str(), vars.dump().c_str()); - ok(testPassed, "connections mysql[%p] proxysql[%s], thread_id [%lu], command [%s]", mysql, paddress.c_str(), mysql->thread_id, testCases[r2].command.c_str()); - // In case of failing test, exit completely. - exit(EXIT_FAILURE); + if (el.key() == "wsrep_sync_wait" && k == mysql_vars.end() && (s.value() == el.value())) { + variables_tested++; + } else { + __sync_fetch_and_add(&g_failed, 1); + testPassed = false; + fprintf(stderr, "Test failed for this case %s->%s.\n\nmysql data [%lu]: %s\n\n proxysql data [%lu]: %s\n\n csv data %s\n\n\n", + el.value().dump().c_str(), el.key().c_str(), + mysql_vars.size(), mysql_vars.dump().c_str(), + proxysql_vars.size(), proxysql_vars.dump().c_str(), + vars.dump().c_str()); + diag("FAILED FOR: connections mysql[%p] proxysql[%s], thread_id [%lu], command [%s]", mysql, paddress.c_str(), mysql->thread_id, testCases[r2].command.c_str()); + //ok(testPassed, "connections mysql[%p] proxysql[%s], thread_id [%lu], command [%s]", mysql, paddress.c_str(), mysql->thread_id, testCases[r2].command.c_str()); + // In case of failing test, exit completely. + //exit(EXIT_FAILURE); + } } else { variables_tested++; } @@ -642,32 +330,13 @@ void * my_conn_thread(void *arg) { int main(int argc, char *argv[]) { CommandLine cl; - if(cl.getEnv()) - return exit_status(); - - std::string fileName(std::string(cl.workdir) + "/set_testing-t.csv"); -/* - // do not connect to admin at all - MYSQL* mysqladmin = mysql_init(NULL); - if (!mysqladmin) - return exit_status(); - - if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { - fprintf(stderr, "File %s, line %d, Error: %s\n", - __FILE__, __LINE__, mysql_error(mysqladmin)); + if(cl.getEnv()) { + diag("Failed to get the required environmental variables."); return exit_status(); } - MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' where variable_name='mysql-default_sql_mode'"); - MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='OFF' where variable_name='mysql-default_sql_safe_update'"); - MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='UTF8' where variable_name='mysql-default_character_set_results'"); - MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='REPEATABLE READ' where variable_name='mysql-default_isolation_level'"); - MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='REPEATABLE READ' where variable_name='mysql-default_tx_isolation'"); - MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='utf8_general_ci' where variable_name='mysql-default_collation_connection'"); - MYSQL_QUERY(mysqladmin, "update global_variables set variable_value='true' where variable_name='mysql-enforce_autocommit_on_reads'"); - MYSQL_QUERY(mysqladmin, "load mysql variables to runtime"); + std::string fileName(std::string(cl.workdir) + "/set_testing-t.csv"); -*/ MYSQL* mysql = mysql_init(NULL); if (!mysql) return exit_status(); @@ -701,7 +370,6 @@ int main(int argc, char *argv[]) { mysql_close(mysql); num_threads = 10; - queries = 300; queries_per_connections = 10; count = 10; username = cl.username; @@ -709,12 +377,15 @@ int main(int argc, char *argv[]) { host = cl.host; port = cl.port; - plan(queries * num_threads); if (!readTestCases(fileName)) { fprintf(stderr, "Cannot read %s\n", fileName.c_str()); return exit_status(); } + queries = 300; + + plan(queries * num_threads); + if (strcmp(host,"localhost")==0) { local = 1; } diff --git a/test/tap/tests/set_testing-t.csv b/test/tap/tests/set_testing-t.csv index d7403e139..aa0f8d348 100644 --- a/test/tap/tests/set_testing-t.csv +++ b/test/tap/tests/set_testing-t.csv @@ -63,9 +63,6 @@ "SET session_track_gtids=OWN_GTID", "{'session_track_gtids':'OWN_GTID'}" "SET sql_auto_is_null=OFF", "{'sql_auto_is_null':'OFF'}" "SET sql_auto_is_null=ON", "{'sql_auto_is_null':'ON'}" -"set net_write_timeout=30", "{'net_write_timeout':'30'}" -"set net_write_timeout=60", "{'net_write_timeout':'60'}" -"set net_write_timeout=90", "{'net_write_timeout':'90'}" "set max_join_size=20000", "{'max_join_size':'20000'}" "set max_join_size=10000", "{'max_join_size':'10000'}" "set max_join_size=18446744073709551615", "{'max_join_size':'18446744073709551615'}" @@ -74,7 +71,7 @@ "set group_concat_max_len=2048", "{'group_concat_max_len':'2048'}" "set group_concat_max_len=4096", "{'group_concat_max_len':'4096'}" "set tx_isolation='READ-COMMITTED', group_concat_max_len=4096", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'4096'}" -"set tx_isolation='READ-COMMITTED', group_concat_max_len=2048, net_write_timeout=30", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'2048', 'net_write_timeout':'30'}" +"set tx_isolation='READ-COMMITTED', group_concat_max_len=2048", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'2048'}" "set group_concat_max_len=4096; ", "{'group_concat_max_len':'4096'}" "SET character_set_database='utf8mb4', max_join_size=10000; ", "{'character_set_database':'utf8mb4', 'max_join_size':'10000'}" "SET character_set_database='utf8mb4', max_join_size=10000, session_track_gtids=OWN_GTID; ", "{'character_set_database':'utf8mb4', 'max_join_size':'10000', 'session_track_gtids':'OWN_GTID'}" @@ -97,6 +94,5 @@ "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']" "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'}" "set `tx_isolation`='READ-COMMITTED', `group_concat_max_len`=4096", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'4096'}" -"set `net_write_timeout`=30", "{'net_write_timeout':'30'}" "SET `sql_auto_is_null`=ON", "{'sql_auto_is_null':'ON'}" "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'}" diff --git a/test/tap/tests/set_testing-t.csv.obsolete b/test/tap/tests/set_testing-t.csv.obsolete new file mode 100644 index 000000000..d7403e139 --- /dev/null +++ b/test/tap/tests/set_testing-t.csv.obsolete @@ -0,0 +1,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'}" +"set names latin2", "{'character_set_results':'latin2', 'collation_connection':'latin2_general_ci', 'character_set_connection':'latin2', 'character_set_client':'latin2'}" +"set names utf8mb4", "{'character_set_results':'utf8mb4', 'collation_connection':'utf8mb4_general_ci', 'character_set_connection':'utf8mb4', 'character_set_client':'utf8mb4'}" +"set character_set_client='utf8mb4'; set names utf8; set character_set_connection='latin1';", "{'character_set_results':'utf8', 'collation_connection':'latin1_swedish_ci', 'character_set_connection':'latin1', 'character_set_client':'utf8'}" +"set character_set_database='latin1'; set character_set_connection='utf8mb4'; set names utf8; set character_set_client='latin1';", "{'character_set_database':'latin1', 'character_set_results':'utf8', 'collation_connection':'utf8_general_ci', 'character_set_connection':'utf8', 'character_set_client':'latin1'}" +"set character_set_connection='utf8mb4'; set names utf8; set character_set_client='latin1'; set collation_connection='latin1_swedish_ci';", "{'character_set_results':'utf8', 'collation_connection':'latin1_swedish_ci', 'character_set_connection':'latin1', 'character_set_client':'latin1'}""set names latin7", "{'character_set_results':'latin7', 'collation_connection':'latin7_general_ci', 'character_set_connection':'latin7', 'character_set_client':'latin7'}" +"set collation_connection='utf8mb4_general_ci'; ", "{'collation_connection':'utf8mb4_general_ci', 'character_set_connection':'utf8mb4'}" +"set collation_connection='latin1_danish_ci'; ", "{'collation_connection':'latin1_danish_ci', 'character_set_connection':'latin1'}" +"set collation_connection='latin1_swedish_ci'", "{'collation_connection':'latin1_swedish_ci', 'character_set_connection':'latin1'}" +"set collation_connection='utf8_general_ci'", "{'collation_connection':'utf8_general_ci', 'character_set_connection':'utf8'}" +"SET collation_connection='latin1_swedish_ci'", "{'character_set_connection':'latin1', 'collation_connection':'latin1_swedish_ci'}" +"SET collation_connection='utf8_general_ci'", "{'character_set_connection':'utf8', 'collation_connection':'utf8_general_ci'}" +"SET collation_connection='utf8mb4_general_ci'", "{'character_set_connection':'utf8mb4', 'collation_connection':'utf8mb4_general_ci'}" +"SET character_set_connection='latin1'", "{'character_set_connection':'latin1', 'collation_connection':'latin1_swedish_ci'}" +"SET character_set_connection='utf8'", "{'character_set_connection':'utf8', 'collation_connection':'utf8_general_ci'}" +"SET character_set_connection='utf8mb4'", "{'character_set_connection':'utf8mb4', 'collation_connection':'utf8mb4_general_ci'}" +"SET character_set_results='latin1'", "{'character_set_results':'latin1'}" +"SET character_set_results='binary'", "{'character_set_results':'binary'}" +"SET character_set_results=NULL", "{'character_set_results':''}" +"SET character_set_results='utf8'", "{'character_set_results':'utf8'}" +"SET character_set_results='utf8mb4'", "{'character_set_results':'utf8mb4'}" +"SET character_set_client='latin1'", "{'character_set_client':'latin1'}" +"SET character_set_client='utf8'", "{'character_set_client':'utf8'}" +"SET character_set_client='utf8mb4'", "{'character_set_client':'utf8mb4'}" +"SET character_set_database='latin1'; ", "{'character_set_database':'latin1'}" +"SET character_set_database='utf8'", "{'character_set_database':'utf8'}" +"SET character_set_database='utf8mb4'", "{'character_set_database':'utf8mb4'}" +"SET sql_log_bin=1", "{'sql_log_bin':'ON'}" +"SET sql_log_bin=0", "{'sql_log_bin':'OFF'}" +"set sql_mode=''","{'sql_mode':''}" +"SET sql_mode='PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'", "{'sql_mode':'PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'}" +"SET time_zone='+01:00'","{'time_zone':'+01:00'}" +"SET time_zone='-03:00', sql_mode='ALLOW_INVALID_DATES'","{'time_zone':'-03:00', 'sql_mode':'ALLOW_INVALID_DATES'}" +"SET time_zone='+04:00', sql_mode='NO_ENGINE_SUBSTITUTION'", "{'time_zone':'+04:00','sql_mode':'NO_ENGINE_SUBSTITUTION'}" +"SET sql_safe_updates='OFF'", "{'sql_safe_updates':'OFF'}" +"SET sql_safe_updates='ON'", "{'sql_safe_updates':'ON'}" +"SET sql_safe_updates=0", "{'sql_safe_updates':'OFF'}" +"SET sql_safe_updates=1", "{'sql_safe_updates':'ON'}" +"SET sql_select_limit=1010", "{'sql_select_limit':'1010'}" +"SET sql_select_limit=2020", "{'sql_select_limit':'2020'}" +"SET sql_select_limit=3030", "{'sql_select_limit':'3030'}" +"SET time_zone='+01:00'; ","{'time_zone':'+01:00'}" +"SET time_zone='-03:00', sql_mode='ALLOW_INVALID_DATES'; ","{'time_zone':'-03:00', 'sql_mode':'ALLOW_INVALID_DATES'}" +"SET time_zone='+04:00', sql_mode='NO_ENGINE_SUBSTITUTION'; ", "{'time_zone':'+04:00','sql_mode':'NO_ENGINE_SUBSTITUTION'}" +"SET sql_safe_updates='OFF'; ", "{'sql_safe_updates':'OFF'}" +"SET sql_safe_updates='ON'; ", "{'sql_safe_updates':'ON'}" +"SET sql_safe_updates=0; ", "{'sql_safe_updates':'OFF'}" +"SET sql_safe_updates=1; ", "{'sql_safe_updates':'ON'}" +"SET sql_select_limit=1010; ", "{'sql_select_limit':'1010'}" +"SET sql_select_limit=2020; ", "{'sql_select_limit':'2020'}" +"SET sql_select_limit=3030; ", "{'sql_select_limit':'3030'}" +"SET session transaction read only", "{'transaction_read_only':'ON'}" +"SET session transaction read write", "{'transaction_read_only':'OFF'}" +"SET session transaction isolation level READ COMMITTED", "{'transaction_isolation':'READ-COMMITTED'}" +"SET session transaction isolation level READ UNCOMMITTED", "{'transaction_isolation':'READ-UNCOMMITTED'}" +"SET session transaction isolation level REPEATABLE READ", "{'transaction_isolation':'REPEATABLE-READ'}" +"SET session transaction isolation level SERIALIZABLE", "{'transaction_isolation':'SERIALIZABLE'}" +"SET tx_isolation='READ-COMMITTED'", "{'transaction_isolation':'READ-COMMITTED'}" +"SET tx_isolation='READ-UNCOMMITTED'", "{'transaction_isolation':'READ-UNCOMMITTED'}" +"SET tx_isolation='REPEATABLE-READ'", "{'transaction_isolation':'REPEATABLE-READ'}" +"SET tx_isolation='SERIALIZABLE'", "{'transaction_isolation':'SERIALIZABLE'}" +"SET session_track_gtids=OFF", "{'session_track_gtids':'OFF'}" +"SET session_track_gtids=OWN_GTID", "{'session_track_gtids':'OWN_GTID'}" +"SET sql_auto_is_null=OFF", "{'sql_auto_is_null':'OFF'}" +"SET sql_auto_is_null=ON", "{'sql_auto_is_null':'ON'}" +"set net_write_timeout=30", "{'net_write_timeout':'30'}" +"set net_write_timeout=60", "{'net_write_timeout':'60'}" +"set net_write_timeout=90", "{'net_write_timeout':'90'}" +"set max_join_size=20000", "{'max_join_size':'20000'}" +"set max_join_size=10000", "{'max_join_size':'10000'}" +"set max_join_size=18446744073709551615", "{'max_join_size':'18446744073709551615'}" +"set wsrep_sync_wait=1", "{'wsrep_sync_wait':'1'}" +"set wsrep_sync_wait=0", "{'wsrep_sync_wait':'0'}" +"set group_concat_max_len=2048", "{'group_concat_max_len':'2048'}" +"set group_concat_max_len=4096", "{'group_concat_max_len':'4096'}" +"set tx_isolation='READ-COMMITTED', group_concat_max_len=4096", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'4096'}" +"set tx_isolation='READ-COMMITTED', group_concat_max_len=2048, net_write_timeout=30", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'2048', 'net_write_timeout':'30'}" +"set group_concat_max_len=4096; ", "{'group_concat_max_len':'4096'}" +"SET character_set_database='utf8mb4', max_join_size=10000; ", "{'character_set_database':'utf8mb4', 'max_join_size':'10000'}" +"SET character_set_database='utf8mb4', max_join_size=10000, session_track_gtids=OWN_GTID; ", "{'character_set_database':'utf8mb4', 'max_join_size':'10000', 'session_track_gtids':'OWN_GTID'}" +"SET sql_select_limit=3030, session_track_gtids=OWN_GTID; SET max_join_size=10000; ", "{'sql_select_limit':'3030', 'max_join_size':'10000', 'session_track_gtids':'OWN_GTID'}" +"SET sql_mode='NO_ENGINE_SUBSTITUTION', sql_select_limit=3030, session_track_gtids=OWN_GTID; SET max_join_size=10000; ", "{'sql_mode':'NO_ENGINE_SUBSTITUTION','sql_select_limit':'3030', 'max_join_size':'10000', 'session_track_gtids':'OWN_GTID'}" +"SET character_set_client='utf8', sql_select_limit=3030, max_join_size=18446744073709551615", "{'character_set_client':'utf8', 'sql_select_limit':'3030', 'max_join_size':'18446744073709551615'}" +"SET sql_select_limit=3030, character_set_client='latin1', max_join_size=10000", "{'sql_select_limit':'3030', 'character_set_client':'latin1', 'max_join_size':'10000'}" +"SET session_track_gtids=OFF", "{'session_track_gtids':'OFF'}" +"SET time_zone='+04:00', sql_mode='NO_ENGINE_SUBSTITUTION', character_set_client='latin1', max_join_size=10000", "{'time_zone':'+04:00', 'sql_mode':'NO_ENGINE_SUBSTITUTION', 'character_set_client':'latin1', 'max_join_size':'10000'}" +"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'}" +"SET character_set_results='binary', max_join_size=10000, sql_mode='NO_ENGINE_SUBSTITUTION', character_set_client='latin1'; ", "{'character_set_results':'binary', 'sql_mode':'NO_ENGINE_SUBSTITUTION', 'character_set_client':'latin1', 'max_join_size':'10000'}" +"SET CHARACTER SET utf8", "{'character_set_results':'utf8', 'character_set_client':'utf8'}", "['character_set_connection', 'collation_connection']" +"SET CHARACTER SET 'utf8'", "{'character_set_results':'utf8', 'character_set_client':'utf8'}", "['character_set_connection', 'collation_connection']" +"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']" +"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']" +"SET session_track_gtids=ALL_GTIDS", "{'session_track_gtids':'ALL_GTIDS'}" +"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\')'}" +"set `group_concat_max_len`=4096", "{'group_concat_max_len':'4096'}" +"SET `sql_select_limit`=3030; ", "{'sql_select_limit':'3030'}" +"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']" +"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'}" +"set `tx_isolation`='READ-COMMITTED', `group_concat_max_len`=4096", "{'transaction_isolation':'READ-COMMITTED', 'group_concat_max_len':'4096'}" +"set `net_write_timeout`=30", "{'net_write_timeout':'30'}" +"SET `sql_auto_is_null`=ON", "{'sql_auto_is_null':'ON'}" +"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'}" diff --git a/test/tap/tests/set_testing.h b/test/tap/tests/set_testing.h new file mode 100644 index 000000000..f301e1949 --- /dev/null +++ b/test/tap/tests/set_testing.h @@ -0,0 +1,446 @@ +std::vector split(const std::string& s, char delimiter) +{ + std::vector tokens; + std::string token; + std::istringstream tokenStream(s); + while (std::getline(tokenStream, token, delimiter)) + { + tokens.push_back(token); + } + return tokens; +} + +using nlohmann::json; + +struct TestCase { + std::string command; + json expected_vars; + json reset_vars; +}; + +std::vector testCases; + +#define MAX_LINE 10240 + +int readTestCases(const std::string& fileName) { + FILE* fp = fopen(fileName.c_str(), "r"); + if (!fp) return 0; + + char buf[MAX_LINE], col1[MAX_LINE], col2[MAX_LINE], col3[MAX_LINE] = {0}; + int n = 0; + for(;;) { + if (fgets(buf, sizeof(buf), fp) == NULL) break; + n = sscanf(buf, " \"%[^\"]\", \"%[^\"]\", \"%[^\"]\"", col1, col2, col3); + if (n == 0) break; + + char *p = col2; + while(*p++) if(*p == '\'') *p = '\"'; + + json vars = json::parse(col2); + + p = col3; + while(col3[0] != 0 && *p++) if(*p == '\'') *p = '\"'; + + json reset_vars; + if (p != col3) { + reset_vars = json::parse(col3); + } + + testCases.push_back({col1, vars, reset_vars}); + } + + fclose(fp); + return 1; +} + + +int readTestCasesJSON(const std::string& fileName) { + FILE* fp = fopen(fileName.c_str(), "r"); + if (!fp) return 0; + + char buf[MAX_LINE] = {0}; + int n = 0; + int i = 0; + for(;;) { + if (fgets(buf, sizeof(buf), fp) == NULL) break; + i++; + json j = json::parse(buf); + +// char *p = col2; +// while(*p++) if(*p == '\'') *p = '\"'; + + //json vars = json::parse(j['expected']); + json vars = j["expected"]; + +// p = col3; +// while(col3[0] != 0 && *p++) if(*p == '\'') *p = '\"'; + + json reset_vars; + if(j.find("reset") != j.end()) + reset_vars = j["reset"]; +// if (p != col3) { +// reset_vars = json::parse(col3); +// } + std::string st = std::string(j["query"].dump(),1,j["query"].dump().length()-2); + unsigned int l = st.length(); + char newbuf[l+1]; + memset(newbuf,0,l+1); + char *s = (char *)st.data(); + char *d = newbuf; + for (unsigned int i=0; i < l; i++) { + if ((*s == '\\') && (*(s+1) == '"')) { + s++; + } else { + *d = *s; + d++; + s++; + } + } + testCases.push_back({newbuf, vars, reset_vars}); + } + + fclose(fp); + return 1; +} + +unsigned long long monotonic_time() { + struct timespec ts; + //clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); // this is faster, but not precise + clock_gettime(CLOCK_MONOTONIC, &ts); + return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); +} + +struct cpu_timer +{ + cpu_timer() { + begin = monotonic_time(); + } + ~cpu_timer() + { + unsigned long long end = monotonic_time(); + std::cerr << double( end - begin ) / 1000000 << " secs.\n" ; + begin=end-begin; + }; + unsigned long long begin; +}; + + +inline int fastrand() { + g_seed = (214014*g_seed+2531011); + return (g_seed>>16)&0x7FFF; +} + +void parseResultJsonColumn(MYSQL_RES *result, json& j) { + if(!result) return; + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + j = json::parse(row[0]); +} + +void parseResult(MYSQL_RES *result, json& j) { + if(!result) return; + MYSQL_ROW row; + + unsigned long long nr = mysql_num_rows(result); + assert(nr > 16); + while ((row = mysql_fetch_row(result))) { + j[row[0]] = row[1]; + } +} + +void dumpResult(MYSQL_RES *result) { + if(!result) return; + MYSQL_ROW row; + + int num_fields = mysql_num_fields(result); + + while ((row = mysql_fetch_row(result))) + { + for(int i = 0; i < num_fields; i++) + { + printf("%s ", row[i] ? row[i] : "NULL"); + } + printf("\n"); + } +} + +void queryVariables(MYSQL *mysql, json& j, std::string& paddress) { + std::stringstream query; + if (is_mariadb) { + query << "SELECT /* mysql " << mysql << " " << paddress << " */ lower(variable_name), variable_value FROM information_schema.session_variables WHERE variable_name IN " + " (" + " 'sql_safe_updates', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " + " 'sql_select_limit', 'character_set_results', 'tx_isolation', 'tx_read_only'"; + } + if (is_cluster) { + query << "SELECT /* mysql " << mysql << " " << paddress << " */ * FROM performance_schema.session_variables WHERE variable_name IN " + " (" + " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " + " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only', " + " 'wsrep_sync_wait'"; + } + if (!is_mariadb && !is_cluster) { + query << "SELECT /* mysql " << mysql << " " << paddress << " */ * FROM performance_schema.session_variables WHERE variable_name IN " + " (" + " 'sql_safe_updates', 'session_track_gtids', 'max_join_size', 'net_write_timeout', 'sql_select_limit', " + " 'sql_select_limit', 'character_set_results', 'transaction_isolation', 'transaction_read_only'"; + } + query << ", 'hostname', 'sql_log_bin', 'sql_mode', 'init_connect', 'time_zone', 'sql_auto_is_null'"; + query << ", 'sql_auto_is_null', 'collation_connection', 'character_set_connection', 'character_set_client', 'character_set_database', 'group_concat_max_len'"; + query << ", 'foreign_key_checks', 'unique_checks'"; + query << ", 'auto_increment_increment', 'auto_increment_offset'"; + query << ", 'default_storage_engine', 'default_tmp_storage_engine'"; + query << ", 'innodb_lock_wait_timeout', 'innodb_strict_mode', 'innodb_table_locks'"; + query << ", 'join_buffer_size', 'lock_wait_timeout'"; + query << ")"; + //fprintf(stderr, "TRACE : QUERY 3 : variables %s\n", query.str().c_str()); + if (mysql_query(mysql, query.str().c_str())) { + if (silent==0) { + fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query.str().c_str(), mysql_errno(mysql), mysql_error(mysql)); + } + } else { + MYSQL_RES *result = mysql_store_result(mysql); + parseResult(result, j); + + mysql_free_result(result); + __sync_fetch_and_add(&g_select_OK,1); + } +} + +void queryInternalStatus(MYSQL *mysql, json& j, std::string& paddress) { + char *query = (char*)"PROXYSQL INTERNAL SESSION"; + + //fprintf(stderr, "TRACE : QUERY 4 : variables %s\n", query); + if (mysql_query(mysql, query)) { + if (silent==0) { + fprintf(stderr,"ERROR while running -- \"%s\" : (%d) %s\n", query, mysql_errno(mysql), mysql_error(mysql)); + } + } else { + MYSQL_RES *result = mysql_store_result(mysql); + parseResultJsonColumn(result, j); + + mysql_free_result(result); + __sync_fetch_and_add(&g_select_OK,1); + } + + std::vector bools_variables = { + "sql_auto_is_null", + "sql_safe_updates", + "foreign_key_checks", + "unique_checks" + }; + // value types in mysql and in proxysql are different + // we should convert proxysql values to mysql format to compare + for (auto& el : j.items()) { + if (paddress.length() == 0) { + if (el.key() == "address") { + paddress = el.value(); + } + } + if (el.key() == "conn") { + std::string sql_log_bin_value; + + // sql_log_bin {0|1} + if (el.value()["sql_log_bin"] == 1) { + el.value().erase("sql_log_bin"); + j["conn"]["sql_log_bin"] = "ON"; + } + else if (el.value()["sql_log_bin"] == 0) { + el.value().erase("sql_log_bin"); + j["conn"]["sql_log_bin"] = "OFF"; + } + + { + for (std::vector::iterator it = bools_variables.begin(); it != bools_variables.end(); it++) { + std::string v = el.value()[*it].dump(); + if ( + (strcasecmp(v.c_str(),"ON")==0) + || + (strcasecmp(v.c_str(),"1")==0) + ) { + el.value().erase(*it); + j["conn"][*it] = "ON"; + } else { + if ( + (strcasecmp(v.c_str(),"OFF")==0) + || + (strcasecmp(v.c_str(),"0")==0) + ) { + el.value().erase(*it); + j["conn"][*it] = "OFF"; + } + } + } + } + +/* + // sql_auto_is_null {true|false} + if (!el.value()["sql_auto_is_null"].dump().compare("ON") || + !el.value()["sql_auto_is_null"].dump().compare("1") || + !el.value()["sql_auto_is_null"].dump().compare("on") || + el.value()["sql_auto_is_null"] == 1) { + el.value().erase("sql_auto_is_null"); + j["conn"]["sql_auto_is_null"] = "ON"; + } + else if (!el.value()["sql_auto_is_null"].dump().compare("OFF") || + !el.value()["sql_auto_is_null"].dump().compare("0") || + !el.value()["sql_auto_is_null"].dump().compare("off") || + el.value()["sql_auto_is_null"] == 0) { + el.value().erase("sql_auto_is_null"); + j["conn"]["sql_auto_is_null"] = "OFF"; + } +*/ + // completely remove autocommit test +/* + // autocommit {true|false} + if (!el.value()["autocommit"].dump().compare("ON") || + !el.value()["autocommit"].dump().compare("1") || + !el.value()["autocommit"].dump().compare("on") || + el.value()["autocommit"] == 1) { + el.value().erase("autocommit"); + j["conn"]["autocommit"] = "ON"; + } + else if (!el.value()["autocommit"].dump().compare("OFF") || + !el.value()["autocommit"].dump().compare("0") || + !el.value()["autocommit"].dump().compare("off") || + el.value()["autocommit"] == 0) { + el.value().erase("autocommit"); + j["conn"]["autocommit"] = "OFF"; + } +*/ +/* + // sql_safe_updates + if (!el.value()["sql_safe_updates"].dump().compare("\"ON\"") || + !el.value()["sql_safe_updates"].dump().compare("\"1\"") || + !el.value()["sql_safe_updates"].dump().compare("\"on\"") || + el.value()["sql_safe_updates"] == 1) { + el.value().erase("sql_safe_updates"); + j["conn"]["sql_safe_updates"] = "ON"; + } + else if (!el.value()["sql_safe_updates"].dump().compare("\"OFF\"") || + !el.value()["sql_safe_updates"].dump().compare("\"0\"") || + !el.value()["sql_safe_updates"].dump().compare("\"off\"") || + el.value()["sql_safe_updates"] == 0) { + el.value().erase("sql_safe_updates"); + j["conn"]["sql_safe_updates"] = "OFF"; + } +*/ + std::stringstream ss; + ss << 0xFFFFFFFFFFFFFFFF; + // sql_select_limit + if (!el.value()["sql_select_limit"].dump().compare("\"DEFAULT\"")) { + el.value().erase("sql_select_limit"); + j["conn"]["sql_select_limit"] = strdup(ss.str().c_str()); + } + + if (!is_mariadb) { + // transaction_isolation (level) + if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "REPEATABLE-READ"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "READ-COMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "READ-UNCOMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { + el.value().erase("isolation_level"); + j["conn"]["transaction_isolation"] = "SERIALIZABLE"; + } + } + else { + // transaction_isolation (level) + if (!el.value()["isolation_level"].dump().compare("\"REPEATABLE READ\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "REPEATABLE-READ"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ COMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "READ-COMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"READ UNCOMMITTED\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "READ-UNCOMMITTED"; + } + else if (!el.value()["isolation_level"].dump().compare("\"SERIALIZABLE\"")) { + el.value().erase("isolation_level"); + j["conn"]["tx_isolation"] = "SERIALIZABLE"; + } + } + + if (!is_mariadb) { + // transaction_read (write|only) + if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { + el.value().erase("transaction_read"); + j["conn"]["transaction_read_only"] = "ON"; + } + else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { + el.value().erase("transaction_read"); + j["conn"]["transaction_read_only"] = "OFF"; + } + } else { + // transaction_read (write|only) + if (!el.value()["transaction_read"].dump().compare("\"ONLY\"")) { + el.value().erase("transaction_read"); + j["conn"]["tx_read_only"] = "ON"; + } + else if (!el.value()["transaction_read"].dump().compare("\"WRITE\"")) { + el.value().erase("transaction_read"); + j["conn"]["tx_read_only"] = "OFF"; + } + } + + if (!is_mariadb) { + // session_track_gtids + if (!el.value()["session_track_gtids"].dump().compare("\"OFF\"")) { + el.value().erase("session_track_gtids"); + j["conn"]["session_track_gtids"] = "OFF"; + } + else if (!el.value()["session_track_gtids"].dump().compare("\"OWN_GTID\"")) { + el.value().erase("session_track_gtids"); + j["conn"]["session_track_gtids"] = "OWN_GTID"; + } + else if (!el.value()["session_track_gtids"].dump().compare("\"ALL_GTIDS\"")) { + el.value().erase("session_track_gtids"); + j["conn"]["session_track_gtids"] = "ALL_GTIDS"; + } + } + + } + } +} + +/** + * @brief Checks that after setting 'session_track_gtids', the new set value follows ProxySQL rules + * for this particular variable. This is: + * - backend connections are by default set to `mysql-default_session_track_gtids`. + * - if `mysql-default_session_track_gtids=OFF` (the default), `session_track_gtids` is not changed on backend. + * - if the client asks for `session_track_gtids=OFF`, proxysql ignores it (it just acknowledge it). + * - if the client asks for `session_track_gtids=OWN_GTID`, proxysql will apply it. + * - if the client asks for `session_track_gtids=ALL_GTIDS`, proxysql will switch to OWN_GTID and generate a warning. + * - if the backend doesn't support `session_track_gtids` (for example in MySQL 5.5 and MySQL 5.6), proxysql won't apply it. It knows checking server capabilities + * + * @param expVal The value to which 'session_track_gtids' have been set. + * @param sVal The ProxySQL session value for 'session_track_gtids'. + * @param mVal The MySQL session value for 'session_track_gtids'. + * @return True in case the check succeed, false otherwise. + */ +bool check_session_track_gtids(const std::string& expVal, const std::string& sVal, const std::string& mVal) { + bool res = false; + + if (expVal == "OFF") { + res = expVal == sVal; + } else if (expVal == "OWN_GTID" && (sVal == mVal && sVal == "OWN_GTID")) { + res = true; + } else if (expVal == "ALL_GTIDS" && (sVal == mVal && sVal == "OWN_GTID")) { + res = true; + } + + return res; +} +