From a3d29863e8d1f206da3a83ad2b092f5fec230cac Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 3 Jul 2023 16:47:46 +0500 Subject: [PATCH] Adding SET TRANSACTION ISOLATION LEVEL * support --- include/MySQL_Variables.h | 2 ++ include/gen_utils.h | 2 ++ include/proxysql_structs.h | 6 ++++ lib/MySQL_Session.cpp | 73 ++++++++++++++++++++++++++++---------- lib/MySQL_Variables.cpp | 37 +++++++++++++++++++ lib/gen_utils.cpp | 14 +++++++- lib/set_parser.cpp | 8 +++-- 7 files changed, 119 insertions(+), 23 deletions(-) diff --git a/include/MySQL_Variables.h b/include/MySQL_Variables.h index e51caee16..95381bf2d 100644 --- a/include/MySQL_Variables.h +++ b/include/MySQL_Variables.h @@ -39,11 +39,13 @@ public: bool client_set_value(MySQL_Session* session, int idx, const std::string& value); bool client_set_hash_and_value(MySQL_Session* session, int idx, const std::string& value, uint32_t hash); + void client_reset_value(MySQL_Session* session, int idx); const char* client_get_value(MySQL_Session* session, int idx) const; uint32_t client_get_hash(MySQL_Session* session, int idx) const; void server_set_value(MySQL_Session* session, int idx, const char* value); void server_set_hash_and_value(MySQL_Session* session, int idx, const char* value, uint32_t hash); + void server_reset_value(MySQL_Session* session, int idx); const char* server_get_value(MySQL_Session* session, int idx) const; inline uint32_t server_get_hash(MySQL_Session* session, int idx) const; diff --git a/include/gen_utils.h b/include/gen_utils.h index 314de078c..95c1ec010 100644 --- a/include/gen_utils.h +++ b/include/gen_utils.h @@ -332,3 +332,5 @@ std::string trim(const std::string& s); * @return An 'unique_ptr' holding the resulting 'SQLite3_result'. */ std::unique_ptr get_SQLite3_resulset(MYSQL_RES* resultset); + +std::vector split_string(const std::string& str, char delimiter); diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 4352380c4..0da273ef5 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -221,6 +221,8 @@ enum mysql_variable_name { SQL_TIME_ZONE, SQL_TIMESTAMP, SQL_TMP_TABLE_SIZE, + SQL_NEXT_ISOLATION_LEVEL, + SQL_NEXT_TRANSACTION_READ, SQL_UNIQUE_CHECKS, SQL_WSREP_OSU_METHOD, SQL_NAME_LAST_HIGH_WM, @@ -254,6 +256,8 @@ enum session_status { SETTING_MULTIPLE_VARIABLES, SETTING_SET_NAMES, SHOW_WARNINGS, + SETTING_NEXT_ISOLATION_LEVEL, + SETTING_NEXT_TRANSACTION_READ, session_status___NONE // special marker }; @@ -1188,6 +1192,8 @@ mysql_variable_st mysql_tracked_variables[] { { SQL_TIME_ZONE, SETTING_VARIABLE, true, false, false, false, (char *)"time_zone", NULL, (char *)"SYSTEM" , false} , { SQL_TIMESTAMP, SETTING_VARIABLE, false, 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_NEXT_ISOLATION_LEVEL, SETTING_NEXT_ISOLATION_LEVEL, false, true, false, false, (char *)"transaction isolation level", (char *)"next_isolation_level", (char *)"READ COMMITTED" , false} , + { SQL_NEXT_TRANSACTION_READ, SETTING_NEXT_TRANSACTION_READ, false, true, false, false, (char *)"transaction read", (char *)"next_transaction_read", (char *)"WRITE" , false} , { SQL_UNIQUE_CHECKS, SETTING_VARIABLE, true, false, false, true, (char *)"unique_checks", NULL, (char *)"" , false} , { SQL_WSREP_OSU_METHOD, SETTING_VARIABLE, true, false, false, false, (char *)"wsrep_osu_method", NULL, (char *)"" , false} , diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 57b825568..10e0b9e4c 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -2582,6 +2582,15 @@ bool MySQL_Session::handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, co myds->DSS = STATE_MARIADB_GENERIC; st=previous_status.top(); previous_status.pop(); + + if (strncasecmp("transaction isolation level", var_name, sizeof("transaction isolation level")-1) == 0) { + mysql_variables.server_reset_value(this, SQL_NEXT_ISOLATION_LEVEL); + mysql_variables.client_reset_value(this, SQL_NEXT_ISOLATION_LEVEL); + } else if (strncasecmp("transaction read", var_name, sizeof("transaction read")-1) == 0) { + mysql_variables.server_reset_value(this, SQL_NEXT_TRANSACTION_READ); + mysql_variables.client_reset_value(this, SQL_NEXT_TRANSACTION_READ); + } + NEXT_IMMEDIATE_NEW(st); } else { if (rc==-1) { @@ -5143,6 +5152,8 @@ handler_again: case SETTING_TRANSACTION_READ: case SETTING_CHARSET: case SETTING_VARIABLE: + case SETTING_NEXT_ISOLATION_LEVEL: + case SETTING_NEXT_TRANSACTION_READ: { int rc = 0; if (mysql_variables.update_variable(this, status, rc)) { @@ -6550,27 +6561,51 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C } else if (match_regexes && match_regexes[2]->match(dig)) { SetParser parser(nq); std::map> set = parser.parse2(); + for(auto it = std::begin(set); it != std::end(set); ++it) { - std::string var = it->first; - auto values = std::begin(it->second); - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET variable %s\n", var.c_str()); - if (var == "isolation level") { - std::string value1 = *values; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SESSION TRANSACTION ISOLATION LEVEL value %s\n", value1.c_str()); - uint32_t isolation_level_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_ISOLATION_LEVEL) != isolation_level_int) { - if (!mysql_variables.client_set_value(this, SQL_ISOLATION_LEVEL, value1.c_str())) - return false; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION ISOLATION LEVEL to %s\n", value1.c_str()); + + const std::vector& val = split_string(it->first, ':'); + + if (val.size() == 2) { + + const auto values = std::begin(it->second); + const std::string& var = val[1]; + + enum mysql_variable_name isolation_level_val; + enum mysql_variable_name transaction_read_val; + + if (val[0] == "SESSION") { + isolation_level_val = SQL_ISOLATION_LEVEL; + transaction_read_val = SQL_TRANSACTION_READ; + } else { + isolation_level_val = SQL_NEXT_ISOLATION_LEVEL; + transaction_read_val = SQL_NEXT_TRANSACTION_READ; } - } else if (var == "read") { - std::string value1 = *values; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET SESSION TRANSACTION READ value %s\n", value1.c_str()); - uint32_t transaction_read_int=SpookyHash::Hash32(value1.c_str(),value1.length(),10); - if (mysql_variables.client_get_hash(this, SQL_TRANSACTION_READ) != transaction_read_int) { - if (!mysql_variables.client_set_value(this, SQL_TRANSACTION_READ, value1.c_str())) - return false; - proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION READ to %s\n", value1.c_str()); + + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET variable %s\n", var.c_str()); + if (var == "isolation level") { + const std::string& value1 = *values; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET %s TRANSACTION ISOLATION LEVEL value %s\n", val[0].c_str(), value1.c_str()); + const uint32_t isolation_level_int = SpookyHash::Hash32(value1.c_str(), value1.length(), 10); + if (mysql_variables.client_get_hash(this, isolation_level_val) != isolation_level_int) { + if (!mysql_variables.client_set_value(this, isolation_level_val, value1.c_str())) + return false; + + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION ISOLATION LEVEL to %s\n", value1.c_str()); + } + } else if (var == "read") { + const std::string& value1 = *values; + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Processing SET %s TRANSACTION READ value %s\n", val[0].c_str(), value1.c_str()); + const uint32_t transaction_read_int = SpookyHash::Hash32(value1.c_str(), value1.length(), 10); + if (mysql_variables.client_get_hash(this, transaction_read_val) != transaction_read_int) { + if (!mysql_variables.client_set_value(this, transaction_read_val, value1.c_str())) + return false; + + proxy_debug(PROXY_DEBUG_MYSQL_COM, 8, "Changing connection TRANSACTION READ to %s\n", value1.c_str()); + } + } else { + unable_to_parse_set_statement(lock_hostgroup); + return false; } } else { unable_to_parse_set_statement(lock_hostgroup); diff --git a/lib/MySQL_Variables.cpp b/lib/MySQL_Variables.cpp index e60c4b3ab..c1ed5685a 100644 --- a/lib/MySQL_Variables.cpp +++ b/lib/MySQL_Variables.cpp @@ -94,6 +94,24 @@ bool MySQL_Variables::client_set_hash_and_value(MySQL_Session* session, int idx, return true; } +void MySQL_Variables::client_reset_value(MySQL_Session* session, int idx) { + if (!session || !session->client_myds || !session->client_myds->myconn) { + proxy_warning("Session validation failed\n"); + return; + } + + MySQL_Connection *client_conn = session->client_myds->myconn; + + if (client_conn->var_hash[idx] != 0) { + client_conn->var_hash[idx] = 0; + if (client_conn->variables[idx].value) { + free(client_conn->variables[idx].value); + client_conn->variables[idx].value = NULL; + } + // we now regererate dynamic_variables_idx + client_conn->reorder_dynamic_variables_idx(); + } +} void MySQL_Variables::server_set_hash_and_value(MySQL_Session* session, int idx, const char* value, uint32_t hash) { if (!session || !session->mybe || !session->mybe->server_myds || !session->mybe->server_myds->myconn || !value) { proxy_warning("Session validation failed\n"); @@ -222,6 +240,25 @@ void MySQL_Variables::server_set_value(MySQL_Session* session, int idx, const ch session->mybe->server_myds->myconn->reorder_dynamic_variables_idx(); } +void MySQL_Variables::server_reset_value(MySQL_Session* session, int idx) { + assert(session); + assert(session->mybe); + assert(session->mybe->server_myds); + assert(session->mybe->server_myds->myconn); + + MySQL_Connection *backend_conn = session->mybe->server_myds->myconn; + + if (backend_conn->var_hash[idx] != 0) { + backend_conn->var_hash[idx] = 0; + if (backend_conn->variables[idx].value) { + free(backend_conn->variables[idx].value); + backend_conn->variables[idx].value = NULL; + } + // we now regererate dynamic_variables_idx + backend_conn->reorder_dynamic_variables_idx(); + } +} + const char* MySQL_Variables::server_get_value(MySQL_Session* session, int idx) const { assert(session); assert(session->mybe); diff --git a/lib/gen_utils.cpp b/lib/gen_utils.cpp index 7be885797..31b948c7f 100644 --- a/lib/gen_utils.cpp +++ b/lib/gen_utils.cpp @@ -1,6 +1,6 @@ #include #include - +#include #include "gen_utils.h" @@ -264,4 +264,16 @@ std::unique_ptr get_SQLite3_resulset(MYSQL_RES* resultset) { mysql_data_seek(resultset, 0); return sqlite_result; +} + +std::vector split_string(const std::string& str, char delimiter) { + std::vector tokens {}; + std::string token {}; + std::istringstream tokenStream(str); + + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + + return tokens; } diff --git a/lib/set_parser.cpp b/lib/set_parser.cpp index 4b4be6ec3..737083749 100644 --- a/lib/set_parser.cpp +++ b/lib/set_parser.cpp @@ -415,7 +415,9 @@ std::map> SetParser::parse2() { proxy_debug(PROXY_DEBUG_MYSQL_QUERY_PROCESSOR, 4, "SET parsing: v1='%s' , v2='%s' , v3='%s' , v4='%s' , v5='%s'\n", value1.c_str(), value2.c_str(), value3.c_str(), value4.c_str(), value5.c_str()); #endif // DEBUG std::string key; - if (value1 != "") { // session is specified + + bool is_session = (value1 != ""); + //if (value1 != "") { // session is specified if (value2 != "") { // isolation level key = value2; std::transform(value3.begin(), value3.end(), value3.begin(), ::toupper); @@ -425,9 +427,9 @@ std::map> SetParser::parse2() { std::transform(value5.begin(), value5.end(), value5.begin(), ::toupper); op.push_back(value5); } - } + //} std::transform(key.begin(), key.end(), key.begin(), ::tolower); - result[key] = op; + result[std::string((is_session == true) ? "SESSION:" : ":") + key] = op; } delete opt2;