From 76f06572edb8fa45f3616f5ebbf813c8f1cf0230 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 3 Mar 2016 10:44:11 +0200 Subject: [PATCH] Fix to multi-statement processing Renamed is_mysql_comment_start to is_mysql_statement_end because it checks whether a statement truly ends instead of just checking comment block starts. The calculations for buffer length in readwritesplit now use the payload size instead of the buffer size. --- server/core/modutil.c | 54 ++++++++++++------- server/include/modutil.h | 2 +- .../routing/readwritesplit/readwritesplit.c | 5 +- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/server/core/modutil.c b/server/core/modutil.c index d5fa3bc0a..2c94e9127 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -893,39 +893,53 @@ char* strnchr_esc_mysql(char* ptr, char c, int len) } /** - * Check if the start of the token is the start of a MySQL style comment block - * @param ptr String with at least two non-null characters in it - * @return True if the token starts a comment block + * @brief Check if the string is the final part of a valid SQL statement + * + * This function checks whether the string pointed by @p start contains any + * tokens that are interpreted as executable commands. + * @param start String containing the statement + * @param len Length of the string + * @return True if statement contains no executable parts */ -bool is_mysql_comment_start(const char* start, int len) +bool is_mysql_statement_end(const char* start, int len) { const char *ptr = start; + bool rval = false; while (ptr < start + len && (isspace(*ptr) || *ptr == ';')) { ptr++; } - switch (*ptr) + if (ptr < start + len) { - case '-': - if (*(ptr + 1) == '-' && isspace(*(ptr + 2))) - { - return true; - } - break; + switch (*ptr) + { + case '-': + if (ptr < start + len - 2 && *(ptr + 1) == '-' && isspace(*(ptr + 2))) + { + rval = true; + } + break; - case '#': - return true; + case '#': + rval = true; + break; - case '/': - if (*(ptr + 1) == '*') - { - return true; - } - break; + case '/': + if (ptr < start + len - 1 && *(ptr + 1) == '*') + { + rval = true; + } + break; + } } - return false; + else + { + rval = true; + } + + return rval; } /** diff --git a/server/include/modutil.h b/server/include/modutil.h index 1223f880a..d8e69da99 100644 --- a/server/include/modutil.h +++ b/server/include/modutil.h @@ -71,7 +71,7 @@ mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* /** Character and token searching functions */ char* strnchr_esc(char* ptr, char c, int len); char* strnchr_esc_mysql(char* ptr, char c, int len); -bool is_mysql_comment_start(const char* start, int len); +bool is_mysql_statement_end(const char* start, int len); bool is_mysql_sp_end(const char* start, int len); #endif diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 467f0dd79..7f249176f 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -5362,7 +5362,8 @@ static void check_for_multi_stmt(ROUTER_CLIENT_SES* rses, GWBUF *buf, packet_type == MYSQL_COM_QUERY && rses->forced_node != rses->rses_master_ref) { char *ptr, *data = GWBUF_DATA(buf) + 5; - int buflen = GWBUF_LENGTH(buf) - 5; + /** Payload size without command byte */ + int buflen = gw_mysql_get_byte3((uint8_t*)GWBUF_DATA(buf)) - 1; if ((ptr = strnchr_esc_mysql(data, ';', buflen))) { @@ -5374,7 +5375,7 @@ static void check_for_multi_stmt(ROUTER_CLIENT_SES* rses, GWBUF *buf, if (ptr) { - if (ptr < data + buflen && !is_mysql_comment_start(ptr, ptr - data)) + if (ptr < data + buflen && !is_mysql_statement_end(ptr, buflen - (ptr - data))) { rses->forced_node = rses->rses_master_ref; MXS_INFO("Multi-statement query, routing all future queries to master.");