From 7469c5be52c5f254d1d5ebb7f6e0e70066fe6568 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 17 Mar 2017 10:02:27 +0200 Subject: [PATCH] Add functionality for bypassing MySQL whitespace/comments This functionality is needed both in the query classifier, where it was first created, and in the cache. --- include/maxscale/modutil.h | 20 ++++++ server/core/modutil.c | 108 +++++++++++++++++++++++++++++++++ server/core/test/testmodutil.c | 38 ++++++++++++ 3 files changed, 166 insertions(+) diff --git a/include/maxscale/modutil.h b/include/maxscale/modutil.h index f8e972243..0fdc3ff4c 100644 --- a/include/maxscale/modutil.h +++ b/include/maxscale/modutil.h @@ -53,9 +53,29 @@ GWBUF* modutil_create_mysql_err_msg(int packet_number, int merrno, const char *statemsg, const char *msg); + int modutil_count_signal_packets(GWBUF*, int, int, int*); mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* string); +/** + * Given a buffer containing a MySQL statement, this function will return + * a pointer to the first character that is not whitespace. In this context, + * comments are also counted as whitespace. For instance: + * + * "SELECT" => "SELECT" + * " SELECT => "SELECT" + * " / * A comment * / SELECT" => "SELECT" + * "-- comment\nSELECT" => "SELECT" + * + * @param sql Pointer to buffer containing a MySQL statement + * @param len Length of sql. + * + * @return The first non whitespace (including comments) character. If the + * entire buffer is only whitespace, the returned pointer will point + * to the character following the buffer (i.e. sql + len). + */ +char* modutil_MySQL_bypass_whitespace(char* sql, size_t len); + /** Character and token searching functions */ char* strnchr_esc(char* ptr, char c, int len); char* strnchr_esc_mysql(char* ptr, char c, int len); diff --git a/server/core/modutil.c b/server/core/modutil.c index 4dc26cf06..c7075d9c7 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -1213,3 +1213,111 @@ char* modutil_get_canonical(GWBUF* querybuf) return querystr; } + + +char* modutil_MySQL_bypass_whitespace(char* sql, size_t len) +{ + char *i = sql; + char *end = i + len; + + while (i != end) + { + if (isspace(*i)) + { + ++i; + } + else if (*i == '/') // Might be a comment + { + if ((i + 1 != end) && (*(i + 1) == '*')) // Indeed it was + { + i += 2; + + while (i != end) + { + if (*i == '*') // Might be the end of the comment + { + ++i; + + if (i != end) + { + if (*i == '/') // Indeed it was + { + ++i; + break; // Out of this inner while. + } + } + } + else + { + // It was not the end of the comment. + ++i; + } + } + } + else + { + // Was not a comment, so we'll bail out. + break; + } + } + else if (*i == '-') // Might be the start of a comment to the end of line + { + bool is_comment = false; + + if (i + 1 != end) + { + if (*(i + 1) == '-') // Might be, yes. + { + if (i + 2 != end) + { + if (isspace(*(i + 2))) // Yes, it is. + { + is_comment = true; + + i += 3; + + while ((i != end) && (*i != '\n')) + { + ++i; + } + + if (i != end) + { + ss_dassert(*i == '\n'); + ++i; + } + } + } + } + } + + if (!is_comment) + { + break; + } + } + else if (*i == '#') + { + ++i; + + while ((i != end) && (*i != '\n')) + { + ++i; + } + + if (i != end) + { + ss_dassert(*i == '\n'); + ++i; + } + break; + } + else + { + // Neither whitespace not start of a comment, so we bail out. + break; + } + } + + return i; +} diff --git a/server/core/test/testmodutil.c b/server/core/test/testmodutil.c index 1a45e9911..724bc9d6a 100644 --- a/server/core/test/testmodutil.c +++ b/server/core/test/testmodutil.c @@ -578,6 +578,43 @@ void test_large_packets() } } +char* bypass_whitespace(char* sql) +{ + return modutil_MySQL_bypass_whitespace(sql, strlen(sql)); +} + +void test_bypass_whitespace() +{ + char* sql; + + sql = bypass_whitespace("SELECT"); + ss_info_dassert(*sql == 'S', "1"); + + sql = bypass_whitespace(" SELECT"); + ss_info_dassert(*sql == 'S', "2"); + + sql = bypass_whitespace("\tSELECT"); + ss_info_dassert(*sql == 'S', "3"); + + sql = bypass_whitespace("\nSELECT"); + ss_info_dassert(*sql == 'S', "4"); + + sql = bypass_whitespace("/* comment */SELECT"); + ss_info_dassert(*sql == 'S', "5"); + + sql = bypass_whitespace(" /* comment */ SELECT"); + ss_info_dassert(*sql == 'S', "6"); + + sql = bypass_whitespace("-- comment\nSELECT"); + ss_info_dassert(*sql == 'S', "7"); + + sql = bypass_whitespace("-- comment\n /* comment */ SELECT"); + ss_info_dassert(*sql == 'S', "8"); + + sql = bypass_whitespace("# comment\nSELECT"); + ss_info_dassert(*sql == 'S', "9"); +} + int main(int argc, char **argv) { int result = 0; @@ -591,5 +628,6 @@ int main(int argc, char **argv) test_strnchr_esc(); test_strnchr_esc_mysql(); test_large_packets(); + test_bypass_whitespace(); exit(result); }