Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/MySQL_Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,15 @@ class MySQL_Session: public Base_Session<MySQL_Session, MySQL_Data_Stream, MySQL
bool handler_again___verify_ldap_user_variable();
bool handler_again___verify_backend_autocommit();
bool handler_again___verify_backend_session_track_gtids();
bool handler_again___verify_backend_session_track_variables();
bool handler_again___verify_backend_multi_statement();
bool handler_again___verify_backend_user_schema();
bool handler_again___verify_multiple_variables(MySQL_Connection *);
bool handler_again___status_SETTING_INIT_CONNECT(int *);
bool handler_again___status_SETTING_LDAP_USER_VARIABLE(int *);
bool handler_again___status_SETTING_SQL_MODE(int *);
bool handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *);
bool handler_again___status_SETTING_SESSION_TRACK_VARIABLES(int *);
bool handler_again___status_CHANGING_CHARSET(int *_rc);
bool handler_again___status_CHANGING_SCHEMA(int *);
bool handler_again___status_CONNECTING_SERVER(int *);
Expand Down Expand Up @@ -280,6 +282,7 @@ class MySQL_Session: public Base_Session<MySQL_Session, MySQL_Data_Stream, MySQL
int RunQuery(MySQL_Data_Stream *myds, MySQL_Connection *myconn);
void handler___status_WAITING_CLIENT_DATA();
void handler_rc0_Process_GTID(MySQL_Connection *myconn);
void handler_rc0_Process_Variables(MySQL_Connection *myconn);
void handler_rc0_RefreshActiveTransactions(MySQL_Connection* myconn);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_INIT_DB_replace_CLICKHOUSE(PtrSize_t& pkt);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY___not_mysql(PtrSize_t& pkt);
Expand Down
2 changes: 2 additions & 0 deletions include/mysql_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class MySQL_Connection {
char *ldap_user_variable;
char *ldap_user_variable_value;
bool session_track_gtids_sent;
bool session_track_variables_sent;
bool ldap_user_variable_sent;
uint8_t protocol_version;
int8_t last_set_autocommit;
Expand Down Expand Up @@ -262,6 +263,7 @@ class MySQL_Connection {
void reset();

bool get_gtid(char *buff, uint64_t *trx_id);
bool get_variables(std::unordered_map<std::string, std::string>&);
void reduce_auto_increment_delay_token() { if (auto_increment_delay_token) auto_increment_delay_token--; };

bool match_ff_req_options(const MySQL_Connection *c);
Expand Down
1 change: 1 addition & 0 deletions include/proxysql_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ enum session_status {
SETTING_NEXT_TRANSACTION_READ,
PROCESSING_EXTENDED_QUERY_SYNC,
RESYNCHRONIZING_CONNECTION,
SETTING_SESSION_TRACK_VARIABLES,
session_status___NONE // special marker
};

Expand Down
54 changes: 54 additions & 0 deletions lib/MySQL_Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,16 @@ bool MySQL_Session::handler_again___verify_backend_session_track_gtids() {
return ret;
}

bool MySQL_Session::handler_again___verify_backend_session_track_variables() {
if (mybe->server_myds->myconn->options.session_track_variables_sent == false) {
mybe->server_myds->myconn->options.session_track_variables_sent = true;
set_previous_status_mode3();
NEXT_IMMEDIATE_NEW(SETTING_SESSION_TRACK_VARIABLES);
}

return false;
}


bool MySQL_Session::handler_again___verify_multiple_variables(MySQL_Connection* myconn) {
for (auto i = 0; i < SQL_NAME_LAST_LOW_WM; i++) {
Expand Down Expand Up @@ -2565,6 +2575,12 @@ bool MySQL_Session::handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, co
free(query);
query=NULL;
}

if (rc == 0 && strncasecmp(var_name, "session_track_system_variables", strlen("session_track_system_variables")) == 0) {
char *q = (char *)"SET session_track_state_change=ON";
rc = myconn->async_send_simple_command(myds->revents, q, strlen(q));
}

if (rc==0) {
myds->revents|=POLLOUT; // we also set again POLLOUT to send a query immediately!
myds->DSS = STATE_MARIADB_GENERIC;
Expand Down Expand Up @@ -2753,6 +2769,13 @@ bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_GTIDS(int *_rc)
return ret;
}

bool MySQL_Session::handler_again___status_SETTING_SESSION_TRACK_VARIABLES(int *_rc) {
bool ret=false;
assert(mybe->server_myds->myconn);
ret = handler_again___status_SETTING_GENERIC_VARIABLE(_rc, (char *)"session_track_system_variables", "*", false);
return ret;
}

bool MySQL_Session::handler_again___status_CHANGING_SCHEMA(int *_rc) {
bool ret=false;
//fprintf(stderr,"CHANGING_SCHEMA\n");
Expand Down Expand Up @@ -4862,6 +4885,28 @@ void MySQL_Session::handler_rc0_Process_GTID(MySQL_Connection *myconn) {
}
}

void MySQL_Session::handler_rc0_Process_Variables(MySQL_Connection *myconn) {
std::unordered_map<string, string> var_map;

if(myconn->get_variables(var_map)) {
const char *variable = nullptr;
const char *value = nullptr;

for (int idx = 0 ; idx < SQL_NAME_LAST_HIGH_WM ; idx++) {
variable = mysql_tracked_variables[idx].set_variable_name;

auto itr = var_map.find(variable);
if(itr != var_map.end()) {
value = itr->second.c_str();
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session=%p, backend=%p. Notification for session_track_system_variables: variable=%s, value=%s\n", this, this->mybe, variable, value);

mysql_variables.client_set_value(this, idx, value);
mysql_variables.server_set_value(this, idx, value);
}
}
}
}

void MySQL_Session::handler_KillConnectionIfNeeded() {
if ( // two conditions
// If the server connection is in a non-idle state (ASYNC_IDLE), and the current time is greater than or equal to mybe->server_myds->wait_until
Expand Down Expand Up @@ -5059,6 +5104,10 @@ int MySQL_Session::handler() {
goto handler_again;
}

if (handler_again___verify_backend_session_track_variables()) {
goto handler_again;
}

// Optimize network traffic when we can use 'SET NAMES'
if (verify_set_names(this)) {
goto handler_again;
Expand Down Expand Up @@ -5139,6 +5188,8 @@ int MySQL_Session::handler() {

handler_rc0_Process_GTID(myconn);

handler_rc0_Process_Variables(myconn);

// if we are locked on hostgroup, the value of autocommit is copied from the backend connection
// see bug #3549
if (locked_on_hostgroup >= 0) {
Expand Down Expand Up @@ -5435,6 +5486,9 @@ bool MySQL_Session::handler_again___multiple_statuses(int *rc) {
case SETTING_SESSION_TRACK_GTIDS:
ret = handler_again___status_SETTING_SESSION_TRACK_GTIDS(rc);
break;
case SETTING_SESSION_TRACK_VARIABLES:
ret = handler_again___status_SETTING_SESSION_TRACK_VARIABLES(rc);
break;
case SETTING_SET_NAMES:
ret = handler_again___status_CHANGING_CHARSET(rc);
break;
Expand Down
44 changes: 44 additions & 0 deletions lib/mysql_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ MySQL_Connection::MySQL_Connection() {
options.init_connect_sent=false;
options.session_track_gtids = NULL;
options.session_track_gtids_sent = false;
options.session_track_variables_sent = false;
options.ldap_user_variable=NULL;
options.ldap_user_variable_value=NULL;
options.ldap_user_variable_sent=false;
Expand Down Expand Up @@ -3082,6 +3083,7 @@ void MySQL_Connection::reset() {
options.session_track_gtids = NULL;
options.session_track_gtids_sent = false;
}
options.session_track_variables_sent = false;
}

bool MySQL_Connection::get_gtid(char *buff, uint64_t *trx_id) {
Expand Down Expand Up @@ -3116,6 +3118,48 @@ bool MySQL_Connection::get_gtid(char *buff, uint64_t *trx_id) {
return ret;
}

bool MySQL_Connection::get_variables(std::unordered_map<string, string>& variables) {
bool ret = false;

if ((mysql != nullptr)
&& (mysql->net.last_errno == 0)
&& (mysql->server_status & SERVER_SESSION_STATE_CHANGED)) {
// when there is no error and status changed
const char *data;
size_t length;

if (mysql_session_track_get_first(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &length) == 0) {
string var_name(data, length);
string val;

// get_first() returns a variable_name
// get_next() will return the value
bool expect_value = true;

while (mysql_session_track_get_next(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &length) == 0) {
if (expect_value) {
val = string(data, length);
variables[var_name] = val;
// got a value in this iteration
// in the next iteration, we have to expect a variable_name
expect_value = false;
} else {
var_name = string(data, length);
// got a variable_name in this iteration
// in the next iteration, we have to expect the value of this variable
expect_value = true;
}
}

// update counters
// __sync_fetch_and_add(&myds->sess->thread->status_variables.stvar[st_var_gtid_session_collected],1);
ret = true;
}
}

return ret;
}

void MySQL_Connection::set_ssl_params(MYSQL *mysql, MySQLServers_SslParams *ssl_params) {
if (ssl_params == NULL) {
mysql_ssl_set(mysql,
Expand Down
4 changes: 3 additions & 1 deletion test/tap/groups/groups.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,7 @@
"eof_mixed_flags_queries-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ],
"eof_packet_mixed_queries-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ],
"ok_packet_mixed_queries-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ],
"test_load_from_config_validation-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ]
"test_load_from_config_validation-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ],
"mysql-show_ssl_version-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ],
"mysql-track_system_variables-t" : [ "default-g4","mysql-auto_increment_delay_multiplex=0-g4","mysql-multiplexing=false-g4","mysql-query_digests=0-g4","mysql-query_digests_keep_comment=1-g4" ]
}
87 changes: 87 additions & 0 deletions test/tap/tests/mysql-track_system_variables-t.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* @file test_track_system_variables-t.cpp
* @brief This test verifies that ProxySQL properly tracks session-specific
* system variables across the backend connections.
*/

#include <stdio.h>
#include <stdlib.h>
#include "json.hpp"
#include "mysql.h"
#include "tap.h"
#include "command_line.h"
#include "utils.h"

using nlohmann::json;

int main(int argc, char** argv) {
CommandLine cl;
if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return exit_status();
}

plan(1);

MYSQL* proxy = init_mysql_conn(cl.host, cl.port, cl.username, cl.password, true);
if (!proxy) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy));
return exit_status();
}

MYSQL_QUERY_T(proxy, "USE test");
MYSQL_QUERY_T(proxy, "SELECT 1");
MYSQL_RES* reset_result = mysql_store_result(proxy);
mysql_free_result(reset_result);

MYSQL_QUERY_T(proxy, "DROP PROCEDURE set_innodb_lock_wait_timeout");
const char* create_proc =
"CREATE PROCEDURE set_innodb_lock_wait_timeout() "
"BEGIN "
" SET innodb_lock_wait_timeout = CAST(FLOOR(50 + (RAND() * 100)) AS UNSIGNED); "
"END";

MYSQL_QUERY_T(proxy, create_proc);
MYSQL_QUERY_T(proxy, "CALL set_innodb_lock_wait_timeout()");

int set_value = -1;
MYSQL_QUERY_T(proxy, "SELECT @@innodb_lock_wait_timeout");
MYSQL_RES* result = mysql_store_result(proxy);
if (result) {
MYSQL_ROW row = mysql_fetch_row(result);
if (row) {
set_value = atoi(row[0]);
}
mysql_free_result(result);
}

MYSQL_QUERY(proxy, "PROXYSQL INTERNAL SESSION");
result = mysql_store_result(proxy);
MYSQL_ROW row = mysql_fetch_row(result);
auto j_session = nlohmann::json::parse(row[0]);
mysql_free_result(result);

int backend_value = -1;
int client_value = -1;
if (j_session.contains("backends")) {
for (auto& backend : j_session["backends"]) {
if (backend != nullptr && backend.contains("conn")) {
if (backend["conn"].contains("innodb_lock_wait_timeout")) {
backend_value = std::stoi(backend["conn"]["innodb_lock_wait_timeout"].get<std::string>());
break;
}
}
}
}
if (j_session.contains("conn")) {
if (j_session["conn"].contains("innodb_lock_wait_timeout")) {
client_value = std::stoi(j_session["conn"]["innodb_lock_wait_timeout"].get<std::string>());
}
}

ok(set_value == backend_value && set_value == client_value,
"Match session innodb_lock_wait_timeout value with backend & client variable list. Expected: %d, Backend: %d, Client: %d", set_value, backend_value, client_value);

mysql_close(proxy);
return exit_status();
}