From 544e64a301a9d7ae1658135bb3403f0df50b704e Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Mon, 18 Aug 2014 14:19:46 +0300 Subject: [PATCH 01/16] query_classifier: implemented skygw_get_canonical which returns a copy of original string with given substrings being replaced with questionmarks. skygw_utils.cc: added string replacement function for use of skygw_get_canonical --- query_classifier/query_classifier.cc | 44 +++++++++++++++++- query_classifier/query_classifier.h | 2 + utils/skygw_utils.cc | 68 +++++++++++++++++++++++++++- utils/skygw_utils.h | 2 + 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 889940e00..184ed4f9d 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -412,7 +412,7 @@ static skygw_query_type_t resolve_query_type( ss_info_dassert(thd != NULL, ("thd is NULL\n")); - force_data_modify_op_replication = FALSE; + force_data_modify_op_replication = FALSE; lex = thd->lex; /** SELECT ..INTO variable|OUTFILE|DUMPFILE */ @@ -816,3 +816,45 @@ char* skygw_query_classifier_get_stmtname( return ((THD *)(mysql->thd))->lex->prepared_stmt_name.str; } + +char* skygw_get_canonical( + MYSQL* mysql, + char* querystr) +{ + THD* thd; + LEX* lex; + bool found = false; + char* newstr; + + thd = (THD *)mysql->thd; + lex = thd->lex; + Item* item; + + for (item=thd->free_list; item != NULL; item=item->next) + { + Item::Type itype; + + itype = item->type(); + + if (itype == Item::STRING_ITEM || itype == Item::INT_ITEM) + { + if (!found) + { + newstr = replace_str(querystr, + item->name, + "?"); + found = true; + } + else + { + char* prevstr = newstr; + + newstr = replace_str(prevstr, + item->name, + "?"); + free(prevstr); + } + } + } /*< for */ + return newstr; +} \ No newline at end of file diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index 31f9cf44e..fa7cefc78 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -60,6 +60,8 @@ skygw_query_type_t skygw_query_classifier_get_type( /** Free THD context and close MYSQL */ void skygw_query_classifier_free(MYSQL* mysql); char* skygw_query_classifier_get_stmtname(MYSQL* mysql); +char* skygw_get_canonical(MYSQL* mysql, char* querystr); + EXTERN_C_BLOCK_END diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 1ed569962..c7175c76f 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -23,7 +23,7 @@ #include #include #include - +#include #include "skygw_debug.h" #include "skygw_types.h" #include "skygw_utils.h" @@ -1863,3 +1863,69 @@ void skygw_file_done( free(file); } } + +/** + * Replaces in the string str all the occurrences of the source string old with + * the destination string new. The lengths of the strings old and new may differ. + * The string new may be of any length, but the string "old" must be of non-zero + * length - the penalty for providing an empty string for the "old" parameter is + * an infinite loop. In addition, none of the three parameters may be NULL. + * + * @param str String to be modified + * @param old Substring to be replaced + * @param new Replacement + * @return String with replacements in new memory area or NULL if memory + * allocation failed. + * Dependencies: For this function to compile, you will need to also #include + * the following files: , and . + * + * Thanks, to Laird Shaw who implemented most of this function. + */ +char* replace_str ( + const char *str, + const char *old, + const char *replacement) +{ + char* ret; + char* r; + const char* p; + const char* q; + size_t oldlen; + size_t count; + size_t retlen; + size_t newlen; + + oldlen = strlen(old); + newlen = strlen(replacement); + + if (oldlen != newlen) + { + for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) + { + count++; + } + /* this is undefined if p - str > PTRDIFF_MAX */ + retlen = p - str + strlen(p) + count * (newlen - oldlen); + } + else + { + retlen = strlen(str); + } + if ((ret = (char *)malloc(retlen + 1)) == NULL) + { + return NULL; + } + + for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) + { + /* this is undefined if q - p > PTRDIFF_MAX */ + ptrdiff_t l = q - p; + memcpy(r, p, l); + r += l; + memcpy(r, replacement, newlen); + r += newlen; + } + strcpy(r, p); + + return ret; +} \ No newline at end of file diff --git a/utils/skygw_utils.h b/utils/skygw_utils.h index 992f169d2..854c82541 100644 --- a/utils/skygw_utils.h +++ b/utils/skygw_utils.h @@ -191,5 +191,7 @@ int skygw_rwlock_unlock(skygw_rwlock_t* rwlock); int skygw_rwlock_init(skygw_rwlock_t** rwlock); int atomic_add(int *variable, int value); +char* replace_str(const char* str, const char* old, const char* replacement); + #endif /* SKYGW_UTILS_H */ From fb3a950a18bb08abef763f2883c5ad18ac720ecc Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Tue, 19 Aug 2014 09:07:18 +0300 Subject: [PATCH 02/16] Completed skygw_get_canonical by adding NULL checks and debug assertions. Replacable literal types are now INT_ITEM, STRING_ITEM, DECIMAL_ITEM, REAL_ITEM, VARBIN_ITEM and NULL_ITEM. --- query_classifier/query_classifier.cc | 46 +++++++++++++++++++++------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 184ed4f9d..77b939819 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -817,6 +817,20 @@ char* skygw_query_classifier_get_stmtname( } +/** + * Replace user-provided literals with question marks. Return a copy of the + * querystr with replacements. + * + * @param mysql Database pointer + * @param querystr Query string + * + * @return Copy of querystr where literals are replaces with question marks or + * NULL if querystr is NULL, thread context or lex are NULL or if replacement + * function fails. + * + * Replaced literal types are STRING_ITEM,INT_ITEM,DECIMAL_ITEM,REAL_ITEM, + * VARBIN_ITEM,NULL_ITEM + */ char* skygw_get_canonical( MYSQL* mysql, char* querystr) @@ -824,11 +838,19 @@ char* skygw_get_canonical( THD* thd; LEX* lex; bool found = false; - char* newstr; + char* newstr = NULL; + Item* item; - thd = (THD *)mysql->thd; - lex = thd->lex; - Item* item; + ss_dassert(mysql != NULL && querystr != NULL); + + if (querystr == NULL || + mysql == NULL || + (thd = (THD *)mysql->thd) == NULL || + (lex = thd->lex) == NULL) + { + ss_dassert(thd != NULL && lex != NULL); + goto retblock; + } for (item=thd->free_list; item != NULL; item=item->next) { @@ -836,25 +858,27 @@ char* skygw_get_canonical( itype = item->type(); - if (itype == Item::STRING_ITEM || itype == Item::INT_ITEM) + if (itype == Item::STRING_ITEM || + itype == Item::INT_ITEM || + itype == Item::DECIMAL_ITEM || + itype == Item::REAL_ITEM || + itype == Item::VARBIN_ITEM || + itype == Item::NULL_ITEM) { if (!found) { - newstr = replace_str(querystr, - item->name, - "?"); + newstr = replace_str(querystr, item->name, "?"); found = true; } else { char* prevstr = newstr; - newstr = replace_str(prevstr, - item->name, - "?"); + newstr = replace_str(prevstr, item->name, "?"); free(prevstr); } } } /*< for */ +retblock: return newstr; } \ No newline at end of file From 13dfd34d5dcc00e13167280dfbb7852c0fc15a1d Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Tue, 19 Aug 2014 09:39:40 +0300 Subject: [PATCH 03/16] Test use for cacnonical query function. Effective in debug build only. --- .../routing/readwritesplit/readwritesplit.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index a0c241920..24262bc6d 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1322,6 +1322,23 @@ static int routeQuery( } } return_ret: +#if defined(SS_DEBUG) + if (mysql != NULL && true) + { + char* canonical_query_str; + + canonical_query_str = skygw_get_canonical(mysql, querystr); + + if (canonical_query_str != NULL) + { + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Canonical version: %s", + canonical_query_str))); + free(canonical_query_str); + } + } +#endif if (plainsqlbuf != NULL) { gwbuf_free(plainsqlbuf); @@ -3647,4 +3664,3 @@ static BACKEND *get_root_master(backend_ref_t *servers, int router_nservers) { } return master_host; } - From e329d8cf13dc56312b0f15298e5da3c41d4b7883 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 20 Aug 2014 15:15:45 +0300 Subject: [PATCH 04/16] Fix to bug #479, http://bugs.skysql.com/show_bug.cgi?id=479 service.c was counting unfound filters towards the filter chain size. --- server/core/service.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/core/service.c b/server/core/service.c index 1102dabb4..080a65d8e 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -675,6 +675,7 @@ int n = 0; "Unable to find filter '%s' for service '%s'\n", trim(ptr), service->name ))); + n--; } flist[n] = NULL; ptr = strtok_r(NULL, "|", &brkt); From c501d4d4e1f49086c60a2af8e3afabc1944a737f Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Wed, 20 Aug 2014 22:10:36 +0300 Subject: [PATCH 05/16] Changes related to canonical query format implementation. query_classifier.cc: Now query can be parsed outside query_classifier_get_type by calling function parse_query. It creates parsing_info_t struct which is then added to the GWBUF which also includes the query. Parsing information follows the buffered query and it is freed at the same time with query buffer, in gwbuf_free. buffer.c: additions of parsing information to gwbuf struct. modutil.c: added function which returns query from GWBUF in plain text string. readwritesplit.c:routeQuery now only calls query_classifier_get_type to get the query type instead of extracting plain text query from the GWBUF buffer. --- query_classifier/makefile | 8 + query_classifier/query_classifier.cc | 365 ++++++++++++------ query_classifier/query_classifier.h | 17 +- server/core/buffer.c | 16 +- server/core/modutil.c | 55 +++ server/include/buffer.h | 23 +- server/include/modutil.h | 2 + .../routing/readwritesplit/readwritesplit.c | 72 +--- utils/skygw_debug.h | 10 +- 9 files changed, 391 insertions(+), 177 deletions(-) diff --git a/query_classifier/makefile b/query_classifier/makefile index b08f0a9bd..35ef36d2a 100644 --- a/query_classifier/makefile +++ b/query_classifier/makefile @@ -8,6 +8,8 @@ SRCS := query_classifier.cc UTILS_PATH := $(ROOT_PATH)/utils QUERY_CLASSIFIER_PATH := $(ROOT_PATH)/query_classifier LOG_MANAGER_PATH := $(ROOT_PATH)/log_manager +SERVER_INC_PATH := $(ROOT_PATH)/server/include +MODULE_INC_PATH := $(ROOT_PATH)/server/modules/include makeall: clean all @@ -43,6 +45,9 @@ libcomp: $(CPP) -c $(CFLAGS) \ $(MYSQL_HEADERS) \ -I$(LOG_MANAGER_PATH) \ + -I$(SERVER_INC_PATH) \ + -I$(MODULE_INC_PATH) \ + -I$(UTILS_PATH) \ -I./ \ -fPIC ./query_classifier.cc -o query_classifier.o @@ -66,6 +71,9 @@ depend: $(CPP) -M $(CFLAGS) \ $(MYSQL_HEADERS) \ -I$(LOG_MANAGER_PATH) \ + -I$(SERVER_INC_PATH) \ + -I$(MODULE_INC_PATH) \ + -I$(UTILS_PATH) \ -I./ \ $(SRCS) > depend diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 77b939819..7354d9639 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -34,6 +34,7 @@ #include "../utils/skygw_types.h" #include "../utils/skygw_debug.h" #include +#include #include #include @@ -83,122 +84,146 @@ static bool skygw_stmt_causes_implicit_commit( static int is_autocommit_stmt( LEX* lex); -/** - * @node (write brief function description here) - * - * Parameters: - * @param query_str - - * - * - * @param client_flag - - * - * - * @return - * +static bool query_is_parsed( + GWBUF* buf); + +static void parsing_info_set_plain_str(void* ptr, + char* str); + +/** + * Calls parser for the query includede in the buffer. Creates and adds parsing + * information to buffer if it doesn't exist already. Resolves the query type. * - * @details (write detailed description here) - * + * @param querybuf buffer including the query and possibly the parsing information + * + * @return query type */ -skygw_query_type_t skygw_query_classifier_get_type( - const char* query, - unsigned long client_flags, - MYSQL** p_mysql) +skygw_query_type_t query_classifier_get_type( + GWBUF* querybuf) { - MYSQL* mysql; - char* query_str; - const char* user = "skygw"; - const char* db = "skygw"; - THD* thd; + MYSQL* mysql; skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN; - bool failp = FALSE; - - ss_info_dassert(query != NULL, ("query_str is NULL")); + bool succp; - query_str = const_cast(query); - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Query : \"%s\"", query_str))); + ss_info_dassert(querybuf != NULL, ("querybuf is NULL")); - /** Get server handle */ - mysql = mysql_init(NULL); - - if (mysql == NULL) { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : call to mysql_real_connect failed due %d, %s.", - mysql_errno(mysql), - mysql_error(mysql)))); - - mysql_library_end(); - goto return_qtype; - } - - if (p_mysql != NULL) + /** Create parsing info for the query and store it to buffer */ + if (!query_is_parsed(querybuf)) { - *p_mysql = mysql; + succp = parse_query(querybuf); } - /** Set methods and authentication to mysql */ - mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_skygw"); - mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL); - mysql->methods = &embedded_methods; - mysql->user = my_strdup(user, MYF(0)); - mysql->db = my_strdup(db, MYF(0)); - mysql->passwd = NULL; - - /** Get one or create new THD object to be use in parsing */ - thd = get_or_create_thd_for_parsing(mysql, query_str); - - if (thd == NULL) + /** Read thd pointer and resolve the query type with it. */ + if (succp) { - skygw_query_classifier_free(mysql); - *p_mysql = NULL; - goto return_qtype; - } - /** - * Create parse_tree inside thd. - * thd and even lex are readable even if parser failed so let it - * continue despite failure. - */ - failp = create_parse_tree(thd); - qtype = resolve_query_type(thd); + parsing_info_t* pi = (parsing_info_t*)gwbuf_get_parsing_info(querybuf); + mysql = (MYSQL *)pi->pi_handle; - if (p_mysql == NULL) - { - skygw_query_classifier_free(mysql); + /** Find out the query type */ + if (mysql != NULL) + { + qtype = resolve_query_type((THD *)mysql->thd); + } } -return_qtype: return qtype; } - -void skygw_query_classifier_free( - MYSQL* mysql) +/** + * Create parsing info and try to parse the query included in the query buffer. + * Store pointer to created parse_tree_t object to buffer. + * + * @param querybuf buffer including the query and possibly the parsing information + * + * @return true if succeed, false otherwise + */ +bool parse_query ( + GWBUF* querybuf) { - if (mysql->thd != NULL) + bool succp; + THD* thd; + uint8_t* data; + size_t len; + char* query_str; + parsing_info_t* pi; + + CHK_GWBUF(querybuf); + ss_dassert(!query_is_parsed(querybuf)); + + if (querybuf->gwbuf_parsing_info == NULL) { - (*mysql->methods->free_embedded_thd)(mysql); - mysql->thd = NULL; + /** Create parsing info */ + querybuf->gwbuf_parsing_info = parsing_info_init(parsing_info_done); } - mysql_close(mysql); - mysql_thread_end(); -} + + if (querybuf->gwbuf_parsing_info == NULL) + { + succp = false; + goto retblock; + } + /** Extract query and copy it to different buffer */ + data = (uint8_t*)GWBUF_DATA(querybuf); + len = MYSQL_GET_PACKET_LEN(data)-1; /*< distract 1 for packet type byte */ + query_str = (char *)malloc(len+1); + + if (query_str == NULL) + { + succp = false; + goto retblock; + } + memcpy(query_str, &data[5], len); + memset(&query_str[len], 0, 1); + parsing_info_set_plain_str(querybuf->gwbuf_parsing_info, query_str); + + /** Get one or create new THD object to be use in parsing */ + pi = (parsing_info_t *)querybuf->gwbuf_parsing_info; + thd = get_or_create_thd_for_parsing((MYSQL *)pi->pi_handle, query_str); + + if (thd == NULL) + { + parsing_info_done(querybuf->gwbuf_parsing_info); + querybuf->gwbuf_parsing_info = NULL; + succp = false; + goto retblock; + } + /** + * Create parse_tree inside thd. + * thd and lex are readable even if creating parse tree fails. + */ + create_parse_tree(thd); + succp = true; +retblock: + return succp; +} +/** + * If buffer has non-NULL gwbuf_parsing_info it is parsed and it has parsing + * information included. + * + * @param buf buffer being examined + * + * @return true or false + */ +static bool query_is_parsed( + GWBUF* buf) +{ + if (buf->gwbuf_parsing_info != NULL) + { + return true; + } + return false; +} -/** - * @node (write brief function description here) + +/** + * Create a thread context, thd, init embedded server, connect to it, and allocate + * query to thd. * * Parameters: - * @param mysql - - * - * - * @param query_str - - * - * - * @return - * + * @param mysql Database handle * - * @details (write detailed description here) + * @param query_str Query in plain txt string + * + * @return Thread context pointer * */ static THD* get_or_create_thd_for_parsing( @@ -821,8 +846,7 @@ char* skygw_query_classifier_get_stmtname( * Replace user-provided literals with question marks. Return a copy of the * querystr with replacements. * - * @param mysql Database pointer - * @param querystr Query string + * @param querybuf GWBUF buffer including necessary parsing info * * @return Copy of querystr where literals are replaces with question marks or * NULL if querystr is NULL, thread context or lex are NULL or if replacement @@ -832,23 +856,32 @@ char* skygw_query_classifier_get_stmtname( * VARBIN_ITEM,NULL_ITEM */ char* skygw_get_canonical( - MYSQL* mysql, - char* querystr) + GWBUF* querybuf) { - THD* thd; - LEX* lex; - bool found = false; - char* newstr = NULL; - Item* item; + parsing_info_t* pi; + MYSQL* mysql; + THD* thd; + LEX* lex; + bool found = false; + char* newstr = NULL; + Item* item; + char* querystr; - ss_dassert(mysql != NULL && querystr != NULL); - - if (querystr == NULL || - mysql == NULL || + if (querybuf->gwbuf_parsing_info == NULL) + { + goto retblock; + } + pi = (parsing_info_t*)querybuf->gwbuf_parsing_info; + + if ((querystr = pi->pi_query_plain_str) == NULL || + (mysql = (MYSQL *)pi->pi_handle) == NULL || (thd = (THD *)mysql->thd) == NULL || (lex = thd->lex) == NULL) { - ss_dassert(thd != NULL && lex != NULL); + ss_dassert(querystr != NULL && + mysql != NULL && + thd != NULL && + lex != NULL); goto retblock; } @@ -858,12 +891,13 @@ char* skygw_get_canonical( itype = item->type(); - if (itype == Item::STRING_ITEM || + if (item->name != NULL && + (itype == Item::STRING_ITEM || itype == Item::INT_ITEM || itype == Item::DECIMAL_ITEM || itype == Item::REAL_ITEM || itype == Item::VARBIN_ITEM || - itype == Item::NULL_ITEM) + itype == Item::NULL_ITEM)) { if (!found) { @@ -881,4 +915,117 @@ char* skygw_get_canonical( } /*< for */ retblock: return newstr; +} + + +/** + * Create parsing information; initialize mysql handle, allocate parsing info + * struct and set handle and free function pointer to it. + * + * @param donefun pointer to free function + * + * @return pointer to parsing information + */ +parsing_info_t* parsing_info_init( + void (*donefun)(void *)) +{ + parsing_info_t* pi = NULL; + MYSQL* mysql; + const char* user = "skygw"; + const char* db = "skygw"; + + ss_dassert(donefun != NULL); + + /** Get server handle */ + mysql = mysql_init(NULL); + ss_dassert(mysql != NULL); + + if (mysql == NULL) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : call to mysql_real_connect failed due %d, %s.", + mysql_errno(mysql), + mysql_error(mysql)))); + + mysql_library_end(); + goto retblock; + } + /** Set methods and authentication to mysql */ + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_skygw"); + mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL); + mysql->methods = &embedded_methods; + mysql->user = my_strdup(user, MYF(0)); + mysql->db = my_strdup(db, MYF(0)); + mysql->passwd = NULL; + + pi = (parsing_info_t*)calloc(1, sizeof(parsing_info_t)); + + if (pi == NULL) + { + mysql_close(mysql); + mysql_thread_end(); + goto retblock; + } +#if defined(SS_DEBUG) + pi->pi_chk_top = CHK_NUM_PINFO; + pi->pi_chk_tail = CHK_NUM_PINFO; +#endif + /** Set handle and free function to parsing info struct */ + pi->pi_handle = mysql; + pi->pi_done_fp = donefun; + +retblock: + return pi; +} + +/** + * Free function for parsing info. Called by gwbuf_free or in case initialization + * of parsing information fails. + * + * @param ptr Pointer to parsing information, cast required + * + * @return void + * + */ +void parsing_info_done( + void* ptr) +{ + parsing_info_t* pi = (parsing_info_t *)ptr; + + if (pi->pi_handle != NULL) + { + MYSQL* mysql = (MYSQL *)pi->pi_handle; + + if (mysql->thd != NULL) + { + (*mysql->methods->free_embedded_thd)(mysql); + mysql->thd = NULL; + } + mysql_close(mysql); + mysql_thread_end(); + } + /** Free plain text query string */ + if (pi->pi_query_plain_str != NULL) + { + free(pi->pi_query_plain_str); + } + free(pi); +} + +/** + * Add plain text query string to parsing info. + * + * @param ptr Pointer to parsing info struct, cast required + * @param str String to be added + * + * @return void + */ +static void parsing_info_set_plain_str( + void* ptr, + char* str) +{ + parsing_info_t* pi = (parsing_info_t *)ptr; + CHK_PARSING_INFO(pi); + + pi->pi_query_plain_str = str; } \ No newline at end of file diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index fa7cefc78..beef84c2c 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -20,7 +20,8 @@ Copyright SkySQL Ab /** getpid */ #include #include -#include "../utils/skygw_utils.h" +#include +#include EXTERN_C_BLOCK_BEGIN @@ -52,15 +53,15 @@ typedef enum { * Create THD and use it for creating parse tree. Examine parse tree and * classify the query. */ -skygw_query_type_t skygw_query_classifier_get_type( - const char* query_str, - unsigned long client_flags, - MYSQL** mysql); +skygw_query_type_t query_classifier_get_type(GWBUF* querybuf); /** Free THD context and close MYSQL */ -void skygw_query_classifier_free(MYSQL* mysql); -char* skygw_query_classifier_get_stmtname(MYSQL* mysql); -char* skygw_get_canonical(MYSQL* mysql, char* querystr); +char* skygw_query_classifier_get_stmtname(MYSQL* mysql); +char* skygw_get_canonical(GWBUF* querybuf); +bool parse_query (GWBUF* querybuf); +parsing_info_t* parsing_info_init(void (*donefun)(void *)); +void parsing_info_done(void* ptr); + EXTERN_C_BLOCK_END diff --git a/server/core/buffer.c b/server/core/buffer.c index d3278a505..47e9a22eb 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -83,7 +83,7 @@ SHARED_BUF *sbuf; rval->sbuf = sbuf; rval->next = NULL; rval->gwbuf_type = GWBUF_TYPE_UNDEFINED; - rval->command = 0; + rval->gwbuf_parsing_info = NULL; CHK_GWBUF(rval); return rval; } @@ -102,6 +102,11 @@ gwbuf_free(GWBUF *buf) free(buf->sbuf->data); free(buf->sbuf); } + if (buf->gwbuf_parsing_info != NULL) + { + parsing_info_t* pi = (parsing_info_t *)buf->gwbuf_parsing_info; + pi->pi_done_fp(pi); + } free(buf); } @@ -131,6 +136,7 @@ GWBUF *rval; rval->end = buf->end; rval->gwbuf_type = buf->gwbuf_type; rval->next = NULL; + rval->gwbuf_parsing_info = NULL; CHK_GWBUF(rval); return rval; } @@ -157,6 +163,7 @@ GWBUF *gwbuf_clone_portion( clonebuf->end = (void *)((char *)clonebuf->start)+length; clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */ clonebuf->next = NULL; + clonebuf->gwbuf_parsing_info = NULL; CHK_GWBUF(clonebuf); return clonebuf; @@ -336,5 +343,10 @@ void gwbuf_set_type( } } - +void* gwbuf_get_parsing_info( + GWBUF* buf) +{ + CHK_GWBUF(buf); + return buf->gwbuf_parsing_info; +} diff --git a/server/core/modutil.c b/server/core/modutil.c index 78f389ebf..781c55763 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -29,6 +29,7 @@ */ #include #include +#include /** * Check if a GWBUF structure is a MySQL COM_QUERY packet @@ -171,3 +172,57 @@ GWBUF *addition; return orig; } + +/** + * Copy query string from GWBUF buffer to separate memory area. + * + * @param buf GWBUF buffer including the query + * + * @return Plaint text query if the packet type is COM_QUERY. Otherwise return + * a string including the packet type. + */ +char* modutil_get_query( + GWBUF* buf) +{ + uint8_t* packet; + mysql_server_cmd_t packet_type; + size_t len; + char* query_str; + + packet = GWBUF_DATA(buf); + packet_type = packet[4]; + + switch (packet_type) { + case MYSQL_COM_QUIT: + len = strlen("[Quit msg]")+1; + if ((query_str = (char *)malloc(len+1)) == NULL) + { + goto retblock; + } + memcpy(query_str, "[Quit msg]", len); + memset(&query_str[len], 0, 1); + break; + + case MYSQL_COM_QUERY: + len = MYSQL_GET_PACKET_LEN(packet)-1; /*< distract 1 for packet type byte */ + if ((query_str = (char *)malloc(len+1)) == NULL) + { + goto retblock; + } + memcpy(query_str, &packet[5], len); + memset(&query_str[len], 0, 1); + break; + + default: + len = strlen(STRPACKETTYPE(packet_type))+1; + if ((query_str = (char *)malloc(len+1)) == NULL) + { + goto retblock; + } + memcpy(query_str, STRPACKETTYPE(packet_type), len); + memset(&query_str[len], 0, 1); + break; + } /*< switch */ +retblock: + return query_str; +} \ No newline at end of file diff --git a/server/include/buffer.h b/server/include/buffer.h index 9729c538c..57e20dc2e 100644 --- a/server/include/buffer.h +++ b/server/include/buffer.h @@ -43,6 +43,7 @@ */ #include +EXTERN_C_BLOCK_BEGIN typedef enum { @@ -73,6 +74,20 @@ typedef struct { int refcount; /*< Reference count on the buffer */ } SHARED_BUF; + +typedef struct parsing_info_st { +#if defined(SS_DEBUG) + skygw_chk_t pi_chk_top; +#endif + void* pi_handle; /*< parsing info object pointer */ + char* pi_query_plain_str; /*< query as plain string */ + void (*pi_done_fp)(void *); /*< clean-up function for parsing info */ +#if defined(SS_DEBUG) + skygw_chk_t pi_chk_tail; +#endif +} parsing_info_t; + + /** * The buffer structure used by the descriptor control blocks. * @@ -86,7 +101,7 @@ typedef struct gwbuf { void *start; /*< Start of the valid data */ void *end; /*< First byte after the valid data */ SHARED_BUF *sbuf; /*< The shared buffer with the real data */ - int command;/*< The command type for the queue */ + void *gwbuf_parsing_info; /*< parsing info object pointer */ gwbuf_type_t gwbuf_type; /*< buffer's data type information */ } GWBUF; @@ -121,4 +136,10 @@ extern unsigned int gwbuf_length(GWBUF *head); extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len); extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type); extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type); +void* gwbuf_get_parsing_info(GWBUF* buf); + + +EXTERN_C_BLOCK_END + + #endif diff --git a/server/include/modutil.h b/server/include/modutil.h index 00336f937..a0624752a 100644 --- a/server/include/modutil.h +++ b/server/include/modutil.h @@ -36,4 +36,6 @@ extern int modutil_is_SQL(GWBUF *); extern int modutil_extract_SQL(GWBUF *, char **, int *); extern int modutil_MySQL_Query(GWBUF *, char **, int *, int *); extern GWBUF *modutil_replace_SQL(GWBUF *, char *); +char* modutil_get_query(GWBUF* buf); + #endif diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 24262bc6d..f89f68562 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -31,6 +31,7 @@ #include #include #include +#include #include MODULE_INFO info = { @@ -703,7 +704,7 @@ static void* newSession( backend_ref[i].bref_sescmd_cur.scmd_cur_ptr_property = &client_rses->rses_properties[RSES_PROP_TYPE_SESCMD]; backend_ref[i].bref_sescmd_cur.scmd_cur_cmd = NULL; - } + } max_nslaves = rses_get_max_slavecount(client_rses, router_nservers); max_slave_rlag = rses_get_max_replication_lag(client_rses); @@ -1031,9 +1032,6 @@ static int routeQuery( GWBUF* querybuf) { skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN; - GWBUF* plainsqlbuf = NULL; - char* querystr = NULL; - char* startpos; mysql_server_cmd_t packet_type; uint8_t* packet; int ret = 0; @@ -1042,8 +1040,6 @@ static int routeQuery( ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session; bool rses_is_closed = false; - size_t len; - MYSQL* mysql = NULL; CHK_CLIENT_RSES(router_cli_ses); @@ -1066,6 +1062,8 @@ static int routeQuery( */ if (packet_type != MYSQL_COM_QUIT) { + char* query_str = modutil_get_query(querybuf); + LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, @@ -1073,15 +1071,15 @@ static int routeQuery( "backend server. %s.", STRPACKETTYPE(packet_type), STRQTYPE(qtype), - (querystr == NULL ? "(empty)" : querystr), + (query_str == NULL ? "(empty)" : query_str), (rses_is_closed ? "Router was closed" : "Router has no backend servers where to " "route to")))); + free(querybuf); } goto return_ret; } inst->stats.n_queries++; - startpos = (char *)&packet[5]; master_dcb = router_cli_ses->rses_master_ref->bref_dcb; CHK_DCB(master_dcb); @@ -1105,44 +1103,16 @@ static int routeQuery( break; case MYSQL_COM_QUERY: - plainsqlbuf = gwbuf_clone_transform(querybuf, - GWBUF_TYPE_PLAINSQL); - len = GWBUF_LENGTH(plainsqlbuf); - /** unnecessary if buffer includes additional terminating null */ - querystr = (char *)malloc(len+1); - memcpy(querystr, startpos, len); - memset(&querystr[len], 0, 1); - /** - * Use mysql handle to query information from parse tree. - * call skygw_query_classifier_free before exit! - */ - qtype = skygw_query_classifier_get_type(querystr, 0, &mysql); + qtype = query_classifier_get_type(querybuf); break; case MYSQL_COM_STMT_PREPARE: - plainsqlbuf = gwbuf_clone_transform(querybuf, - GWBUF_TYPE_PLAINSQL); - len = GWBUF_LENGTH(plainsqlbuf); - /** unnecessary if buffer includes additional terminating null */ - querystr = (char *)malloc(len+1); - memcpy(querystr, startpos, len); - memset(&querystr[len], 0, 1); - qtype = skygw_query_classifier_get_type(querystr, 0, &mysql); + qtype = query_classifier_get_type(querybuf); qtype |= QUERY_TYPE_PREPARE_STMT; break; case MYSQL_COM_STMT_EXECUTE: /** Parsing is not needed for this type of packet */ -#if defined(NOT_USED) - plainsqlbuf = gwbuf_clone_transform(querybuf, - GWBUF_TYPE_PLAINSQL); - len = GWBUF_LENGTH(plainsqlbuf); - /** unnecessary if buffer includes additional terminating null */ - querystr = (char *)malloc(len+1); - memcpy(querystr, startpos, len); - memset(&querystr[len], 0, 1); - qtype = skygw_query_classifier_get_type(querystr, 0, &mysql); -#endif qtype = QUERY_TYPE_EXEC_STMT; break; @@ -1207,7 +1177,7 @@ static int routeQuery( */ bool succp = route_session_write( router_cli_ses, - querybuf, + gwbuf_clone(querybuf), inst, packet_type, qtype); @@ -1238,7 +1208,7 @@ static int routeQuery( if (succp) { - if ((ret = slave_dcb->func.write(slave_dcb, querybuf)) == 1) + if ((ret = slave_dcb->func.write(slave_dcb, gwbuf_clone(querybuf))) == 1) { backend_ref_t* bref; @@ -1252,10 +1222,12 @@ static int routeQuery( } else { + char* query_str = modutil_get_query(querybuf); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Routing query \"%s\" failed.", - querystr))); + (query_str == NULL ? "not available" : query_str)))); + free(query_str); } } rses_end_locked_router_action(router_cli_ses); @@ -1296,7 +1268,7 @@ static int routeQuery( if (succp) { - if ((ret = master_dcb->func.write(master_dcb, querybuf)) == 1) + if ((ret = master_dcb->func.write(master_dcb, gwbuf_clone(querybuf))) == 1) { backend_ref_t* bref; @@ -1323,11 +1295,10 @@ static int routeQuery( } return_ret: #if defined(SS_DEBUG) - if (mysql != NULL && true) { char* canonical_query_str; - canonical_query_str = skygw_get_canonical(mysql, querystr); + canonical_query_str = skygw_get_canonical(querybuf); if (canonical_query_str != NULL) { @@ -1339,18 +1310,7 @@ return_ret: } } #endif - if (plainsqlbuf != NULL) - { - gwbuf_free(plainsqlbuf); - } - if (querystr != NULL) - { - free(querystr); - } - if (mysql != NULL) - { - skygw_query_classifier_free(mysql); - } + gwbuf_free(querybuf); return ret; } diff --git a/utils/skygw_debug.h b/utils/skygw_debug.h index 43a609a40..9ffc5e7e2 100644 --- a/utils/skygw_debug.h +++ b/utils/skygw_debug.h @@ -123,7 +123,8 @@ typedef enum skygw_chk_t { CHK_NUM_SESCMD_CUR, CHK_NUM_BACKEND, CHK_NUM_BACKEND_REF, - CHK_NUM_PREP_STMT + CHK_NUM_PREP_STMT, + CHK_NUM_PINFO } skygw_chk_t; # define STRBOOL(b) ((b) ? "true" : "false") @@ -486,6 +487,13 @@ typedef enum skygw_chk_t { "Prepared statement struct has invalid check fields"); \ } +#define CHK_PARSING_INFO(p) { \ + ss_info_dassert((p)->pi_chk_top == CHK_NUM_PINFO && \ + (p)->pi_chk_tail == CHK_NUM_PINFO, \ + "Parsing info struct has invalid check fields"); \ +} + + #if defined(SS_DEBUG) bool conn_open[10240]; From fa2189373dce93e7938612e192653870cb7cbcbc Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Thu, 21 Aug 2014 22:28:23 +0300 Subject: [PATCH 06/16] query_classifier.cc: query_is_parsed is now global function and can be used to check whether parsing information already exists in the buffer. skygw_utils.cc: removed replace_str and implemented replace_literal which replaces user-provided literals in query with predefined string "?". Replacing is done one by one, so it is suboptimal ipmlementation since all literals could be passed to replacement function in one call and processed all before returning. --- query_classifier/query_classifier.cc | 19 ++-- query_classifier/query_classifier.h | 2 + utils/makefile | 3 +- utils/skygw_types.h | 2 + utils/skygw_utils.cc | 124 +++++++++++++++------------ utils/skygw_utils.h | 8 +- 6 files changed, 94 insertions(+), 64 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 7354d9639..9c2ab146a 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -84,9 +84,6 @@ static bool skygw_stmt_causes_implicit_commit( static int is_autocommit_stmt( LEX* lex); -static bool query_is_parsed( - GWBUF* buf); - static void parsing_info_set_plain_str(void* ptr, char* str); @@ -203,7 +200,7 @@ retblock: * * @return true or false */ -static bool query_is_parsed( +bool query_is_parsed( GWBUF* buf) { if (buf->gwbuf_parsing_info != NULL) @@ -873,7 +870,7 @@ char* skygw_get_canonical( } pi = (parsing_info_t*)querybuf->gwbuf_parsing_info; - if ((querystr = pi->pi_query_plain_str) == NULL || + if (pi->pi_query_plain_str == NULL || (mysql = (MYSQL *)pi->pi_handle) == NULL || (thd = (THD *)mysql->thd) == NULL || (lex = thd->lex) == NULL) @@ -885,6 +882,8 @@ char* skygw_get_canonical( goto retblock; } + querystr = strdup(pi->pi_query_plain_str); + for (item=thd->free_list; item != NULL; item=item->next) { Item::Type itype; @@ -901,14 +900,18 @@ char* skygw_get_canonical( { if (!found) { - newstr = replace_str(querystr, item->name, "?"); - found = true; + newstr = replace_literal(querystr, item->name, "?"); + if (newstr != NULL) + { + free(querystr); + found = true; + } } else { char* prevstr = newstr; - newstr = replace_str(prevstr, item->name, "?"); + newstr = replace_literal(prevstr, item->name, "?"); free(prevstr); } } diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index beef84c2c..dd5ddc5cc 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -61,6 +61,8 @@ char* skygw_get_canonical(GWBUF* querybuf); bool parse_query (GWBUF* querybuf); parsing_info_t* parsing_info_init(void (*donefun)(void *)); void parsing_info_done(void* ptr); +bool query_is_parsed(GWBUF* buf); + diff --git a/utils/makefile b/utils/makefile index a8df09a7f..0faa27144 100644 --- a/utils/makefile +++ b/utils/makefile @@ -3,6 +3,7 @@ include ../makefile.inc CC = gcc CPP = g++ +UTILS_PATH := $(ROOT_PATH)/utils makeall: clean all @@ -13,7 +14,7 @@ clean: - $(DEL) *~ all: - $(CPP) -c $(CFLAGS) \ + $(CPP) -c $(CFLAGS) -I$(UTILS_PATH) \ -fPIC skygw_utils.cc -o skygw_utils.o cleantests: diff --git a/utils/skygw_types.h b/utils/skygw_types.h index d497fbfd7..cccf55f2b 100644 --- a/utils/skygw_types.h +++ b/utils/skygw_types.h @@ -44,4 +44,6 @@ # endif #endif +#define MAX_ERROR_MSG PATH_MAX + #endif /* SKYGW_TYPES_H */ diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index c7175c76f..2b2204814 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -24,8 +24,9 @@ #include #include #include +#include #include "skygw_debug.h" -#include "skygw_types.h" +#include #include "skygw_utils.h" const char* timestamp_formatstr = "%04d %02d/%02d %02d:%02d:%02d "; @@ -1864,68 +1865,83 @@ void skygw_file_done( } } + /** - * Replaces in the string str all the occurrences of the source string old with - * the destination string new. The lengths of the strings old and new may differ. - * The string new may be of any length, but the string "old" must be of non-zero - * length - the penalty for providing an empty string for the "old" parameter is - * an infinite loop. In addition, none of the three parameters may be NULL. + * Find the given needle - user-provided literal - and replace it with + * replacement string. Separate user-provided literals from matching table names + * etc. by searching only substrings preceded by non-letter and non-number. * - * @param str String to be modified - * @param old Substring to be replaced - * @param new Replacement - * @return String with replacements in new memory area or NULL if memory - * allocation failed. - * Dependencies: For this function to compile, you will need to also #include - * the following files: , and . + * @param haystack Plain text query string, not to be freed + * @param needle Substring to be searched, not to be freed + * @param replacement Replacement text, not to be freed * - * Thanks, to Laird Shaw who implemented most of this function. - */ -char* replace_str ( - const char *str, - const char *old, - const char *replacement) + * @return newly allocated string where needle is replaced + */ +char* replace_literal( + char* haystack, + const char* needle, + const char* replacement) { - char* ret; - char* r; - const char* p; - const char* q; - size_t oldlen; - size_t count; - size_t retlen; - size_t newlen; + const char* prefix = "[ =',\\(]"; /*< ' ','=','(',''',',' are allowed before needle */ + const char* suffix = "[$^[:alnum:]]?"; /*< alpha-num chars aren't allowed after the needle */ + char* search_re; + char* newstr; + regex_t re; + regmatch_t match; + int rc; + size_t rlen = strlen(replacement); + size_t nlen = strlen(needle); + size_t hlen = strlen(haystack); - oldlen = strlen(old); - newlen = strlen(replacement); + search_re = (char *)malloc(strlen(prefix)+nlen+strlen(suffix)+1); + sprintf(search_re, "%s%s%s", prefix, needle, suffix); + /** + * +2 because there may be up to 2 extra characters which + * aren't replaced + */ + newstr = (char *)malloc(hlen-nlen+rlen); - if (oldlen != newlen) + rc = regcomp(&re, search_re, REG_EXTENDED); + ss_dassert(rc == 0); + + if (rc != 0) { - for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) - { - count++; - } - /* this is undefined if p - str > PTRDIFF_MAX */ - retlen = p - str + strlen(p) + count * (newlen - oldlen); - } - else - { - retlen = strlen(str); - } - if ((ret = (char *)malloc(retlen + 1)) == NULL) - { - return NULL; + char error_message[MAX_ERROR_MSG]; + regerror (rc, &re, error_message, MAX_ERROR_MSG); + fprintf(stderr, + "Regex error compiling '%s': %s\n", + search_re, + error_message); + free(search_re); + newstr = NULL; + goto retblock; } + rc = regexec(&re, haystack, 1, &match, 0); - for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) + if (rc != 0) { - /* this is undefined if q - p > PTRDIFF_MAX */ - ptrdiff_t l = q - p; - memcpy(r, p, l); - r += l; - memcpy(r, replacement, newlen); - r += newlen; + free(search_re); + newstr = NULL; + goto retblock; } - strcpy(r, p); + memcpy(newstr, haystack, match.rm_so+1); + memcpy(newstr+match.rm_so+1, replacement, rlen); + /** +1 is terminating byte */ + memcpy(newstr+match.rm_so+1+rlen, haystack+match.rm_so+1+nlen, hlen-(match.rm_so+1)-nlen+1); - return ret; -} \ No newline at end of file + regfree(&re); + +retblock: + return newstr; +} + + + + + + + + + + + diff --git a/utils/skygw_utils.h b/utils/skygw_utils.h index 854c82541..a50dd08b6 100644 --- a/utils/skygw_utils.h +++ b/utils/skygw_utils.h @@ -191,7 +191,13 @@ int skygw_rwlock_unlock(skygw_rwlock_t* rwlock); int skygw_rwlock_init(skygw_rwlock_t** rwlock); int atomic_add(int *variable, int value); -char* replace_str(const char* str, const char* old, const char* replacement); +EXTERN_C_BLOCK_BEGIN + +char* replace_literal(char* haystack, + const char* needle, + const char* replacement); + +EXTERN_C_BLOCK_END #endif /* SKYGW_UTILS_H */ From 8d1eae6fde3c416a8537f31bce8413ed29fad6b0 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Thu, 21 Aug 2014 22:34:50 +0300 Subject: [PATCH 07/16] Fixed comment. --- utils/skygw_utils.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 2b2204814..c9c3fb77f 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -1895,10 +1895,7 @@ char* replace_literal( search_re = (char *)malloc(strlen(prefix)+nlen+strlen(suffix)+1); sprintf(search_re, "%s%s%s", prefix, needle, suffix); - /** - * +2 because there may be up to 2 extra characters which - * aren't replaced - */ + /** Allocate memory for new string */ newstr = (char *)malloc(hlen-nlen+rlen); rc = regcomp(&re, search_re, REG_EXTENDED); From 3a5b8ef64c074e33670de570cc19f26152f1d59f Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Thu, 21 Aug 2014 23:08:21 +0300 Subject: [PATCH 08/16] query_classifier.cc: cleaned up and simplified skygw_get_canonical skygw_util.cc:fixed memory allocation issue where terminating byte wasn't counted. Added some error checks. --- query_classifier/query_classifier.cc | 20 ++--------------- utils/skygw_utils.cc | 32 ++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 9c2ab146a..2f29ddba1 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -860,7 +860,6 @@ char* skygw_get_canonical( THD* thd; LEX* lex; bool found = false; - char* newstr = NULL; Item* item; char* querystr; @@ -898,26 +897,11 @@ char* skygw_get_canonical( itype == Item::VARBIN_ITEM || itype == Item::NULL_ITEM)) { - if (!found) - { - newstr = replace_literal(querystr, item->name, "?"); - if (newstr != NULL) - { - free(querystr); - found = true; - } - } - else - { - char* prevstr = newstr; - - newstr = replace_literal(prevstr, item->name, "?"); - free(prevstr); - } + querystr = replace_literal(querystr, item->name, "?"); } } /*< for */ retblock: - return newstr; + return querystr; } diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index c9c3fb77f..6ffafd7c3 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -1882,7 +1882,7 @@ char* replace_literal( const char* needle, const char* replacement) { - const char* prefix = "[ =',\\(]"; /*< ' ','=','(',''',',' are allowed before needle */ + const char* prefix = "[ ='\",\\(]"; /*< ' ','=','(',''',''"',',' are allowed before needle */ const char* suffix = "[$^[:alnum:]]?"; /*< alpha-num chars aren't allowed after the needle */ char* search_re; char* newstr; @@ -1894,9 +1894,27 @@ char* replace_literal( size_t hlen = strlen(haystack); search_re = (char *)malloc(strlen(prefix)+nlen+strlen(suffix)+1); + + if (search_re == NULL) + { + fprintf(stderr, "Regex memory allocation failed : %s\n", + strerror(errno)); + newstr = haystack; + goto retblock; + } + sprintf(search_re, "%s%s%s", prefix, needle, suffix); - /** Allocate memory for new string */ - newstr = (char *)malloc(hlen-nlen+rlen); + /** Allocate memory for new string +1 for terminating byte */ + newstr = (char *)malloc(hlen-nlen+rlen+1); + + if (newstr == NULL) + { + fprintf(stderr, "Regex memory allocation failed : %s\n", + strerror(errno)); + free(search_re); + newstr = haystack; + goto retblock; + } rc = regcomp(&re, search_re, REG_EXTENDED); ss_dassert(rc == 0); @@ -1910,7 +1928,7 @@ char* replace_literal( search_re, error_message); free(search_re); - newstr = NULL; + newstr = haystack; goto retblock; } rc = regexec(&re, haystack, 1, &match, 0); @@ -1918,7 +1936,7 @@ char* replace_literal( if (rc != 0) { free(search_re); - newstr = NULL; + newstr = haystack; goto retblock; } memcpy(newstr, haystack, match.rm_so+1); @@ -1927,8 +1945,8 @@ char* replace_literal( memcpy(newstr+match.rm_so+1+rlen, haystack+match.rm_so+1+nlen, hlen-(match.rm_so+1)-nlen+1); regfree(&re); - -retblock: + free(haystack); +retblock: return newstr; } From 3bc88e4eb97b06bc9fcb019bd91c0d50a107f3d4 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Thu, 21 Aug 2014 23:31:23 +0300 Subject: [PATCH 09/16] skygw_get_canonical didn't return NULL pointerin cases where parsing info didn't exist or something went wrong. --- query_classifier/query_classifier.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 2f29ddba1..8379c1b89 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -865,6 +865,7 @@ char* skygw_get_canonical( if (querybuf->gwbuf_parsing_info == NULL) { + querystr = NULL; goto retblock; } pi = (parsing_info_t*)querybuf->gwbuf_parsing_info; @@ -878,6 +879,7 @@ char* skygw_get_canonical( mysql != NULL && thd != NULL && lex != NULL); + querystr = NULL; goto retblock; } From ee52ac64a9c9955d309753bc8b203e2b6bc473e0 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Fri, 22 Aug 2014 19:01:56 +0300 Subject: [PATCH 10/16] query_classifier.cc:skygw_get_canonical: if the item to be replaced is an empty string "", it is processed differently from the other cases due to difficulties to get wanted result by adding special rule for that to regex. query_classifier.h: added parsing information structure to query classifier away from buffer.h. buffer.c:introduced a buffer object which includes object pointer and a clean-up call-back function. Buffer objects form a list which is cleaned up by the last referrer of the buffer, as a part of gwbuf_free. Buffer object list is protected by a spinlock gwbuf_lock. Also added identifier type, bufobj_id_t which is enumerated type and currently includes one value only, GWBUF_PARSING_INFO. Added also a bitfield for information about the buffer. It currently has one type only, GWBUF_INFO_PARSED indicating that buffer content is parsed and there is buffer object of type GWBUF_PARSING_INFO. skygw_utils.cc:replace_literal:changed regexec matching to case insensitive because user-defined literals are sometimes converted to upper-case ones. --- query_classifier/query_classifier.cc | 82 +++++++++++++------- query_classifier/query_classifier.h | 18 ++++- server/core/buffer.c | 111 ++++++++++++++++++++++++--- server/include/buffer.h | 49 ++++++++---- utils/skygw_utils.cc | 3 +- 5 files changed, 208 insertions(+), 55 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 8379c1b89..009cbe94f 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -105,20 +105,29 @@ skygw_query_type_t query_classifier_get_type( ss_info_dassert(querybuf != NULL, ("querybuf is NULL")); /** Create parsing info for the query and store it to buffer */ - if (!query_is_parsed(querybuf)) + succp = query_is_parsed(querybuf); + + if (!succp) { succp = parse_query(querybuf); } /** Read thd pointer and resolve the query type with it. */ if (succp) { - parsing_info_t* pi = (parsing_info_t*)gwbuf_get_parsing_info(querybuf); - mysql = (MYSQL *)pi->pi_handle; - - /** Find out the query type */ - if (mysql != NULL) + parsing_info_t* pi; + + pi = (parsing_info_t*)gwbuf_get_buffer_object_data(querybuf, + GWBUF_PARSING_INFO); + + if (pi != NULL) { - qtype = resolve_query_type((THD *)mysql->thd); + mysql = (MYSQL *)pi->pi_handle; + + /** Find out the query type */ + if (mysql != NULL) + { + qtype = resolve_query_type((THD *)mysql->thd); + } } } return qtype; @@ -143,19 +152,21 @@ bool parse_query ( parsing_info_t* pi; CHK_GWBUF(querybuf); + /** Do not parse without releasing previous parse info first */ ss_dassert(!query_is_parsed(querybuf)); - if (querybuf->gwbuf_parsing_info == NULL) + if (query_is_parsed(querybuf)) { - /** Create parsing info */ - querybuf->gwbuf_parsing_info = parsing_info_init(parsing_info_done); + return false; } + /** Create parsing info */ + pi = parsing_info_init(parsing_info_done); - if (querybuf->gwbuf_parsing_info == NULL) + if (pi == NULL) { succp = false; goto retblock; - } + } /** Extract query and copy it to different buffer */ data = (uint8_t*)GWBUF_DATA(querybuf); len = MYSQL_GET_PACKET_LEN(data)-1; /*< distract 1 for packet type byte */ @@ -163,21 +174,22 @@ bool parse_query ( if (query_str == NULL) { + /** Free parsing info data */ + parsing_info_done(pi); succp = false; goto retblock; } memcpy(query_str, &data[5], len); memset(&query_str[len], 0, 1); - parsing_info_set_plain_str(querybuf->gwbuf_parsing_info, query_str); + parsing_info_set_plain_str(pi, query_str); /** Get one or create new THD object to be use in parsing */ - pi = (parsing_info_t *)querybuf->gwbuf_parsing_info; thd = get_or_create_thd_for_parsing((MYSQL *)pi->pi_handle, query_str); if (thd == NULL) { - parsing_info_done(querybuf->gwbuf_parsing_info); - querybuf->gwbuf_parsing_info = NULL; + /** Free parsing info data */ + parsing_info_done(pi); succp = false; goto retblock; } @@ -186,6 +198,12 @@ bool parse_query ( * thd and lex are readable even if creating parse tree fails. */ create_parse_tree(thd); + /** Add complete parsing info struct to the query buffer */ + gwbuf_add_buffer_object(querybuf, + GWBUF_PARSING_INFO, + (void *)pi, + parsing_info_done); + succp = true; retblock: return succp; @@ -203,11 +221,8 @@ retblock: bool query_is_parsed( GWBUF* buf) { - if (buf->gwbuf_parsing_info != NULL) - { - return true; - } - return false; + CHK_GWBUF(buf); + return GWBUF_IS_PARSED(buf); } @@ -859,23 +874,29 @@ char* skygw_get_canonical( MYSQL* mysql; THD* thd; LEX* lex; - bool found = false; Item* item; char* querystr; - if (querybuf->gwbuf_parsing_info == NULL) + if (!GWBUF_IS_PARSED(querybuf)) { querystr = NULL; goto retblock; - } - pi = (parsing_info_t*)querybuf->gwbuf_parsing_info; + } + pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf, + GWBUF_PARSING_INFO); + if (pi == NULL) + { + querystr = NULL; + goto retblock; + } + if (pi->pi_query_plain_str == NULL || (mysql = (MYSQL *)pi->pi_handle) == NULL || (thd = (THD *)mysql->thd) == NULL || (lex = thd->lex) == NULL) { - ss_dassert(querystr != NULL && + ss_dassert(pi->pi_query_plain_str != NULL && mysql != NULL && thd != NULL && lex != NULL); @@ -899,7 +920,14 @@ char* skygw_get_canonical( itype == Item::VARBIN_ITEM || itype == Item::NULL_ITEM)) { - querystr = replace_literal(querystr, item->name, "?"); + if (itype == Item::STRING_ITEM && strlen(item->name) == 0) + { + querystr = replace_literal(querystr, "\"\"", "\"?\""); + } + else + { + querystr = replace_literal(querystr, item->name, "?"); + } } } /*< for */ retblock: diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index dd5ddc5cc..325087910 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -47,6 +47,20 @@ typedef enum { QUERY_TYPE_EXEC_STMT = 0x1000 /*< Execute prepared statement */ } skygw_query_type_t; + +typedef struct parsing_info_st { +#if defined(SS_DEBUG) + skygw_chk_t pi_chk_top; +#endif + void* pi_handle; /*< parsing info object pointer */ + char* pi_query_plain_str; /*< query as plain string */ + void (*pi_done_fp)(void *); /*< clean-up function for parsing info */ +#if defined(SS_DEBUG) + skygw_chk_t pi_chk_tail; +#endif +} parsing_info_t; + + #define QUERY_IS_TYPE(mask,type) ((mask & type) == type) /** @@ -61,9 +75,7 @@ char* skygw_get_canonical(GWBUF* querybuf); bool parse_query (GWBUF* querybuf); parsing_info_t* parsing_info_init(void (*donefun)(void *)); void parsing_info_done(void* ptr); -bool query_is_parsed(GWBUF* buf); - - +bool query_is_parsed(GWBUF* buf); EXTERN_C_BLOCK_END diff --git a/server/core/buffer.c b/server/core/buffer.c index 47e9a22eb..3b8a8c8e6 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -40,6 +40,11 @@ #include #include +static buffer_object_t* gwbuf_remove_buffer_object( + GWBUF* buf, + buffer_object_t* bufobj); + + /** * Allocate a new gateway buffer structure of size bytes. * @@ -77,13 +82,15 @@ SHARED_BUF *sbuf; free(sbuf); return NULL; } + spinlock_init(&rval->gwbuf_lock); rval->start = sbuf->data; rval->end = rval->start + size; sbuf->refcount = 1; rval->sbuf = sbuf; rval->next = NULL; rval->gwbuf_type = GWBUF_TYPE_UNDEFINED; - rval->gwbuf_parsing_info = NULL; + rval->gwbuf_info = GWBUF_INFO_NONE; + rval->gwbuf_bufobj = NULL; CHK_GWBUF(rval); return rval; } @@ -96,16 +103,19 @@ SHARED_BUF *sbuf; void gwbuf_free(GWBUF *buf) { + buffer_object_t* bo; + CHK_GWBUF(buf); if (atomic_add(&buf->sbuf->refcount, -1) == 1) { free(buf->sbuf->data); free(buf->sbuf); - } - if (buf->gwbuf_parsing_info != NULL) - { - parsing_info_t* pi = (parsing_info_t *)buf->gwbuf_parsing_info; - pi->pi_done_fp(pi); + bo = buf->gwbuf_bufobj; + + while (bo != NULL) + { + bo = gwbuf_remove_buffer_object(buf, bo); + } } free(buf); } @@ -135,8 +145,9 @@ GWBUF *rval; rval->start = buf->start; rval->end = buf->end; rval->gwbuf_type = buf->gwbuf_type; + rval->gwbuf_info = buf->gwbuf_info; + rval->gwbuf_bufobj = buf->gwbuf_bufobj; rval->next = NULL; - rval->gwbuf_parsing_info = NULL; CHK_GWBUF(rval); return rval; } @@ -162,8 +173,9 @@ GWBUF *gwbuf_clone_portion( clonebuf->start = (void *)((char*)buf->start)+start_offset; clonebuf->end = (void *)((char *)clonebuf->start)+length; clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */ + clonebuf->gwbuf_info = buf->gwbuf_info; + clonebuf->gwbuf_bufobj = buf->gwbuf_bufobj; clonebuf->next = NULL; - clonebuf->gwbuf_parsing_info = NULL; CHK_GWBUF(clonebuf); return clonebuf; @@ -343,10 +355,87 @@ void gwbuf_set_type( } } -void* gwbuf_get_parsing_info( - GWBUF* buf) +/** + * Add a buffer object to GWBUF buffer. + * + * @param buf GWBUF where object is added + * @param id Type identifier for object + * @param data Object data + * @param donefun_dp Clean-up function to be executed before buffer is freed. + */ +void gwbuf_add_buffer_object( + GWBUF* buf, + bufobj_id_t id, + void* data, + void (*donefun_fp)(void *)) { + buffer_object_t** p_b; + buffer_object_t* newb; + CHK_GWBUF(buf); - return buf->gwbuf_parsing_info; + newb = (buffer_object_t *)malloc(sizeof(buffer_object_t)); + newb->bo_id = id; + newb->bo_data = data; + newb->bo_donefun_fp = donefun_fp; + newb->bo_next = NULL; + /** Lock */ + spinlock_acquire(&buf->gwbuf_lock); + p_b = &buf->gwbuf_bufobj; + /** Search the end of the list and add there */ + while (*p_b != NULL) + { + p_b = &(*p_b)->bo_next; + } + *p_b = newb; + /** Set flag */ + buf->gwbuf_info |= GWBUF_INFO_PARSED; + /** Unlock */ + spinlock_release(&buf->gwbuf_lock); +} + +/** + * Search buffer object which matches with the id. + * + * @param buf GWBUF to be searched + * @param id Identifier for the object + * + * @return Searched buffer object or NULL if not found + */ +void* gwbuf_get_buffer_object_data( + GWBUF* buf, + bufobj_id_t id) +{ + buffer_object_t* bo; + + CHK_GWBUF(buf); + /** Lock */ + spinlock_acquire(&buf->gwbuf_lock); + bo = buf->gwbuf_bufobj; + + while (bo != NULL && bo->bo_id != id) + { + bo = bo->bo_next; + } + /** Unlock */ + spinlock_release(&buf->gwbuf_lock); + + return bo->bo_data; +} + + +/** + * @return pointer to next buffer object or NULL + */ +static buffer_object_t* gwbuf_remove_buffer_object( + GWBUF* buf, + buffer_object_t* bufobj) +{ + buffer_object_t* next; + + next = bufobj->bo_next; + /** Call corresponding clean-up function to clean buffer object's data */ + bufobj->bo_donefun_fp(bufobj->bo_data); + free(bufobj); + return next; } diff --git a/server/include/buffer.h b/server/include/buffer.h index 57e20dc2e..8f6dcf864 100644 --- a/server/include/buffer.h +++ b/server/include/buffer.h @@ -42,6 +42,8 @@ * @endverbatim */ #include +#include + EXTERN_C_BLOCK_BEGIN @@ -74,18 +76,33 @@ typedef struct { int refcount; /*< Reference count on the buffer */ } SHARED_BUF; +typedef enum +{ + GWBUF_INFO_NONE = 0x0, + GWBUF_INFO_PARSED = 0x1 +} gwbuf_info_t; -typedef struct parsing_info_st { -#if defined(SS_DEBUG) - skygw_chk_t pi_chk_top; -#endif - void* pi_handle; /*< parsing info object pointer */ - char* pi_query_plain_str; /*< query as plain string */ - void (*pi_done_fp)(void *); /*< clean-up function for parsing info */ -#if defined(SS_DEBUG) - skygw_chk_t pi_chk_tail; -#endif -} parsing_info_t; +#define GWBUF_IS_PARSED(b) (b->gwbuf_info & GWBUF_INFO_PARSED) + +/** + * A structure for cleaning up memory allocations of structures which are + * referred to by GWBUF and deallocated in gwbuf_free but GWBUF doesn't + * know what they are. + * All functions on the list are executed before freeing memory of GWBUF struct. + */ +typedef enum +{ + GWBUF_PARSING_INFO +} bufobj_id_t; + +typedef struct buffer_object_st buffer_object_t; + +struct buffer_object_st { + bufobj_id_t bo_id; + void* bo_data; + void (*bo_donefun_fp)(void *); + buffer_object_t* bo_next; +}; /** @@ -97,11 +114,13 @@ typedef struct parsing_info_st { * be copied within the gateway. */ typedef struct gwbuf { + SPINLOCK gwbuf_lock; struct gwbuf *next; /*< Next buffer in a linked chain of buffers */ void *start; /*< Start of the valid data */ void *end; /*< First byte after the valid data */ SHARED_BUF *sbuf; /*< The shared buffer with the real data */ - void *gwbuf_parsing_info; /*< parsing info object pointer */ + buffer_object_t *gwbuf_bufobj; /*< List of objects referred to by GWBUF */ + gwbuf_info_t gwbuf_info; /*< Info bits */ gwbuf_type_t gwbuf_type; /*< buffer's data type information */ } GWBUF; @@ -136,8 +155,12 @@ extern unsigned int gwbuf_length(GWBUF *head); extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len); extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type); extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type); -void* gwbuf_get_parsing_info(GWBUF* buf); +void gwbuf_add_buffer_object(GWBUF* buf, + bufobj_id_t id, + void* data, + void (*donefun_fp)(void *)); +void* gwbuf_get_buffer_object_data(GWBUF* buf, bufobj_id_t id); EXTERN_C_BLOCK_END diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 6ffafd7c3..7bfc42949 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -1916,7 +1916,7 @@ char* replace_literal( goto retblock; } - rc = regcomp(&re, search_re, REG_EXTENDED); + rc = regcomp(&re, search_re, REG_EXTENDED|REG_ICASE); ss_dassert(rc == 0); if (rc != 0) @@ -1936,6 +1936,7 @@ char* replace_literal( if (rc != 0) { free(search_re); + regfree(&re); newstr = haystack; goto retblock; } From 73707cc9da989d523023cd3ac2411a2863d461fa Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 22 Aug 2014 20:44:26 +0300 Subject: [PATCH 11/16] canonical query tests for query classifier --- .../test/canonical_tests/Makefile | 63 +++++++++++ .../test/canonical_tests/canonizer.c | 101 ++++++++++++++++++ .../test/canonical_tests/canontest.sh | 21 ++++ .../test/canonical_tests/errmsg.sys | Bin 0 -> 47264 bytes .../test/canonical_tests/expected.sql | 6 ++ .../test/canonical_tests/input.sql | 6 ++ query_classifier/test/makefile | 4 +- 7 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 query_classifier/test/canonical_tests/Makefile create mode 100644 query_classifier/test/canonical_tests/canonizer.c create mode 100755 query_classifier/test/canonical_tests/canontest.sh create mode 100644 query_classifier/test/canonical_tests/errmsg.sys create mode 100755 query_classifier/test/canonical_tests/expected.sql create mode 100755 query_classifier/test/canonical_tests/input.sql diff --git a/query_classifier/test/canonical_tests/Makefile b/query_classifier/test/canonical_tests/Makefile new file mode 100644 index 000000000..a416e1aa9 --- /dev/null +++ b/query_classifier/test/canonical_tests/Makefile @@ -0,0 +1,63 @@ +# cleantests - clean local and subdirectories' tests +# buildtests - build all local and subdirectories' tests +# runtests - run all local tests +# testall - clean, build and run local and subdirectories' tests + +include ../../../build_gateway.inc +include ../../../makefile.inc +include ../../../test.inc + +CC = gcc +CPP = g++ + +TESTPATH := $(shell pwd) +TESTLOG := $(TESTPATH)/testqclass.log +QUERY_CLASSIFIER_PATH := $(ROOT_PATH)/query_classifier +LOG_MANAGER_PATH := $(ROOT_PATH)/log_manager +UTILS_PATH := $(ROOT_PATH)/utils +CORE_PATH := $(ROOT_PATH)/server/core +TESTAPP = $(TESTPATH)/canonizer + +LDFLAGS=-L$(QUERY_CLASSIFIER_PATH) \ + -L$(LOG_MANAGER_PATH) \ + -L$(EMBEDDED_LIB) \ + -Wl,-rpath,$(DEST)/lib \ + -Wl,-rpath,$(EMBEDDED_LIB) \ + -Wl,-rpath,$(LOG_MANAGER_PATH) \ + -Wl,-rpath,$(QUERY_CLASSIFIER_PATH) + +LIBS=-lpthread -lquery_classifier -lz -ldl -lssl -laio -lcrypt -lcrypto -lrt \ + -llog_manager $(UTILS_PATH)/skygw_utils.o $(CORE_PATH)/buffer.o $(CORE_PATH)/atomic.o $(CORE_PATH)/spinlock.o + +CFLAGS=$ -g $(MYSQL_HEADERS) \ + -I$(QUERY_CLASSIFIER_PATH) \ + $(MYSQL_HEADERS) \ + -I$(ROOT_PATH)/server/include \ + -I$(UTILS_PATH) + +EMBFLAGS=$(shell mysql_config --cflags --libmysqld-libs) + + +testall: + $(MAKE) cleantests + $(MAKE) buildtests + $(MAKE) runtests + +cleantests: + - $(DEL) *.o + - $(DEL) *~ + - $(DEL) canonizer + - $(DEL) aria_log* + - $(DEL) ib* + +buildtests: $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(EMBFLAGS) $(LIBS) canonizer.c -o $(TESTAPP) + +runtests: + @echo "" > $(TESTLOG) + @echo "-------------------------------" >> $(TESTLOG) + @echo $(shell date) >> $(TESTLOG) + @echo "Canonical Query Tests" >> $(TESTLOG) + @echo "-------------------------------" >> $(TESTLOG) + @echo "" >> $(TESTLOG) + ./canontest.sh $(TESTLOG) input.sql output.sql expected.sql diff --git a/query_classifier/test/canonical_tests/canonizer.c b/query_classifier/test/canonical_tests/canonizer.c new file mode 100644 index 000000000..a4c9df90f --- /dev/null +++ b/query_classifier/test/canonical_tests/canonizer.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include + +static char* server_options[] = { + "SkySQL Gateway", + "--datadir=./", + "--language=./", + "--skip-innodb", + "--default-storage-engine=myisam", + NULL +}; + +const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1; + +static char* server_groups[] = { + "embedded", + "server", + "server", + NULL +}; + +int main(int argc, char** argv) +{ + + int fdin,fdout,i=0,fnamelen,fsz,lines = 0; + unsigned int psize; + GWBUF** qbuff; + char *qin, *outnm, *buffer, *tok; + + if(argc != 3){ + printf("Usage: canonizer \n"); + return 1; + } + + + + bool failed = mysql_library_init(num_elements, server_options, server_groups); + + if(failed){ + printf("Embedded server init failed.\n"); + return 1; + } + + fnamelen = strlen(argv[1]) + 16; + fdin = open(argv[1],O_RDONLY); + fsz = lseek(fdin,0,SEEK_END); + lseek(fdin,0,SEEK_SET); + + if(!(buffer = malloc(sizeof(char)*fsz))){ + printf("Error: Failed to allocate memory."); + return 1; + } + + read(fdin,buffer,fsz); + tok = strpbrk(buffer,"\n"); + lines = 1; + + while((tok = strpbrk(tok + 1,"\n"))){ + lines++; + } + + qbuff = malloc(sizeof(GWBUF*)*lines); + + i = 0; + tok = strtok(buffer,"\n"); + + while(tok){ + qin = strdup(tok); + psize = strlen(qin); + qbuff[i] = gwbuf_alloc(psize + 6); + *(qbuff[i]->sbuf->data + 0) = (unsigned char)psize; + *(qbuff[i]->sbuf->data + 1) = (unsigned char)(psize>>8); + *(qbuff[i]->sbuf->data + 2) = (unsigned char)(psize>>16); + *(qbuff[i]->sbuf->data + 4) = 0x03; + memcpy(qbuff[i]->sbuf->data + 5,qin,psize); + *(qbuff[i]->sbuf->data + 5 + psize) = 0x00; + tok = strtok(NULL,"\n"); + free(qin); + i++; + } + + fdout = open(argv[2],O_TRUNC|O_CREAT|O_WRONLY,S_IRWXU|S_IXGRP|S_IXOTH); + + for(i = 0;i " + exit 0 +fi +TESTLOG=$1 +INPUT=$2 +OUTPUT=$3 +EXPECTED=$4 +DIFFLOG=diff.out +$PWD/canonizer $INPUT $OUTPUT +diff $OUTPUT $EXPECTED > $DIFFLOG +if [ $? -eq 0 ] +then + echo "PASSED" >> $TESTLOG +else + echo "FAILED" >> $TESTLOG + echo "Diff output: " >> $TESTLOG + cat $DIFFLOG >> $TESTLOG +fi diff --git a/query_classifier/test/canonical_tests/errmsg.sys b/query_classifier/test/canonical_tests/errmsg.sys new file mode 100644 index 0000000000000000000000000000000000000000..70ec93c1dba02ce8ec5c73bf88e40abb59ab8c45 GIT binary patch literal 47264 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 $(TESTLOG) endif - @cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG) \ No newline at end of file + @cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG) From 662b4b00ee37eba78c5a5294b8098266f8148658 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 22 Aug 2014 20:50:54 +0300 Subject: [PATCH 12/16] the errmsg.sys file is now copied during test building --- query_classifier/test/canonical_tests/Makefile | 1 + .../test/canonical_tests/errmsg.sys | Bin 47264 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 query_classifier/test/canonical_tests/errmsg.sys diff --git a/query_classifier/test/canonical_tests/Makefile b/query_classifier/test/canonical_tests/Makefile index a416e1aa9..6db90ac17 100644 --- a/query_classifier/test/canonical_tests/Makefile +++ b/query_classifier/test/canonical_tests/Makefile @@ -51,6 +51,7 @@ cleantests: - $(DEL) ib* buildtests: $(OBJS) + cp $(ERRMSG)/errmsg.sys . $(CC) $(CFLAGS) $(LDFLAGS) $(EMBFLAGS) $(LIBS) canonizer.c -o $(TESTAPP) runtests: diff --git a/query_classifier/test/canonical_tests/errmsg.sys b/query_classifier/test/canonical_tests/errmsg.sys deleted file mode 100644 index 70ec93c1dba02ce8ec5c73bf88e40abb59ab8c45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47264 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 Date: Mon, 25 Aug 2014 15:09:33 +0300 Subject: [PATCH 13/16] more test cases for canonical queries and makefile typo fixes --- query_classifier/test/canonical_tests/Makefile | 4 ++-- query_classifier/test/canonical_tests/expected.sql | 7 +++++++ query_classifier/test/canonical_tests/input.sql | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/query_classifier/test/canonical_tests/Makefile b/query_classifier/test/canonical_tests/Makefile index 6db90ac17..3d69507ec 100644 --- a/query_classifier/test/canonical_tests/Makefile +++ b/query_classifier/test/canonical_tests/Makefile @@ -26,10 +26,10 @@ LDFLAGS=-L$(QUERY_CLASSIFIER_PATH) \ -Wl,-rpath,$(LOG_MANAGER_PATH) \ -Wl,-rpath,$(QUERY_CLASSIFIER_PATH) -LIBS=-lpthread -lquery_classifier -lz -ldl -lssl -laio -lcrypt -lcrypto -lrt \ +LIBS=-lstdc++ -lpthread -lquery_classifier -lz -ldl -lssl -laio -lcrypt -lcrypto -lrt \ -llog_manager $(UTILS_PATH)/skygw_utils.o $(CORE_PATH)/buffer.o $(CORE_PATH)/atomic.o $(CORE_PATH)/spinlock.o -CFLAGS=$ -g $(MYSQL_HEADERS) \ +CFLAGS=-g $(MYSQL_HEADERS) \ -I$(QUERY_CLASSIFIER_PATH) \ $(MYSQL_HEADERS) \ -I$(ROOT_PATH)/server/include \ diff --git a/query_classifier/test/canonical_tests/expected.sql b/query_classifier/test/canonical_tests/expected.sql index e1db906af..9ba3aa925 100755 --- a/query_classifier/test/canonical_tests/expected.sql +++ b/query_classifier/test/canonical_tests/expected.sql @@ -4,3 +4,10 @@ select ?,?,?,?,?,? from tst select * from tst where fname like '?' select * from tst where lname like '?' order by fname insert into tst values ("?","?"),("?",?),("?","?") +drop table if exists tst +create table tst(fname varchar(30), lname varchar(30)) +update tst set lname="?" where fname like '?' or lname like '?' +delete from tst where lname like '?' and fname like '?' +select ? from tst where fname='?' or lname like '?' +select ?,?,?,? from tst where name='?' or name='?' or name='?' +select count(?),count(?),count(?),count(?),count (?),count(?) from tst diff --git a/query_classifier/test/canonical_tests/input.sql b/query_classifier/test/canonical_tests/input.sql index eabf0af32..192db761c 100755 --- a/query_classifier/test/canonical_tests/input.sql +++ b/query_classifier/test/canonical_tests/input.sql @@ -4,3 +4,10 @@ select 1,2,3,4,5,6 from tst; select * from tst where fname like '%a%'; select * from tst where lname like '%e%' order by fname; insert into tst values ("John","Doe"),("Plato",null),("Nietzsche",""); +drop table if exists tst; +create table tst(fname varchar(30), lname varchar(30)); +update tst set lname="Human" where fname like '%a%' or lname like '%a%'; +delete from tst where lname like '%man%' and fname like '%ard%'; +select 100 from tst where fname='10' or lname like '%100%'; +select 1,20,300,4000 from tst where name='1000' or name='200' or name='30' or name='4'; +select count(1),count(10),count(100),count(2),count (20),count(200) from tst; From c5fbb1f29594adbc403e2d09feec8cd4c1e880be Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Mon, 25 Aug 2014 22:17:21 +0300 Subject: [PATCH 14/16] query_classifier.cc:parsing_info_done: calling mysql_thread_end caused segfauls when same thread tried next time call free_embedded_thd because thread's sysvar was set to NULL in mysql_thread_end. Added a few lines to canonical query test's input script which are not handled correctly. --- log_manager/test/makefile | 2 +- query_classifier/query_classifier.cc | 4 +--- query_classifier/test/canonical_tests/input.sql | 4 ++++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/log_manager/test/makefile b/log_manager/test/makefile index 75df90c60..71fd074f1 100644 --- a/log_manager/test/makefile +++ b/log_manager/test/makefile @@ -39,7 +39,7 @@ buildtests: -o testlog \ -I$(MARIADB_SRC_PATH)/include \ -I$(LOG_MANAGER_PATH) -I$(UTILS_PATH) testlog.c \ - -llog_manager $(LDLIBS) \ + -lstdc++ -llog_manager $(LDLIBS) \ $(UTILS_PATH)/skygw_utils.o diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 009cbe94f..8cef07a10 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -964,9 +964,8 @@ parsing_info_t* parsing_info_init( mysql_errno(mysql), mysql_error(mysql)))); - mysql_library_end(); goto retblock; - } + } /** Set methods and authentication to mysql */ mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_skygw"); mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL); @@ -1019,7 +1018,6 @@ void parsing_info_done( mysql->thd = NULL; } mysql_close(mysql); - mysql_thread_end(); } /** Free plain text query string */ if (pi->pi_query_plain_str != NULL) diff --git a/query_classifier/test/canonical_tests/input.sql b/query_classifier/test/canonical_tests/input.sql index eabf0af32..5e0410761 100755 --- a/query_classifier/test/canonical_tests/input.sql +++ b/query_classifier/test/canonical_tests/input.sql @@ -4,3 +4,7 @@ select 1,2,3,4,5,6 from tst; select * from tst where fname like '%a%'; select * from tst where lname like '%e%' order by fname; insert into tst values ("John","Doe"),("Plato",null),("Nietzsche",""); +select md5("200000foo") =10, sleep(2), rand(100); +select * from my1 where md5("110") =10; +select md5("100foo") =10; +select * from my1 where md5("100") =10; From 69104d7dee584261e4fcfa2ae3833b45a009dfea Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Tue, 26 Aug 2014 11:08:05 +0300 Subject: [PATCH 15/16] skygw_utils.cc:replace_literal: Fixed regular expression which, for example, accepted "200" with needle "2" query_classifier.cc: fixed invalid argument list in logging command. input.sql: added a few previously failed cases for canonical query test --- query_classifier/query_classifier.cc | 1 - query_classifier/test/canonical_tests/input.sql | 4 ++++ utils/skygw_utils.cc | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 8cef07a10..7a4bcba40 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -672,7 +672,6 @@ static skygw_query_type_t resolve_query_type( "%lu [resolve_query_type] " "functype FUNC_SP, stored proc " "or unknown function.", - "%s:%s", pthread_self()))); break; case Item_func::UDF_FUNC: diff --git a/query_classifier/test/canonical_tests/input.sql b/query_classifier/test/canonical_tests/input.sql index 192db761c..59e9a2496 100755 --- a/query_classifier/test/canonical_tests/input.sql +++ b/query_classifier/test/canonical_tests/input.sql @@ -1,3 +1,7 @@ +select md5("200000foo") =10, sleep(2), rand(100); +select * from my1 where md5("110") =10; +select md5("100foo") =10; +select * from my1 where md5("100") =10; select sleep(2); select * from tst where lname='Doe'; select 1,2,3,4,5,6 from tst; diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 7bfc42949..8297ca5ba 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -1883,7 +1883,7 @@ char* replace_literal( const char* replacement) { const char* prefix = "[ ='\",\\(]"; /*< ' ','=','(',''',''"',',' are allowed before needle */ - const char* suffix = "[$^[:alnum:]]?"; /*< alpha-num chars aren't allowed after the needle */ + const char* suffix = "[^[:alnum:]]"; /*< alpha-num chars aren't allowed after the needle */ char* search_re; char* newstr; regex_t re; From cf5821d4ef6f392a91460f8be8767465fcd1608b Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Tue, 26 Aug 2014 11:14:51 +0300 Subject: [PATCH 16/16] Added expected results for missing queries. --- query_classifier/test/canonical_tests/expected.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/query_classifier/test/canonical_tests/expected.sql b/query_classifier/test/canonical_tests/expected.sql index 9ba3aa925..fabb27dc7 100755 --- a/query_classifier/test/canonical_tests/expected.sql +++ b/query_classifier/test/canonical_tests/expected.sql @@ -1,3 +1,7 @@ +select md5(?) =?, sleep(?), rand(?); +select * from my1 where md5(?) =?; +select md5(?) =?; +select * from my1 where md5(?) =?; select sleep(?) select * from tst where lname='?' select ?,?,?,?,?,? from tst