From f3e7f039980f781b1cd87e07e1ad6e2ba104fe8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Wed, 9 Jan 2019 16:01:17 +1100 Subject: [PATCH] Implementation of COM_FIELD_LIST Port from 1.4.16 to 2.0.6 --- include/MySQL_Data_Stream.h | 3 ++ include/MySQL_Protocol.h | 5 ++- include/gen_utils.h | 1 + lib/MySQL_Protocol.cpp | 82 +++++++++++++++++++++++++++++++++---- lib/MySQL_Session.cpp | 17 +++++++- lib/gen_utils.cpp | 33 +++++++++++++++ lib/mysql_data_stream.cpp | 9 +++- 7 files changed, 139 insertions(+), 11 deletions(-) diff --git a/include/MySQL_Data_Stream.h b/include/MySQL_Data_Stream.h index 24bc08658..6962ef003 100644 --- a/include/MySQL_Data_Stream.h +++ b/include/MySQL_Data_Stream.h @@ -147,6 +147,9 @@ class MySQL_Data_Stream uint8_t pkt_sid; + bool com_field_list; + char *com_field_wild; + MySQL_Data_Stream(); ~MySQL_Data_Stream(); diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index dae46f8ea..3f21e173b 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -30,6 +30,7 @@ class MySQL_ResultSet { void add_eof(); void add_err(MySQL_Data_Stream *_myds); bool get_resultset(PtrSizeArray *PSarrayFinal); + //bool generate_COM_FIELD_LIST_response(PtrSizeArray *PSarrayFinal); unsigned char *buffer; unsigned int buffer_used; void buffer_to_PSarrayOut(bool _last=false); @@ -50,10 +51,10 @@ class MySQL_Prepared_Stmt_info { class MySQL_Protocol { private: - MySQL_Data_Stream **myds; MySQL_Connection_userinfo *userinfo; MySQL_Session *sess; public: + MySQL_Data_Stream **myds; #ifdef DEBUG bool dump_pkt; #endif @@ -108,5 +109,7 @@ class MySQL_Protocol { void generate_STMT_PREPARE_RESPONSE_OK(uint8_t sequence_id, uint32_t stmt_id); stmt_execute_metadata_t * get_binds_from_pkt(void *ptr, unsigned int size, MySQL_STMT_Global_info *stmt_info, stmt_execute_metadata_t **stmt_meta); + + bool generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt); }; #endif /* __CLASS_MYSQL_PROTOCOL_H */ diff --git a/include/gen_utils.h b/include/gen_utils.h index aa3f59fcf..edbfb06ea 100644 --- a/include/gen_utils.h +++ b/include/gen_utils.h @@ -231,3 +231,4 @@ char *escape_string_single_quotes(char *input, bool free_it); int remove_spaces(const char *); char *trim_spaces_in_place(char *str); char *trim_spaces_and_quotes_in_place(char *str); +bool mywildcmp(const char *p, const char *str); diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 48f7baa2e..530a471d2 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -2164,6 +2164,52 @@ stmt_execute_metadata_t * MySQL_Protocol::get_binds_from_pkt(void *ptr, unsigned return ret; } +bool MySQL_Protocol::generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt) { + PtrSize_t n_pkt; + unsigned int o_pkt_size = pkt->size; + char *pkt_ptr = (char *)pkt->ptr; + + pkt_ptr+=5; + // some sanity check + void *a = NULL; + a = memchr((void *)pkt_ptr, 0, o_pkt_size-5); + if (a==NULL) return false; // we failed to parse + char *tablename = strdup(pkt_ptr); + unsigned int wild_len = o_pkt_size - 5 - strlen(tablename) - 1; + char *wild = NULL; + if (wild_len > 0) { + pkt_ptr+=strlen(tablename); + pkt_ptr++; + wild=strndup(pkt_ptr,wild_len); + } + char *q = NULL; + if ((*myds)->com_field_wild) { + free((*myds)->com_field_wild); + (*myds)->com_field_wild=NULL; + } + if (wild) { + (*myds)->com_field_wild=strdup(wild); + } + + char *qt = (char *)"SELECT * FROM %s WHERE 1=0"; + q = (char *)malloc(strlen(qt)+strlen(tablename)); + sprintf(q,qt,tablename); + l_free(pkt->size, pkt->ptr); + pkt->size = strlen(q)+5; + mysql_hdr Hdr; + Hdr.pkt_id=1; + Hdr.pkt_length = pkt->size - 4; + pkt->ptr=malloc(pkt->size); + memcpy(pkt->ptr,&Hdr,sizeof(mysql_hdr)); + memset((char *)pkt->ptr+4,3,1); // COM_QUERY + memcpy((char *)pkt->ptr+5,q,pkt->size-5); + + if (wild) free(wild); + free(tablename); + free(q); + return true; +} + MySQL_ResultSet::MySQL_ResultSet() { buffer = NULL; //reset_pid = true; @@ -2173,6 +2219,7 @@ void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, resultset_completed=false; myprot=_myprot; mysql=_my; + MySQL_Data_Stream * c_myds = *(myprot->myds); if (buffer==NULL) { //if (_stmt==NULL) { // we allocate this buffer only for not prepared statements // removing the previous assumption. We allocate this buffer also for prepared statements @@ -2203,17 +2250,25 @@ void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, if (myprot==NULL) { return; // this is a mirror } - myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields,this); - sid++; - //PSarrayOUT->add(pkt.ptr,pkt.size); - resultset_size+=pkt.size; + if (c_myds->com_field_list==false) { + myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields,this); + sid++; + resultset_size+=pkt.size; + } // columns description for (unsigned int i=0; igenerate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,false,0,NULL,this); - //PSarrayOUT->add(pkt.ptr,pkt.size); - resultset_size+=pkt.size; - sid++; + if (c_myds->com_field_list==false) { + myprot->generate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,false,0,NULL,this); + resultset_size+=pkt.size; + sid++; + } else { + if (c_myds->com_field_wild==NULL || mywildcmp(c_myds->com_field_wild,field->name)) { + myprot->generate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,true,4,(char *)"null",this); + resultset_size+=pkt.size; + sid++; + } + } } // first EOF unsigned int nTrx=myds->sess->NumActiveTransactions(); @@ -2230,9 +2285,11 @@ void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, if (RESULTSET_BUFLEN <= (buffer_used + 9)) { buffer_to_PSarrayOut(); } + if (myds->com_field_list==false) { myprot->generate_pkt_EOF(false, NULL, NULL, sid, 0, setStatus, this); sid++; resultset_size += 9; + } //} if (_stmt) { // binary protocol , we also assume we have ALL the resultset buffer_to_PSarrayOut(); @@ -2402,6 +2459,15 @@ void MySQL_ResultSet::add_err(MySQL_Data_Stream *_myds) { resultset_completed=true; } +/* +bool MySQL_ResultSet::get_COM_FIELD_LIST_response(PtrSizeArray *PSarrayFinal) { + transfer_started=true; + if (myprot) { + } + return resultset_completed; +} +*/ + bool MySQL_ResultSet::get_resultset(PtrSizeArray *PSarrayFinal) { transfer_started=true; if (myprot) { diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index b6115ffc1..b7106c03a 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -2808,6 +2808,20 @@ __get_pkts_from_client: c=*((unsigned char *)pkt.ptr+sizeof(mysql_hdr)); } } + client_myds->com_field_list=false; // default + if (c == _MYSQL_COM_FIELD_LIST) { + if (session_type == PROXYSQL_SESSION_MYSQL) { + MySQL_Protocol *myprot=&client_myds->myprot; + bool rcp = myprot->generate_COM_QUERY_from_COM_FIELD_LIST(&pkt); + if (rcp) { + // all went well + c=*((unsigned char *)pkt.ptr+sizeof(mysql_hdr)); + client_myds->com_field_list=true; + } else { + // parsing failed, proxysql will return not suppported command + } + } + } switch ((enum_mysql_command)c) { case _MYSQL_COM_QUERY: __sync_add_and_fetch(&thread->status_variables.queries,1); @@ -5510,9 +5524,10 @@ void MySQL_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *My bool transfer_started=MyRS->transfer_started; bool resultset_completed=MyRS->get_resultset(client_myds->PSarrayOUT); CurrentQuery.rows_sent = MyRS->num_rows; + bool com_field_list=client_myds->com_field_list; assert(resultset_completed); // the resultset should always be completed if MySQL_Result_to_MySQL_wire is called if (transfer_started==false) { // we have all the resultset when MySQL_Result_to_MySQL_wire was called - if (qpo && qpo->cache_ttl>0) { // the resultset should be cached + if (qpo && qpo->cache_ttl>0 && com_field_list==false) { // the resultset should be cached if (mysql_errno(mysql)==0) { // no errors if ( (qpo->cache_empty_result==1) diff --git a/lib/gen_utils.cpp b/lib/gen_utils.cpp index 4e32e3187..cf6de2b66 100644 --- a/lib/gen_utils.cpp +++ b/lib/gen_utils.cpp @@ -107,6 +107,39 @@ char *trim_spaces_and_quotes_in_place(char *str) { return str; } + +bool mywildcmp(const char *p, const char *str) { + if (*p == '\0') { + if (*str == '\0') { + return true; + } else { + return false; + } + } + + if (*p == '_' || *p == *str) { + if (*str == '\0') { + return false; + } else { + return mywildcmp(p + 1, str + 1); + } + } + + if (*p == '%') { + if (mywildcmp(p + 1, str)) { + return true; + } else { + if (*str == '\0') { + return false; + } else { + return mywildcmp(p, str + 1); + } + } + } + return false; +} + + void * PtrSizeArray::operator new(size_t size) { return l_alloc(size); } diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 630f0e52c..6cc61d034 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -216,10 +216,12 @@ MySQL_Data_Stream::MySQL_Data_Stream() { CompPktOUT.partial=0; multi_pkt.ptr=NULL; multi_pkt.size=0; - + statuses.questions = 0; statuses.myconnpoll_get = 0; statuses.myconnpoll_put = 0; + + com_field_wild=NULL; } // Destructor @@ -242,6 +244,11 @@ MySQL_Data_Stream::~MySQL_Data_Stream() { free_mysql_real_query(); + if (com_field_wild) { + free(com_field_wild); + com_field_wild=NULL; + } + proxy_debug(PROXY_DEBUG_NET,1, "Shutdown Data Stream. Session=%p, DataStream=%p\n" , sess, this); PtrSize_t pkt; if (PSarrayIN) {