From 83b2a2a463b3d12464586dfbc10339eade1afc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sat, 28 Oct 2017 21:53:13 +0200 Subject: [PATCH] Support for collations Issues #780 #554 #1219 --- deps/Makefile | 2 +- .../mariadb-client-library/libmariadb.c.patch | 43 +++++++++++++++++-- deps/mariadb-client-library/mysql.h.patch | 18 ++++++++ .../mysql_async.c.patch | 40 +++++++++++++---- lib/MySQL_Session.cpp | 35 ++++++++++++--- lib/MySQL_Thread.cpp | 11 +++++ lib/mysql_connection.cpp | 2 +- 7 files changed, 129 insertions(+), 22 deletions(-) diff --git a/deps/Makefile b/deps/Makefile index a9c5830da..7d0b7e6d3 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -65,7 +65,7 @@ mariadb-client-library/mariadb_client/include/my_config.h: cd mariadb-client-library/mariadb_client && cmake . cd mariadb-client-library/mariadb_client && patch libmariadb/libmariadb.c < ../libmariadb.c.patch cd mariadb-client-library/mariadb_client && patch libmariadb/net.c < ../net.c.patch -# cd mariadb-client-library/mariadb_client && patch libmariadb/mysql_async.c < ../mysql_async.c.patch + cd mariadb-client-library/mariadb_client && patch libmariadb/mysql_async.c < ../mysql_async.c.patch cd mariadb-client-library/mariadb_client && patch libmariadb/password.c < ../password.c.patch # cd mariadb-client-library/mariadb_client && patch libmariadb/ma_secure.c < ../ma_secure.c.patch cd mariadb-client-library/mariadb_client && patch include/mysql.h < ../mysql.h.patch diff --git a/deps/mariadb-client-library/libmariadb.c.patch b/deps/mariadb-client-library/libmariadb.c.patch index d16f177cf..04f0c4b97 100644 --- a/deps/mariadb-client-library/libmariadb.c.patch +++ b/deps/mariadb-client-library/libmariadb.c.patch @@ -69,11 +69,10 @@ mysql->user= s_user; mysql->passwd= s_passwd; mysql->db= s_db; -@@ -2395,6 +2404,36 @@ - /* Clear pointers for better safety */ +@@ -2396,6 +2405,36 @@ bzero((char*) &mysql->options,sizeof(mysql->options)); mysql->net.vio= 0; -+ if (mysql->free_me) + if (mysql->free_me) + my_free((gptr) mysql); + } + DBUG_VOID_RETURN; @@ -103,6 +102,42 @@ + /* Clear pointers for better safety */ + bzero((char*) &mysql->options,sizeof(mysql->options)); + mysql->net.vio= 0; - if (mysql->free_me) ++ if (mysql->free_me) my_free(mysql); } + DBUG_VOID_RETURN; +@@ -3510,19 +3549,27 @@ + DBUG_VOID_RETURN; + } + +-int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname) ++int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname, uint charsetnr) + { + const CHARSET_INFO *cs; + DBUG_ENTER("mysql_set_character_set"); + +- if (!csname) ++ if (!csname && !charsetnr) + goto error; + +- if ((cs= mysql_find_charset_name(csname))) +- { +- char buff[64]; +- +- my_snprintf(buff, 63, "SET NAMES %s", cs->csname); ++ if (csname) { ++ cs = mysql_find_charset_name(csname); ++ } else { ++ cs = mysql_find_charset_nr(charsetnr); ++ } ++ if (cs) ++ { ++ char buff[128]; ++ if (csname) { // default behavior ++ my_snprintf(buff, 63, "SET NAMES %s", cs->csname); ++ } else { ++ my_snprintf(buff, 63, "SET NAMES %s COLLATE %s", cs->csname, cs->name); ++ } + if (!mysql_real_query(mysql, buff, (uint)strlen(buff))) + { + mysql->charset= cs; diff --git a/deps/mariadb-client-library/mysql.h.patch b/deps/mariadb-client-library/mysql.h.patch index e119f2157..8ef51000b 100644 --- a/deps/mariadb-client-library/mysql.h.patch +++ b/deps/mariadb-client-library/mysql.h.patch @@ -1,3 +1,12 @@ +@@ -404,7 +404,7 @@ + unsigned long STDCALL mysql_thread_id(MYSQL *mysql); + const char * STDCALL mysql_character_set_name(MYSQL *mysql); + void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs); +-int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname); ++int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname, uint charsetnr); + + MYSQL * STDCALL mysql_init(MYSQL *mysql); + int STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, @@ -424,6 +424,7 @@ const char *unix_socket, unsigned long clientflag); @@ -6,3 +15,12 @@ int STDCALL mysql_select_db(MYSQL *mysql, const char *db); int STDCALL mysql_query(MYSQL *mysql, const char *q); int STDCALL mysql_send_query(MYSQL *mysql, const char *q, +@@ -515,7 +516,7 @@ + int STDCALL mysql_stmt_close_start(my_bool *ret, MYSQL_STMT *stmt); + int STDCALL mysql_stmt_close_cont(my_bool *ret, MYSQL_STMT * stmt, int status); + int STDCALL mysql_set_character_set_start(int *ret, MYSQL *mysql, +- const char *csname); ++ const char *csname, uint charsetnr); + int STDCALL mysql_set_character_set_cont(int *ret, MYSQL *mysql, + int status); + int STDCALL mysql_change_user_start(my_bool *ret, MYSQL *mysql, diff --git a/deps/mariadb-client-library/mysql_async.c.patch b/deps/mariadb-client-library/mysql_async.c.patch index ef4712767..18fb96880 100644 --- a/deps/mariadb-client-library/mysql_async.c.patch +++ b/deps/mariadb-client-library/mysql_async.c.patch @@ -1,9 +1,31 @@ -@@ -805,7 +805,7 @@ - int res; - - /* It is legitimate to have NULL sock argument, which will do nothing. */ -- if (sock) -+ if (sock && sock->net.vio) - { - res= mysql_close_slow_part_start(sock); - /* If we need to block, return now and do the rest in mysql_close_cont(). */ +@@ -570,19 +570,20 @@ + struct mysql_set_character_set_params { + MYSQL *mysql; + const char *csname; ++ uint charsetnr; + }; + static void + mysql_set_character_set_start_internal(void *d) + { + MK_ASYNC_INTERNAL_BODY( + mysql_set_character_set, +- (parms->mysql, parms->csname), ++ (parms->mysql, parms->csname, parms->charsetnr), + parms->mysql, + int, + r_int) + } + int STDCALL +-mysql_set_character_set_start(int *ret, MYSQL *mysql, const char *csname) ++mysql_set_character_set_start(int *ret, MYSQL *mysql, const char *csname, uint charsetnr) + { + MK_ASYNC_START_BODY( + mysql_set_character_set, +@@ -591,6 +592,7 @@ + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.csname= csname; ++ parms.charsetnr= charsetnr; + }, + 1, + r_int, diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index c9ddce75f..bdf8b5ca2 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -16,6 +16,7 @@ #define EXPMARIA extern const CHARSET_INFO * proxysql_find_charset_name(const char * const name); +extern CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname, const char *collatename); extern MySQL_Authentication *GloMyAuth; extern ProxySQL_Admin *GloAdmin; @@ -676,16 +677,36 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { pkt->ptr=pkt_2.ptr; } } - if ( (pkt->size < 35) && (pkt->size > 15) && (strncasecmp((char *)"SET NAMES ",(char *)pkt->ptr+5,10)==0) ) { + if ( (pkt->size < 100) && (pkt->size > 15) && (strncasecmp((char *)"SET NAMES ",(char *)pkt->ptr+5,10)==0) ) { char *unstripped=strndup((char *)pkt->ptr+15,pkt->size-15); - char *name=trim_spaces_and_quotes_in_place(unstripped); - const CHARSET_INFO * c = proxysql_find_charset_name(name); + char *csname=trim_spaces_and_quotes_in_place(unstripped); + bool collation_specified = false; + unsigned int charsetnr = 0; + const CHARSET_INFO * c; + char * collation_name = NULL; + if (strcasestr(csname," COLLATE ")) { + collation_specified = true; + collation_name = strcasestr(csname," COLLATE ") + strlen(" COLLATE "); + char *_s=index(csname,' '); + *_s = '\0'; + c = proxysql_find_charset_collate_names(csname,collation_name); + } else { + c = proxysql_find_charset_name(csname); + } client_myds->DSS=STATE_QUERY_SENT_NET; if (!c) { - char *m=(char *)"Unknown character set: '%s'"; - char *errmsg=(char *)malloc(strlen(name)+strlen(m)); - sprintf(errmsg,m,name); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1115,(char *)"#42000",errmsg); + char *m = NULL; + char *errmsg = NULL; + if (collation_specified) { + m=(char *)"Unknown character set '%s' or collation '%s'"; + errmsg=(char *)malloc(strlen(csname)+strlen(collation_name)+strlen(m)); + sprintf(errmsg,m,csname,collation_name); + } else { + m=(char *)"Unknown character set: '%s'"; + errmsg=(char *)malloc(strlen(csname)+strlen(m)); + sprintf(errmsg,m,csname); + } + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,1,1115,(char *)"42000",errmsg); free(errmsg); } else { client_myds->myconn->set_charset(c->nr); diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 352fcefa4..a9ba320be 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -45,6 +45,17 @@ CHARSET_INFO * proxysql_find_charset_name(const char *name) { return NULL; } +CHARSET_INFO * proxysql_find_charset_collate_names(const char *csname, const char *collatename) { + CHARSET_INFO *c = (CHARSET_INFO *)compiled_charsets; + do { + if (!strcasecmp(c->csname, csname) && !strcasecmp(c->name, collatename)) { + return c; + } + ++c; + } while (c[0].nr != 0); + return NULL; +} + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 02950d5f9..85e5b71bd 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -471,7 +471,7 @@ void MySQL_Connection::set_names_start() { proxy_error("Not existing charset number %u\n", options.charset); assert(0); } - async_exit_status = mysql_set_character_set_start(&interr,mysql, c->csname); + async_exit_status = mysql_set_character_set_start(&interr,mysql, NULL, options.charset); } void MySQL_Connection::set_names_cont(short event) {