From f5ea427a4d4da2bfac7a3d5ec225d2e16035b8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 1 Mar 2015 00:01:46 +0000 Subject: [PATCH] Developing support for charset , issue #223 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added functions: MySQL_Protocol::generate_COM_QUERY() MySQL_Connection::set_charset() MySQL_Session::handler___status_CHANGING_CHARSET() MySQL_Session::handler___client_DSS_QUERY_SENT___send_SET_NAMES_to_backend() Added CHANGING_CHARSET in enum session_status, and handled in MySQL_Session Still TODO: track and handle client’s change of charset --- include/MySQL_Protocol.h | 1 + include/mysql_connection.h | 1 + include/mysql_session.h | 2 ++ include/proxysql_structs.h | 1 + lib/MySQL_Protocol.cpp | 32 ++++++++++++++++++++-- lib/MySQL_Session.cpp | 55 +++++++++++++++++++++++++++++++++++--- lib/mysql_connection.cpp | 5 ++++ 7 files changed, 92 insertions(+), 5 deletions(-) diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index 97b0a08c6..6b231e01c 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -41,6 +41,7 @@ class MySQL_Protocol { bool generate_COM_QUIT(bool send, void **ptr, unsigned int *len); bool generate_COM_INIT_DB(bool send, void **ptr, unsigned int *len, char *schema); bool generate_COM_PING(bool send, void **ptr, unsigned int *len); + bool generate_COM_QUERY(bool send, void **ptr, unsigned int *len, char *query); bool generate_COM_RESET_CONNECTION(bool send, void **ptr, unsigned int *len); bool generate_COM_CHANGE_USER(bool send, void **ptr, unsigned int *len); diff --git a/include/mysql_connection.h b/include/mysql_connection.h index e63646d31..853b6b05a 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -52,6 +52,7 @@ class MySQL_Connection { // void free_mshge(); MyConnArray *set_MCA(MySQL_Connection_Pool *_MyConnPool, const char *hostname, const char *username, const char *password, const char *db, unsigned int port); bool return_to_connection_pool(); + uint8_t set_charset(uint8_t); friend class MyConnArray; }; #endif /* __CLASS_MYSQL_CONNECTION_H */ diff --git a/include/mysql_session.h b/include/mysql_session.h index 62b277be4..247863e01 100644 --- a/include/mysql_session.h +++ b/include/mysql_session.h @@ -36,6 +36,7 @@ class MySQL_Session private: bool handler___status_CHANGING_SCHEMA(PtrSize_t *); bool handler___status_CHANGING_USER_SERVER(PtrSize_t *); + bool handler___status_CHANGING_CHARSET(PtrSize_t *); void handler___status_WAITING_SERVER_DATA___STATE_QUERY_SENT(PtrSize_t *); void handler___status_WAITING_SERVER_DATA___STATE_PING_SENT(PtrSize_t *); void handler___status_WAITING_SERVER_DATA___STATE_ROW(PtrSize_t *); @@ -58,6 +59,7 @@ class MySQL_Session void handler___client_DSS_QUERY_SENT___server_DSS_NOT_INITIALIZED__get_connection(); void handler___client_DSS_QUERY_SENT___send_INIT_DB_to_backend(); void handler___client_DSS_QUERY_SENT___send_CHANGE_USER_to_backend(); + void handler___client_DSS_QUERY_SENT___send_SET_NAMES_to_backend(); public: void * operator new(size_t); diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 7a62ea429..bb947e4d8 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -57,6 +57,7 @@ enum session_status { WAITING_CLIENT_DATA, WAITING_SERVER_DATA, CHANGING_SCHEMA, + CHANGING_CHARSET, CHANGING_USER_CLIENT, CHANGING_USER_SERVER, NONE diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 19ebeddd2..5749a9d36 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -900,6 +900,28 @@ bool MySQL_Protocol::generate_COM_INIT_DB(bool send, void **ptr, unsigned int *l return true; } +bool MySQL_Protocol::generate_COM_QUERY(bool send, void **ptr, unsigned int *len, char *query) { + uint32_t query_len=strlen(query); + mysql_hdr myhdr; + myhdr.pkt_id=0; + myhdr.pkt_length=1+query_len; + unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr); + unsigned char *_ptr=(unsigned char *)l_alloc(size); + memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); + //Copy4B(_ptr, &myhdr); + int l=sizeof(mysql_hdr); + _ptr[l]=0x03; l++; + memcpy(_ptr+l, query, query_len); + + if (send==true) { (*myds)->PSarrayOUT->add((void *)_ptr,size); } + if (len) { *len=size; } + if (ptr) { *ptr=(void *)_ptr; } +#ifdef DEBUG + if (dump_pkt) { __dump_pkt(__func__,_ptr,size); } +#endif + return true; +} + //bool MySQL_Protocol::generate_COM_PING(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len) { bool MySQL_Protocol::generate_COM_PING(bool send, void **ptr, unsigned int *len) { mysql_hdr myhdr; @@ -1103,7 +1125,10 @@ bool MySQL_Protocol::generate_pkt_handshake_response(bool send, void **ptr, unsi uint32_t capabilities = CLIENT_LONG_PASSWORD | CLIENT_FOUND_ROWS | CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 | CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION | CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS | CLIENT_PS_MULTI_RESULTS ; uint32_t max_allowed_packet=1*1024*1024; - uint8_t charset=21; + assert(sess); + assert(sess->client_myds); + assert(sess->client_myds->myconn); + uint8_t charset=sess->client_myds->myconn->options.charset; uint8_t _tmp; /* pkt += sizeof(mysql_hdr); @@ -1533,7 +1558,10 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned } proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,1,"Handshake (%s auth) , capabilities:%u char:%u, use_ssl:%s\n", (capabilities & CLIENT_SECURE_CONNECTION ? "new" : "old"), user, password, pass, db, max_pkt, capabilities, charset, ((*myds)->encrypted ? "yes" : "no")); - + assert(sess); + assert(sess->client_myds); + assert(sess->client_myds->myconn); + sess->client_myds->myconn->set_charset(charset); #ifdef DEBUG if (dump_pkt) { __dump_pkt(__func__,_ptr,len); } #endif diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 50b5ff621..33ecce47a 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -465,9 +465,13 @@ __get_a_backend: handler___client_DSS_QUERY_SENT___send_INIT_DB_to_backend(); } } else { - //server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); - mybe->server_myds->DSS=STATE_QUERY_SENT_DS; - status=WAITING_SERVER_DATA; + if (client_myds->myconn->options.charset!=mybe->server_myds->myconn->options.charset || rand()%3==0) { + handler___client_DSS_QUERY_SENT___send_SET_NAMES_to_backend(); + } else { + //server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); + mybe->server_myds->DSS=STATE_QUERY_SENT_DS; + status=WAITING_SERVER_DATA; + } } } // } TRY #1 @@ -534,6 +538,12 @@ __exit_DSS__STATE_NOT_INITIALIZED: } break; + case CHANGING_CHARSET: + if (handler___status_CHANGING_CHARSET(&pkt)==false) { + return -1; + } + break; + default: assert(0); } @@ -667,6 +677,33 @@ bool MySQL_Session::handler___status_CHANGING_USER_SERVER(PtrSize_t *pkt) { return false; } +bool MySQL_Session::handler___status_CHANGING_CHARSET(PtrSize_t *pkt) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Statuses: CHANGING_CHARSET - UNKNWON\n"); + if (mybe->server_myds->myprot.process_pkt_OK((unsigned char *)pkt->ptr,pkt->size)==true) { + l_free(pkt->size,pkt->ptr); + mybe->server_myds->DSS=STATE_READY; + //mybe->myconn=server_myds->myconn; + status=WAITING_SERVER_DATA; + unsigned int k; + PtrSize_t pkt2; + for (k=0; kserver_myds->PSarrayOUTpending->len;) { + mybe->server_myds->PSarrayOUTpending->remove_index(0,&pkt2); + mybe->server_myds->PSarrayOUT->add(pkt2.ptr, pkt2.size); + mybe->server_myds->DSS=STATE_QUERY_SENT_DS; + } + return true; + } else { + l_free(pkt->size,pkt->ptr); + set_unhealthy(); + //mybe->myconn=server_myds->myconn; + // if we reach here, server_myds->DSS should be STATE_QUERY_SENT , therefore the connection to the backend should be dropped anyway + // although we enforce this here + mybe->server_myds->myconn->reusable=false; + return false; + } + return false; +} + void MySQL_Session::handler___status_WAITING_SERVER_DATA___STATE_PING_SENT(PtrSize_t *pkt) { proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Statuses: WAITING_SERVER_DATA - STATE_PING_SENT\n"); @@ -1105,6 +1142,18 @@ void MySQL_Session::handler___client_DSS_QUERY_SENT___send_INIT_DB_to_backend() status=CHANGING_SCHEMA; } +void MySQL_Session::handler___client_DSS_QUERY_SENT___send_SET_NAMES_to_backend() { + mybe->server_myds->move_from_OUT_to_OUTpending(); + mybe->server_myds->myconn->set_charset(client_myds->myconn->options.charset); + //userinfo_server.set_schemaname(userinfo_client.schemaname,strlen(userinfo_client.schemaname)); + //mybe->server_myds->myconn->userinfo->set_schemaname(client_myds->myconn->userinfo->schemaname,strlen(client_myds->myconn->userinfo->schemaname)); + //myprot_server.generate_COM_INIT_DB(true,NULL,NULL,userinfo_server.schemaname); + //mybe->server_myds->myprot.generate_COM_INIT_DB(true,NULL,NULL,mybe->server_myds->myconn->userinfo->schemaname); + mybe->server_myds->myprot.generate_COM_QUERY(true,NULL,NULL,"SET NAMES utf8"); + mybe->server_myds->DSS=STATE_QUERY_SENT_DS; + status=CHANGING_SCHEMA; +} + void MySQL_Session::handler___client_DSS_QUERY_SENT___send_CHANGE_USER_to_backend() { mybe->server_myds->move_from_OUT_to_OUTpending(); //userinfo_server.set_schemaname(userinfo_client.schemaname,strlen(userinfo_client.schemaname)); diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 212a2c2d8..9c21ee3b6 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -148,6 +148,11 @@ MySQL_Connection::~MySQL_Connection() { } }; +uint8_t MySQL_Connection::set_charset(uint8_t _c) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "Setting charset %d\n", _c); + options.charset=_c; + return _c; +} MyConnArray * MySQL_Connection::set_MCA(MySQL_Connection_Pool *_MyConnPool, const char *hostname, const char *username, const char *password, const char *db, unsigned int port) { proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "MySQL_Connection_Pool=%p, Host=%s, user=%s, pass=%s, db=%s, port=%d\n", _MyConnPool, hostname, username, password, db, port);