Improvements on how MySQL variables are processed

Deprecated variables:
- net_write_timeout

Renamed set_testing-t.csv to set_testing-t.csv.obsolete , and removed net_write_timeout from set_testing-t.csv

Variables added to ignore list:
- net_write_timeout
- net_buffer_length
- read_buffer_size
- read_rnd_buffer_size

Added support for quoting with backtick

Renamed `enum variable_name` into `enum mysql_variable_name` for better readibility

`enum mysql_variable_name` has variable names alphabetically ordered after SQL_NAME_LAST_LOW_WM

`mysql_tracked_variables` has variable names alphabetically ordered after SQL_NAME_LAST_LOW_WM

A lot of new variables added in `mysql_tracked_variables` , although not all tracked yet (TODO)

In `MySQL_Variables::MySQL_Variables()`:
- if `internal_variable_name` in `mysql_tracked_variables` is NULL , it will be automatically initialized
- it performs some sanity check between `enum mysql_variable_name` and `mysql_tracked_variables`

Testing:
- refactored code in set_testing-t.cpp and set_testing-multi-t.cpp
- added set_testing-240-t.cpp to test improvements in 2.4.0
- generate_set_session_csv to automatically create set_testing-240.csv used by set_testing-240-t
pull/3702/head
René Cannaò 4 years ago
parent 189b30b630
commit d335bfb899

1
.gitignore vendored

@ -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

@ -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();

@ -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[];

@ -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()))

@ -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);

@ -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)+)"

@ -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)\"

@ -1,8 +1,9 @@
#include <vector>
#include <iostream>
#include <string>
#include <unordered_map>
#include <stdlib.h>
#include <string.h>
std::vector<std::string> bool_values = {
"0", "1",
@ -11,10 +12,15 @@ std::vector<std::string> bool_values = {
"`ON`", "`OFF`",
"\"ON\"", "\"OFF\"",
};
std::vector<int> int_values = {
10, 20, 100, 1010, 1234, 3456, 7890, 34564, 68100, 123456, 456123
};
std::vector<int> int_values_small = {
13, 32, 110, 310, 434, 558, 789
};
std::unordered_map<std::string, std::string> 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<std::string> 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<int>& v, int inc) {
for (std::vector<int>::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<std::string, variable *> 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; i<s.length(); i++) {
if (i==0 || i==(s.length() - 1)) {
if (s[i] == '`' || s[i] == '\'' || s[i]== '"')
continue;
}
if (v->name == "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<std::string>& vals = vars["join_buffer_size"]->values;
for (std::vector<std::string>::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<std::string> {"+01:00", "`+02:15`", "\"+03:30\""});
vars["time_zone"]->add(std::vector<std::string> {"+04:45", "`+05:00`", "\"+06:10\""});
vars["time_zone"]->add(std::vector<std::string> {"-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<std::string> {"'+01:00'", "`+02:15`", "\"+03:30\""});
vars["time_zone"]->add(std::vector<std::string> {"'+04:45'", "`+05:00`", "\"+06:10\""});
vars["time_zone"]->add(std::vector<std::string> {"'-1:10'", "`-03:33`", "\"-04:56\""});
vars["time_zone"]->add(std::vector<std::string> {"'+02:45'", "`+7:00`", "\"+06:10\""});
vars["time_zone"]->add(std::vector<std::string> {"'-11:10'", "`-11:33`", "\"-04:56\""});
vars["sql_mode"] = new variable("sql_mode", true, false, false);
vars["sql_mode"]->add(std::vector<std::string> {"'traditional'", "TRADITIONAL", "''"});
vars["sql_mode"]->add(std::vector<std::string> {"'PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION'", "\"PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION\""});
vars["sql_mode"]->add(std::vector<std::string> {"ALLOW_INVALID_DATES", "'ALLOW_INVALID_DATES'", "\"ALLOW_INVALID_DATES\""});
vars["sql_mode"]->add(std::vector<std::string> {"NO_ENGINE_SUBSTITUTION", "'NO_ENGINE_SUBSTITUTION'", "\"NO_ENGINE_SUBSTITUTION\""});
vars["sql_mode"]->add(std::vector<std::string> {"concat(@@sql_mode,',STRICT_TRANS_TABLES')"});
vars["sql_mode"]->add(std::vector<std::string> {"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<std::string> engines = {"InnoDB", "MEMORY", "MyISAM", "BLACKHOLE"};
for (std::vector<std::string>::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<std::string, variable *>::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;

@ -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 <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <mysql.h>
#include <string.h>
#include <string>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <mutex>
#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<std::string> 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<json> varsperconn(count);
if (mysqlconns==NULL) {
exit(EXIT_FAILURE);
}
std::vector<std::string> cs = {"latin1", "utf8", "utf8mb4", "latin2", "latin7"};
for (i=0; i<count; i++) {
MYSQL *mysql=mysql_init(NULL);
std::string nextcs = cs[i%cs.size()];
mysql_options(mysql, MYSQL_SET_CHARSET_NAME, nextcs.c_str());
if (mysql==NULL) {
exit(EXIT_FAILURE);
}
MYSQL *rc=mysql_real_connect(mysql, host, username, password, schema, (local ? 0 : ( port + rand()%multiport ) ), NULL, 0);
if (rc==NULL) {
if (silent==0) {
fprintf(stderr,"%s\n", mysql_error(mysql));
}
exit(EXIT_FAILURE);
}
mysqlconns[i]=mysql;
__sync_add_and_fetch(&status_connections,1);
}
__sync_fetch_and_add(&connect_phase_completed,1);
while(__sync_fetch_and_add(&connect_phase_completed,0) != num_threads) {
}
MYSQL *mysql=NULL;
json vars;
std::string paddress = "";
for (j=0; j<queries; j++) {
int fr = fastrand();
int r1=fr%count;
//int r2=fastrand()%testCases.size();
int r2=rand()%testCases.size();
if (j%queries_per_connections==0) {
mysql=mysqlconns[r1];
vars = varsperconn[r1];
}
if (strcmp(username,(char *)"root")) {
if (strstr(testCases[r2].command.c_str(),"database")) {
std::lock_guard<std::mutex> 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<std::mutex> 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<std::string> 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<std::mutex> 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<num_threads; i++) {
if ( pthread_create(&thi[i], NULL, my_conn_thread , NULL) != 0 )
perror("Thread creation");
}
for (unsigned int i=0; i<num_threads; i++) {
pthread_join(thi[i], NULL);
}
return exit_status();
}

@ -21,6 +21,7 @@
#include "utils.h"
#include "command_line.h"
/*
std::vector<std::string> split(const std::string& s, char delimiter)
{
std::vector<std::string> 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<std::string> 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<queries; j++) {
int fr = fastrand();
int r1=fr%count;
@ -543,10 +271,10 @@ void * my_conn_thread(void *arg) {
}
json mysql_vars;
queryVariables(mysql, mysql_vars);
queryVariables(mysql, mysql_vars, paddress);
json proxysql_vars;
queryInternalStatus(mysql, proxysql_vars);
queryInternalStatus(mysql, proxysql_vars, paddress);
if (!testCases[r2].reset_vars.empty()) {
for (const auto& var : testCases[r2].reset_vars) {

@ -31,81 +31,7 @@
#include "utils.h"
#include "command_line.h"
std::vector<std::string> split(const std::string& s, char delimiter)
{
std::vector<std::string> 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<TestCase> 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<std::string> 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<std::string> 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; j<queries; j++) {
int fr = fastrand();
int r1=fr%count;
int r2=fastrand()%testCases.size();
//int r2=fastrand()%testCases.size();
int r2=rand()%testCases.size();
if (j%queries_per_connections==0) {
mysql=mysqlconns[r1];
@ -482,6 +137,7 @@ void * my_conn_thread(void *arg) {
continue;
}
}
diag("Thread_id: %lu, random number: %d . Query/ies: %s", mysql->thread_id, r2, testCases[r2].command.c_str());
std::vector<std::string> 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;
}

@ -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'}"

Can't render this file because it has a wrong number of fields in line 6.

@ -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'}"

@ -0,0 +1,446 @@
std::vector<std::string> split(const std::string& s, char delimiter)
{
std::vector<std::string> 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<TestCase> 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<std::string> 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<std::string>::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;
}
Loading…
Cancel
Save