From 66788863b3acf0928e6974b7dbcf52bb3968a779 Mon Sep 17 00:00:00 2001 From: val Date: Thu, 9 Apr 2020 08:09:27 +0000 Subject: [PATCH] set names optimization --- include/MySQL_Session.h | 1 + lib/MySQL_Session.cpp | 86 ++++++++++++++++++++++++++++++++++++++++- lib/MySQL_Variables.cpp | 69 +++++++++++++++++++++++++-------- 3 files changed, 138 insertions(+), 18 deletions(-) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index f158d197d..db007f519 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -119,6 +119,7 @@ class MySQL_Session 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_CHANGING_CHARSET(int *_rc); bool handler_again___status_CHANGING_SCHEMA(int *); bool handler_again___status_CONNECTING_SERVER(int *); bool handler_again___status_CHANGING_USER_SERVER(int *); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 09b9f072a..8dd06bc95 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -2092,6 +2092,80 @@ bool MySQL_Session::handler_again___status_SETTING_SQL_LOG_BIN(int *_rc) { return ret; } +bool MySQL_Session::handler_again___status_CHANGING_CHARSET(int *_rc) { + assert(mybe->server_myds->myconn); + MySQL_Data_Stream *myds=mybe->server_myds; + MySQL_Connection *myconn=myds->myconn; + char msg[128]; + const MARIADB_CHARSET_INFO *ci = NULL; + const char* replace_collation = ""; + const char* not_supported_collation = ""; + + /* Validate that server can support client's charset */ + if (!validate_charset(this, SQL_CHARACTER_SET_CLIENT, *_rc)) { + return false; + } + + myds->DSS=STATE_MARIADB_QUERY; + enum session_status st=status; + if (myds->mypolls==NULL) { + thread->mypolls.add(POLLIN|POLLOUT, mybe->server_myds->fd, mybe->server_myds, thread->curtime); + } + + mysql_variables.client_set_value(this, SQL_CHARACTER_SET, mysql_variables.client_get_value(this, SQL_CHARACTER_SET_CLIENT)); + int charset = atoi(mysql_variables.client_get_value(this, SQL_CHARACTER_SET_CLIENT)); + int rc=myconn->async_set_names(myds->revents, charset); + + if (rc==0) { + __sync_fetch_and_add(&MyHGM->status.backend_set_names, 1); + myds->DSS = STATE_MARIADB_GENERIC; + st=previous_status.top(); + previous_status.pop(); + NEXT_IMMEDIATE_NEW(st); + } else { + if (rc==-1) { + // the command failed + int myerr=mysql_errno(myconn->mysql); + if (myerr >= 2000) { + if (myerr == 2019) { + proxy_error("Client trying to set a charset/collation (%u) not supported by backend (%s:%d). Changing it to %u\n", charset, myconn->parent->address, myconn->parent->port, mysql_tracked_variables[SQL_CHARACTER_SET].default_value); + } + bool retry_conn=false; + // client error, serious + proxy_error("Detected a broken connection during SET NAMES on %s , %d : %d, %s\n", myconn->parent->address, myconn->parent->port, myerr, mysql_error(myconn->mysql)); + if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { + retry_conn=true; + } + myds->destroy_MySQL_Connection_From_Pool(false); + myds->fd=0; + if (retry_conn) { + myds->DSS=STATE_NOT_INITIALIZED; + //previous_status.push(PROCESSING_QUERY); + NEXT_IMMEDIATE_NEW(CONNECTING_SERVER); + } + *_rc=-1; + return false; + } else { + proxy_warning("Error during SET NAMES: %d, %s\n", myerr, mysql_error(myconn->mysql)); + // we won't go back to PROCESSING_QUERY + st=previous_status.top(); + previous_status.pop(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(myconn->mysql)); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,mysql_errno(myconn->mysql),sqlstate,mysql_error(myconn->mysql)); + myds->destroy_MySQL_Connection_From_Pool(true); + myds->fd=0; + status=WAITING_CLIENT_DATA; + client_myds->DSS=STATE_SLEEP; + RequestEnd(myds); + } + } else { + // rc==1 , nothing to do for now + } + } + return false; +} + bool MySQL_Session::handler_again___status_SETTING_GENERIC_VARIABLE(int *_rc, const char *var_name, const char *var_value, bool no_quote, bool set_transaction) { bool ret = false; assert(mybe->server_myds->myconn); @@ -3998,6 +4072,17 @@ handler_again: } break; + case SETTING_SET_NAMES: + { + int rc=0; + if (handler_again___status_CHANGING_CHARSET(&rc)) + goto handler_again; // we changed status + if (rc==-1) { // we have an error we can't handle + handler_ret = -1; + return handler_ret; + } + } + break; case SETTING_SQL_MODE: case SETTING_SQL_SELECT_LIMIT: @@ -4014,7 +4099,6 @@ handler_again: case SETTING_NET_WRITE_TIMEOUT: case SETTING_MAX_JOIN_SIZE: case SETTING_CHARSET: - case SETTING_SET_NAMES: case SETTING_SQL_LOG_BIN: case SETTING_WSREP_SYNC_WAIT: for (auto i = 0; i < SQL_NAME_LAST; i++) { diff --git a/lib/MySQL_Variables.cpp b/lib/MySQL_Variables.cpp index 44648f4e7..22eaae76a 100644 --- a/lib/MySQL_Variables.cpp +++ b/lib/MySQL_Variables.cpp @@ -325,24 +325,59 @@ bool update_server_variable(MySQL_Session* session, int idx, int &_rc) { inline bool verify_server_variable(MySQL_Session* session, int idx, uint32_t client_hash, uint32_t server_hash) { if (client_hash != server_hash) { - 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); - break; - case PROCESSING_STMT_PREPARE: - session->previous_status.push(PROCESSING_STMT_PREPARE); - break; - case PROCESSING_STMT_EXECUTE: - session->previous_status.push(PROCESSING_STMT_EXECUTE); - break; - default: - proxy_error("Wrong status %d\n", session->status); - assert(0); - break; + uint32_t client_charset_hash = mysql_variables.client_get_hash(session, SQL_CHARACTER_SET_CLIENT); + uint32_t results_charset_hash = mysql_variables.client_get_hash(session, SQL_CHARACTER_SET_RESULTS); + uint32_t connection_charset_hash = mysql_variables.client_get_hash(session, SQL_CHARACTER_SET_CONNECTION); + uint32_t collation_hash = mysql_variables.client_get_hash(session, SQL_COLLATION_CONNECTION); + + bool process_charset_variable = ((idx == SQL_CHARACTER_SET_CLIENT) || (idx == SQL_CHARACTER_SET_RESULTS) || (idx == SQL_CHARACTER_SET_CONNECTION) || (idx == SQL_COLLATION_CONNECTION)); + bool is_set_names_hash = ((client_charset_hash == results_charset_hash) && (client_charset_hash == connection_charset_hash) && (client_charset_hash == collation_hash)); + + if (process_charset_variable && is_set_names_hash) { + 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); + break; + case PROCESSING_STMT_PREPARE: + session->previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + session->previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + proxy_error("Wrong status %d\n", session->status); + assert(0); + break; + } + session->set_status(SETTING_SET_NAMES); + mysql_variables.server_set_value(session, SQL_CHARACTER_SET_CLIENT, mysql_variables.client_get_value(session, SQL_CHARACTER_SET_CLIENT)); + mysql_variables.server_set_value(session, SQL_CHARACTER_SET_RESULTS, mysql_variables.client_get_value(session, SQL_CHARACTER_SET_RESULTS)); + mysql_variables.server_set_value(session, SQL_CHARACTER_SET_CONNECTION, mysql_variables.client_get_value(session, SQL_CHARACTER_SET_CONNECTION)); + mysql_variables.server_set_value(session, SQL_COLLATION_CONNECTION, mysql_variables.client_get_value(session, SQL_COLLATION_CONNECTION)); + mysql_variables.client_set_value(session, SQL_CHARACTER_SET, mysql_variables.client_get_value(session, SQL_CHARACTER_SET_CLIENT)); + mysql_variables.server_set_value(session, SQL_CHARACTER_SET, mysql_variables.client_get_value(session, SQL_CHARACTER_SET_CLIENT)); + return true; + } else { + 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); + break; + case PROCESSING_STMT_PREPARE: + session->previous_status.push(PROCESSING_STMT_PREPARE); + break; + case PROCESSING_STMT_EXECUTE: + session->previous_status.push(PROCESSING_STMT_EXECUTE); + break; + default: + proxy_error("Wrong status %d\n", session->status); + assert(0); + break; + } + session->set_status(mysql_tracked_variables[idx].status); + mysql_variables.server_set_value(session, idx, mysql_variables.client_get_value(session, idx)); + return true; } - session->set_status(mysql_tracked_variables[idx].status); - mysql_variables.server_set_value(session, idx, mysql_variables.client_get_value(session, idx)); - return true; + } return false; }