From e840f92cbbb3f4541035e38e433d9dba6b026f03 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 26 Jan 2016 17:37:09 +0200 Subject: [PATCH 01/13] Make query_classifier a wrapper. The query_classifier library is now only a wrapper that loads an actual query classifier implementation. Currently it is hardwired to load qc_mysqlembedded, which implements the query classifier API using MySQL embedded. This will be changed, so that the library to load is specified when qc_init() is called. That will then allow the query classifier to be specified in the config file. Currently there seems to be a conflict between the mysql_library_end() call made in qc_mysqlembedded and the mysql_library_end() call made in gateway.c. The reason is that they both finalize a shared library. For the time being mysql_library_end() is not called in gateway.c. This problem is likely to go away by switching from the client library to the connector-c library. --- query_classifier/CMakeLists.txt | 2 +- .../qc_mysqlembedded/qc_mysqlembedded.cc | 5 +- .../qc_mysqlembedded/qc_mysqlembedded.map | 2 +- query_classifier/query_classifier.cc | 1882 ++--------------- server/core/gateway.c | 12 +- server/modules/routing/binlog/CMakeLists.txt | 4 +- .../routing/binlog/test/CMakeLists.txt | 4 +- 7 files changed, 145 insertions(+), 1766 deletions(-) diff --git a/query_classifier/CMakeLists.txt b/query_classifier/CMakeLists.txt index 7191833bf..4780df0e9 100644 --- a/query_classifier/CMakeLists.txt +++ b/query_classifier/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(query_classifier SHARED query_classifier.cc) target_link_libraries(query_classifier ${MYSQL_EMBEDDED_LIB} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) set_target_properties(query_classifier PROPERTIES VERSION "1.0.0") set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/query_classifier.map) -#set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,-z,defs) +set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,-z,defs) install(TARGETS query_classifier COMPONENT lib DESTINATION ${MAXSCALE_LIBDIR}) add_subdirectory(qc_mysqlembedded) diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index 8c312497b..c81759d64 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -1818,6 +1818,9 @@ void qc_thread_end() * EXPORTS */ +extern "C" +{ + static char version_string[] = "V1.0.0"; static QUERY_CLASSIFIER qc = @@ -1862,4 +1865,4 @@ QUERY_CLASSIFIER* GetModuleObject() return &qc; } - +} diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map index baad642c0..bff425c4e 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map @@ -2,7 +2,7 @@ global: info; version; - GetModuleInfo; + GetModuleObject; ModuleInit; local: *; diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index b37a90d2d..401c8da1f 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -22,1794 +22,156 @@ * */ -#define EMBEDDED_LIBRARY -#define MYSQL_YACC -#define MYSQL_LEX012 -#define MYSQL_SERVER -#if defined(MYSQL_CLIENT) -#undef MYSQL_CLIENT -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// In client_settings.h mysql_server_init and mysql_server_end are defined to -// mysql_client_plugin_init and mysql_client_plugin_deinit respectively. -// Those must be undefined, so that we here really call mysql_server_[init|end]. -#undef mysql_server_init -#undef mysql_server_end -#include -#include -#include - -#include "../utils/skygw_types.h" -#include "../utils/skygw_debug.h" -#include #include -#include +#include +#include -#include -#include -#include -#include +//#define QC_TRACE_ENABLED +#undef QC_TRACE_ENABLED -#define MYSQL_COM_QUERY_HEADER_SIZE 5 /*< 3 bytes size, 1 sequence, 1 command */ -#define MAX_QUERYBUF_SIZE 2048 -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 QTYPE_LESS_RESTRICTIVE_THAN_WRITE(t) (tpi_handle; - - /** Find out the query type */ - if (mysql != NULL) - { - qtype = resolve_query_type((THD *) mysql->thd); - } - } - } - -retblock: - return qtype; -} - -/** - * 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 - */ -static bool parse_query(GWBUF* querybuf) -{ - bool succp; - THD* thd; - uint8_t* data; - size_t len; - char* query_str = NULL; - parsing_info_t* pi; - - CHK_GWBUF(querybuf); - /** Do not parse without releasing previous parse info first */ - ss_dassert(!query_is_parsed(querybuf)); - - if (querybuf == NULL || query_is_parsed(querybuf)) - { - return false; - } - - /** Create parsing info */ - pi = parsing_info_init(parsing_info_done); - - 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 */ - - - if (len < 1 || len >= ~((size_t) 0) - 1 || (query_str = (char *) malloc(len + 1)) == 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(pi, query_str); - - /** Get one or create new THD object to be use in parsing */ - thd = get_or_create_thd_for_parsing((MYSQL *) pi->pi_handle, query_str); - - if (thd == NULL) - { - /** Free parsing info data */ - parsing_info_done(pi); - succp = false; - goto retblock; - } - - /** - * Create parse_tree inside thd. - * 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; -} - -/** - * 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) -{ - CHK_GWBUF(buf); - return (buf != NULL && GWBUF_IS_PARSED(buf)); -} - -/** - * Create a thread context, thd, init embedded server, connect to it, and allocate - * query to thd. - * - * Parameters: - * @param mysql Database handle - * - * @param query_str Query in plain txt string - * - * @return Thread context pointer - * - */ -static THD* get_or_create_thd_for_parsing(MYSQL* mysql, char* query_str) -{ - THD* thd = NULL; - unsigned long client_flags; - char* db = mysql->options.db; - bool failp = FALSE; - size_t query_len; - - ss_info_dassert(mysql != NULL, ("mysql is NULL")); - ss_info_dassert(query_str != NULL, ("query_str is NULL")); - - query_len = strlen(query_str); - client_flags = set_client_flags(mysql); - - /** Get THD. - * NOTE: Instead of creating new every time, THD instance could - * be get from a pool of them. - */ - thd = (THD *) create_embedded_thd(client_flags); - - if (thd == NULL) - { - MXS_ERROR("Failed to create thread context for parsing."); - goto return_thd; - } - - mysql->thd = thd; - init_embedded_mysql(mysql, client_flags); - failp = check_embedded_connection(mysql, db); - - if (failp) - { - MXS_ERROR("Call to check_embedded_connection failed."); - goto return_err_with_thd; - } - - thd->clear_data_list(); - - /** Check that we are calling the client functions in right order */ - if (mysql->status != MYSQL_STATUS_READY) - { - set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); - MXS_ERROR("Invalid status %d in embedded server.", - mysql->status); - goto return_err_with_thd; - } - - /** Clear result variables */ - thd->current_stmt = NULL; - thd->store_globals(); - /** - * We have to call free_old_query before we start to fill mysql->fields - * for new query. In the case of embedded server we collect field data - * during query execution (not during data retrieval as it is in remote - * client). So we have to call free_old_query here - */ - free_old_query(mysql); - thd->extra_length = query_len; - thd->extra_data = query_str; - alloc_query(thd, query_str, query_len); - goto return_thd; - -return_err_with_thd: - (*mysql->methods->free_embedded_thd)(mysql); - thd = 0; - mysql->thd = 0; -return_thd: - return thd; -} - -/** - * @node Set client flags. This is copied from libmysqld.c:mysql_real_connect - * - * Parameters: - * @param mysql - - * - * - * @return - * - * - * @details (write detailed description here) - * - */ -static unsigned long set_client_flags(MYSQL* mysql) -{ - unsigned long f = 0; - - f |= mysql->options.client_flag; - - /* Send client information for access check */ - f |= CLIENT_CAPABILITIES; - - if (f & CLIENT_MULTI_STATEMENTS) - { - f |= CLIENT_MULTI_RESULTS; - } - - /** - * No compression in embedded as we don't send any data, - * and no pluggable auth, as we cannot do a client-server dialog - */ - f &= ~(CLIENT_COMPRESS | CLIENT_PLUGIN_AUTH); - - if (mysql->options.db != NULL) - { - f |= CLIENT_CONNECT_WITH_DB; - } - - return f; -} - -static bool create_parse_tree(THD* thd) -{ - Parser_state parser_state; - bool failp = FALSE; - const char* virtual_db = "skygw_virtual"; - - if (parser_state.init(thd, thd->query(), thd->query_length())) - { - failp = TRUE; - goto return_here; - } - - thd->reset_for_next_command(); - - /** - * Set some database to thd so that parsing won't fail because of - * missing database. Then parse. - */ - failp = thd->set_db(virtual_db, strlen(virtual_db)); - - if (failp) - { - MXS_ERROR("Failed to set database in thread context."); - } - - failp = parse_sql(thd, &parser_state, NULL); - - if (failp) - { - MXS_DEBUG("%lu [readwritesplit:create_parse_tree] failed to " - "create parse tree.", - pthread_self()); - } - -return_here: - return failp; -} - -/** - * Detect query type by examining parsed representation of it. - * - * @param thd MariaDB thread context. - * - * @return Copy of query type value. - * - * - * @details Query type is deduced by checking for certain properties - * of them. The order is essential. Some SQL commands have multiple - * flags set and changing the order in which flags are tested, - * the resulting type may be different. - * - */ -static qc_query_type_t resolve_query_type(THD* thd) -{ - qc_query_type_t qtype = QUERY_TYPE_UNKNOWN; - u_int32_t type = QUERY_TYPE_UNKNOWN; - int set_autocommit_stmt = -1; /*< -1 no, 0 disable, 1 enable */ - LEX* lex; - Item* item; - /** - * By default, if sql_log_bin, that is, recording data modifications - * to binary log, is disabled, gateway treats operations normally. - * Effectively nothing is replicated. - * When force_data_modify_op_replication is TRUE, gateway distributes - * all write operations to all nodes. - */ -#if defined(NOT_IN_USE) - bool force_data_modify_op_replication; - force_data_modify_op_replication = FALSE; -#endif /* NOT_IN_USE */ - ss_info_dassert(thd != NULL, ("thd is NULL\n")); - - lex = thd->lex; - - /** SELECT ..INTO variable|OUTFILE|DUMPFILE */ - if (lex->result != NULL) - { - type = QUERY_TYPE_GSYSVAR_WRITE; - goto return_qtype; - } - - if (skygw_stmt_causes_implicit_commit(lex, &set_autocommit_stmt)) - { - if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) - { - if (sql_command_flags[lex->sql_command] & - CF_IMPLICT_COMMIT_BEGIN) - { - MXS_INFO("Implicit COMMIT before executing the next command."); - } - else if (sql_command_flags[lex->sql_command] & - CF_IMPLICIT_COMMIT_END) - { - MXS_INFO("Implicit COMMIT after executing the next command."); - } - } - - if (set_autocommit_stmt == 1) - { - type |= QUERY_TYPE_ENABLE_AUTOCOMMIT; - } - - type |= QUERY_TYPE_COMMIT; - } - - if (set_autocommit_stmt == 0) - { - if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) - { - MXS_INFO("Disable autocommit : implicit START TRANSACTION" - " before executing the next command."); - } - - type |= QUERY_TYPE_DISABLE_AUTOCOMMIT; - type |= QUERY_TYPE_BEGIN_TRX; - } - - if (lex->option_type == OPT_GLOBAL) - { - /** - * SHOW syntax http://dev.mysql.com/doc/refman/5.6/en/show.html - */ - if (lex->sql_command == SQLCOM_SHOW_VARIABLES) - { - type |= QUERY_TYPE_GSYSVAR_READ; - } - /** - * SET syntax http://dev.mysql.com/doc/refman/5.6/en/set-statement.html - */ - else if (lex->sql_command == SQLCOM_SET_OPTION) - { - type |= QUERY_TYPE_GSYSVAR_WRITE; - } - - /* - * SHOW GLOBAL STATUS - Route to master - */ - else if (lex->sql_command == SQLCOM_SHOW_STATUS) - { - type = QUERY_TYPE_WRITE; - } - /** - * REVOKE ALL, ASSIGN_TO_KEYCACHE, - * PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER - */ - - else - { - type |= QUERY_TYPE_GSYSVAR_WRITE; - } - - goto return_qtype; - } - else if (lex->option_type == OPT_SESSION) - { - /** - * SHOW syntax http://dev.mysql.com/doc/refman/5.6/en/show.html - */ - if (lex->sql_command == SQLCOM_SHOW_VARIABLES) - { - type |= QUERY_TYPE_SYSVAR_READ; - } - /** - * SET syntax http://dev.mysql.com/doc/refman/5.6/en/set-statement.html - */ - else if (lex->sql_command == SQLCOM_SET_OPTION) - { - /** Either user- or system variable write */ - type |= QUERY_TYPE_GSYSVAR_WRITE; - } - - goto return_qtype; - } - - /** - * 1:ALTER TABLE, TRUNCATE, REPAIR, OPTIMIZE, ANALYZE, CHECK. - * 2:CREATE|ALTER|DROP|TRUNCATE|RENAME TABLE, LOAD, CREATE|DROP|ALTER DB, - * CREATE|DROP INDEX, CREATE|DROP VIEW, CREATE|DROP TRIGGER, - * CREATE|ALTER|DROP EVENT, UPDATE, INSERT, INSERT(SELECT), - * DELETE, REPLACE, REPLACE(SELECT), CREATE|RENAME|DROP USER, - * GRANT, REVOKE, OPTIMIZE, CREATE|ALTER|DROP FUNCTION|PROCEDURE, - * CREATE SPFUNCTION, INSTALL|UNINSTALL PLUGIN - */ - if (is_log_table_write_query(lex->sql_command) || - is_update_query(lex->sql_command)) - { -#if defined(NOT_IN_USE) - - if (thd->variables.sql_log_bin == 0 && - force_data_modify_op_replication) - { - /** Not replicated */ - type |= QUERY_TYPE_SESSION_WRITE; - } - else -#endif /* NOT_IN_USE */ - { - /** Written to binlog, that is, replicated except tmp tables */ - type |= QUERY_TYPE_WRITE; /*< to master */ - - if (lex->sql_command == SQLCOM_CREATE_TABLE && - (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) - { - type |= QUERY_TYPE_CREATE_TMP_TABLE; /*< remember in router */ - } - } - - goto return_qtype; - } - - /** Try to catch session modifications here */ - switch (lex->sql_command) - { - /** fallthrough */ - case SQLCOM_CHANGE_DB: - case SQLCOM_DEALLOCATE_PREPARE: - type |= QUERY_TYPE_SESSION_WRITE; - break; - - case SQLCOM_SELECT: - case SQLCOM_SHOW_SLAVE_STAT: - type |= QUERY_TYPE_READ; - break; - - case SQLCOM_CALL: - type |= QUERY_TYPE_WRITE; - break; - - case SQLCOM_BEGIN: - type |= QUERY_TYPE_BEGIN_TRX; - goto return_qtype; - break; - - case SQLCOM_COMMIT: - type |= QUERY_TYPE_COMMIT; - goto return_qtype; - break; - - case SQLCOM_ROLLBACK: - type |= QUERY_TYPE_ROLLBACK; - goto return_qtype; - break; - - case SQLCOM_PREPARE: - type |= QUERY_TYPE_PREPARE_NAMED_STMT; - goto return_qtype; - break; - - case SQLCOM_SHOW_DATABASES: - type |= QUERY_TYPE_SHOW_DATABASES; - goto return_qtype; - break; - - case SQLCOM_SHOW_TABLES: - type |= QUERY_TYPE_SHOW_TABLES; - goto return_qtype; - break; - - default: - break; - } - -#if defined(UPDATE_VAR_SUPPORT) - - if (QTYPE_LESS_RESTRICTIVE_THAN_WRITE(type)) -#endif - if (QUERY_IS_TYPE(qtype, QUERY_TYPE_UNKNOWN) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_LOCAL_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_READ)) - { - /** - * These values won't change qtype more restrictive than write. - * UDFs and procedures could possibly cause session-wide write, - * but unless their content is replicated this is a limitation - * of this implementation. - * In other words : UDFs and procedures are not allowed to - * perform writes which are not replicated but need to repeat - * in every node. - * It is not sure if such statements exist. vraa 25.10.13 - */ - - /** - * Search for system functions, UDFs and stored procedures. - */ - for (item = thd->free_list; item != NULL; item = item->next) - { - Item::Type itype; - - itype = item->type(); - MXS_DEBUG("%lu [resolve_query_type] Item %s:%s", - pthread_self(), item->name, STRITEMTYPE(itype)); - - if (itype == Item::SUBSELECT_ITEM) - { - continue; - } - else if (itype == Item::FUNC_ITEM) - { - int func_qtype = QUERY_TYPE_UNKNOWN; - /** - * Item types: - * FIELD_ITEM = 0, FUNC_ITEM, - * SUM_FUNC_ITEM, STRING_ITEM, INT_ITEM, - * REAL_ITEM, NULL_ITEM, VARBIN_ITEM, - * COPY_STR_ITEM, FIELD_AVG_ITEM, - * DEFAULT_VALUE_ITEM, PROC_ITEM, - * COND_ITEM, REF_ITEM, FIELD_STD_ITEM, - * FIELD_VARIANCE_ITEM, - * INSERT_VALUE_ITEM, - * SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, - * TYPE_HOLDER, PARAM_ITEM, - * TRIGGER_FIELD_ITEM, DECIMAL_ITEM, - * XPATH_NODESET, XPATH_NODESET_CMP, - * VIEW_FIXER_ITEM, - * EXPR_CACHE_ITEM == 27 - **/ - - Item_func::Functype ftype; - ftype = ((Item_func*) item)->functype(); - - /** - * Item_func types: - * - * UNKNOWN_FUNC = 0,EQ_FUNC, EQUAL_FUNC, - * NE_FUNC, LT_FUNC, LE_FUNC, - * GE_FUNC, GT_FUNC, FT_FUNC, - * LIKE_FUNC == 10, ISNULL_FUNC, ISNOTNULL_FUNC, - * COND_AND_FUNC, COND_OR_FUNC, XOR_FUNC, - * BETWEEN, IN_FUNC, - * MULT_EQUAL_FUNC, INTERVAL_FUNC, - * ISNOTNULLTEST_FUNC == 20, - * SP_EQUALS_FUNC, SP_DISJOINT_FUNC, - * SP_INTERSECTS_FUNC, - * SP_TOUCHES_FUNC, SP_CROSSES_FUNC, - * SP_WITHIN_FUNC, SP_CONTAINS_FUNC, - * SP_OVERLAPS_FUNC, - * SP_STARTPOINT, SP_ENDPOINT == 30, - * SP_EXTERIORRING, SP_POINTN, SP_GEOMETRYN, - * SP_INTERIORRINGN,NOT_FUNC, NOT_ALL_FUNC, - * NOW_FUNC, TRIG_COND_FUNC, - * SUSERVAR_FUNC, GUSERVAR_FUNC == 40, - * COLLATE_FUNC, EXTRACT_FUNC, - * CHAR_TYPECAST_FUNC, - * FUNC_SP, UDF_FUNC, NEG_FUNC, - * GSYSVAR_FUNC == 47 - **/ - switch (ftype) - { - case Item_func::FUNC_SP: - /** - * An unknown (for maxscale) function / sp - * belongs to this category. - */ - func_qtype |= QUERY_TYPE_WRITE; - MXS_DEBUG("%lu [resolve_query_type] " - "functype FUNC_SP, stored proc " - "or unknown function.", - pthread_self()); - break; - - case Item_func::UDF_FUNC: - func_qtype |= QUERY_TYPE_WRITE; - MXS_DEBUG("%lu [resolve_query_type] " - "functype UDF_FUNC, user-defined " - "function.", - pthread_self()); - break; - - case Item_func::NOW_FUNC: - func_qtype |= QUERY_TYPE_LOCAL_READ; - MXS_DEBUG("%lu [resolve_query_type] " - "functype NOW_FUNC, could be " - "executed in MaxScale.", - pthread_self()); - break; - - /** System session variable */ - case Item_func::GSYSVAR_FUNC: - func_qtype |= QUERY_TYPE_SYSVAR_READ; - MXS_DEBUG("%lu [resolve_query_type] " - "functype GSYSVAR_FUNC, system " - "variable read.", - pthread_self()); - break; - - /** User-defined variable read */ - case Item_func::GUSERVAR_FUNC: - func_qtype |= QUERY_TYPE_USERVAR_READ; - MXS_DEBUG("%lu [resolve_query_type] " - "functype GUSERVAR_FUNC, user " - "variable read.", - pthread_self()); - break; - - /** User-defined variable modification */ - case Item_func::SUSERVAR_FUNC: - /** - * Really it is user variable but we - * don't separate sql variables atm. - * 15.9.14 - */ - func_qtype |= QUERY_TYPE_GSYSVAR_WRITE; - MXS_DEBUG("%lu [resolve_query_type] " - "functype SUSERVAR_FUNC, user " - "variable write.", - pthread_self()); - break; - - case Item_func::UNKNOWN_FUNC: - - if (((Item_func*) item)->func_name() != NULL && - strcmp((char*) ((Item_func*) item)->func_name(), "last_insert_id") == 0) - { - func_qtype |= QUERY_TYPE_MASTER_READ; - } - else - { - func_qtype |= QUERY_TYPE_READ; - } - - /** - * Many built-in functions are of this - * type, for example, rand(), soundex(), - * repeat() . - */ - MXS_DEBUG("%lu [resolve_query_type] " - "functype UNKNOWN_FUNC, " - "typically some system function.", - pthread_self()); - break; - - default: - MXS_DEBUG("%lu [resolve_query_type] " - "Functype %d.", - pthread_self(), - ftype); - break; - } /**< switch */ - - /**< Set new query type */ - type |= func_qtype; - } - -#if defined(UPDATE_VAR_SUPPORT) - - /** - * Write is as restrictive as it gets due functions, - * so break. - */ - if ((type & QUERY_TYPE_WRITE) == QUERY_TYPE_WRITE) - { - break; - } - -#endif - } /**< for */ - } /**< if */ - -return_qtype: - qtype = (qc_query_type_t) type; - return qtype; -} - -/** - * Checks if statement causes implicit COMMIT. - * autocommit_stmt gets values 1, 0 or -1 if stmt is enable, disable or - * something else than autocommit. - * - * @param lex Parse tree - * @param autocommit_stmt memory address for autocommit status - * - * @return true if statement causes implicit commit and false otherwise - */ -static bool skygw_stmt_causes_implicit_commit(LEX* lex, int* autocommit_stmt) -{ - bool succp; - - if (!(sql_command_flags[lex->sql_command] & CF_AUTO_COMMIT_TRANS)) - { - succp = false; - goto return_succp; - } - - switch (lex->sql_command) - { - case SQLCOM_DROP_TABLE: - succp = !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); - break; - - case SQLCOM_ALTER_TABLE: - case SQLCOM_CREATE_TABLE: - /* If CREATE TABLE of non-temporary table, do implicit commit */ - succp = !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); - break; - - case SQLCOM_SET_OPTION: - if ((*autocommit_stmt = is_autocommit_stmt(lex)) == 1) - { - succp = true; - } - else - { - succp = false; - } - - break; - - default: - succp = true; - break; - } - -return_succp: - return succp; -} - -/** - * Finds out if stmt is SET autocommit - * and if the new value matches with the enable_cmd argument. - * - * @param lex parse tree - * - * @return 1, 0, or -1 if command was: - * enable, disable, or not autocommit, respectively. - */ -static int is_autocommit_stmt(LEX* lex) -{ - struct list_node* node; - set_var* setvar; - int rc = -1; - static char target[8]; /*< for converted string */ - Item* item = NULL; - - node = lex->var_list.first_node(); - setvar = (set_var*) node->info; - - if (setvar == NULL) - { - goto return_rc; - } - - do /*< Search for the last occurrence of 'autocommit' */ - { - if ((sys_var*) setvar->var == Sys_autocommit_ptr) - { - item = setvar->value; - } - - node = node->next; - } - while ((setvar = (set_var*) node->info) != NULL); - - if (item != NULL) /*< found autocommit command */ - { - if (item->type() == Item::INT_ITEM) /*< '0' or '1' */ - { - rc = item->val_int(); - - if (rc > 1 || rc < 0) - { - rc = -1; - } - } - else if (item->type() == Item::STRING_ITEM) /*< 'on' or 'off' */ - { - String str(target, sizeof (target), system_charset_info); - String* res = item->val_str(&str); - - if ((rc = find_type(&bool_typelib, res->ptr(), res->length(), false))) - { - ss_dassert(rc >= 0 && rc <= 2); - /** - * rc is the position of matchin string in - * typelib's value array. - * 1=OFF, 2=ON. - */ - rc -= 1; - } - } - } - -return_rc: - return rc; -} - -#if defined(NOT_USED) - -char* qc_get_stmtname(GWBUF* buf) -{ - MYSQL* mysql; - - if (buf == NULL || - buf->gwbuf_bufobj == NULL || - buf->gwbuf_bufobj->bo_data == NULL || - (mysql = (MYSQL *) ((parsing_info_t *) buf->gwbuf_bufobj->bo_data)->pi_handle) == NULL || - mysql->thd == NULL || - (THD *) (mysql->thd))->lex == NULL || - (THD *) (mysql->thd))->lex->prepared_stmt_name == NULL) - { - return NULL; - } - - return ((THD *) (mysql->thd))->lex->prepared_stmt_name.str; -} +#if defined(QC_TRACE_ENABLED) +#define QC_TRACE() MXS_NOTICE(__func__) +#else +#define QC_TRACE() #endif -/** - * Get the parse tree from parsed querybuf. - * @param querybuf The parsed GWBUF - * - * @return Pointer to the LEX struct or NULL if an error occurred or the query - * was not parsed - */ -LEX* get_lex(GWBUF* querybuf) -{ +static const char default_qc_name[] = "qc_mysqlembedded"; - parsing_info_t* pi; - MYSQL* mysql; - THD* thd; - - if (querybuf == NULL || !GWBUF_IS_PARSED(querybuf)) - { - return NULL; - } - - pi = (parsing_info_t *) gwbuf_get_buffer_object_data(querybuf, GWBUF_PARSING_INFO); - - if (pi == NULL) - { - return NULL; - } - - if ((mysql = (MYSQL *) pi->pi_handle) == NULL || - (thd = (THD *) mysql->thd) == NULL) - { - ss_dassert(mysql != NULL && thd != NULL); - return NULL; - } - - return thd->lex; -} - -/** - * Finds the head of the list of tables affected by the current select statement. - * @param thd Pointer to a valid THD - * @return Pointer to the head of the TABLE_LIST chain or NULL in case of an error - */ -static void* skygw_get_affected_tables(void* lexptr) -{ - LEX* lex = (LEX*) lexptr; - - if (lex == NULL || lex->current_select == NULL) - { - ss_dassert(lex != NULL && lex->current_select != NULL); - return NULL; - } - - return (void*) lex->current_select->table_list.first; -} - -/** - * Reads the parsetree and lists all the affected tables and views in the query. - * In the case of an error, the size of the table is set to zero and no memory - * is allocated. The caller must free the allocated memory. - * - * @param querybuf GWBUF where the table names are extracted from - * @param tblsize Pointer where the number of tables is written - * @return Array of null-terminated strings with the table names - */ -char** qc_get_table_names(GWBUF* querybuf, int* tblsize, bool fullnames) -{ - LEX* lex; - TABLE_LIST* tbl; - int i = 0, currtblsz = 0; - char **tables = NULL, **tmp = NULL; - - if (querybuf == NULL || tblsize == NULL) - { - goto retblock; - } - - if (!ensure_query_is_parsed(querybuf)) - { - goto retblock; - } - - if ((lex = get_lex(querybuf)) == NULL || lex->current_select == NULL) - { - goto retblock; - } - - lex->current_select = lex->all_selects_list; - - while (lex->current_select) - { - tbl = (TABLE_LIST*) skygw_get_affected_tables(lex); - - while (tbl) - { - if (i >= currtblsz) - { - tmp = (char**) malloc(sizeof (char*)*(currtblsz * 2 + 1)); - - if (tmp) - { - if (currtblsz > 0) - { - for (int x = 0; x < currtblsz; x++) - { - tmp[x] = tables[x]; - } - - free(tables); - } - - tables = tmp; - currtblsz = currtblsz * 2 + 1; - } - } - - if (tmp != NULL) - { - char *catnm = NULL; - - if (fullnames) - { - if (tbl->db && - strcmp(tbl->db, "skygw_virtual") != 0) - { - catnm = (char*) calloc(strlen(tbl->db) + - strlen(tbl->table_name) + - 2, - sizeof (char)); - strcpy(catnm, tbl->db); - strcat(catnm, "."); - strcat(catnm, tbl->table_name); - } - } - - if (catnm) - { - tables[i++] = catnm; - } - else - { - tables[i++] = strdup(tbl->table_name); - } - - tbl = tbl->next_local; - } - } /*< while (tbl) */ - - lex->current_select = lex->current_select->next_select_in_list(); - } /*< while(lex->current_select) */ - -retblock: - - if (tblsize) - { - *tblsize = i; - } - - return tables; -} - -/** - * Extract, allocate memory and copy the name of the created table. - * @param querybuf Buffer to use. - * @return A pointer to the name if a table was created, otherwise NULL - */ -char* qc_get_created_table_name(GWBUF* querybuf) -{ - if (querybuf == NULL) - { - return NULL; - } - - if (!ensure_query_is_parsed(querybuf)) - { - return NULL; - } - - LEX* lex = get_lex(querybuf); - - if (lex == NULL) - { - return NULL; - } - - if (lex->create_last_non_select_table && - lex->create_last_non_select_table->table_name) - { - char* name = strdup(lex->create_last_non_select_table->table_name); - return name; - } - else - { - return NULL; - } -} - -/** - * Checks whether the query is a "real" query ie. SELECT,UPDATE,INSERT,DELETE or - * any variation of these. Queries that affect the underlying database are not - * considered as real queries and the queries that target specific row or - * variable data are regarded as the real queries. - * - * @param GWBUF to analyze - * - * @return true if the query is a real query, otherwise false - */ -bool qc_is_real_query(GWBUF* querybuf) -{ - bool succp; - LEX* lex; - - if (querybuf == NULL) - { - succp = false; - goto retblock; - } - - if (!ensure_query_is_parsed(querybuf)) - { - succp = false; - goto retblock; - } - - if ((lex = get_lex(querybuf)) == NULL) - { - succp = false; - goto retblock; - } - - switch (lex->sql_command) - { - case SQLCOM_SELECT: - succp = lex->all_selects_list->table_list.elements > 0; - goto retblock; - break; - - case SQLCOM_UPDATE: - case SQLCOM_INSERT: - case SQLCOM_INSERT_SELECT: - case SQLCOM_DELETE: - case SQLCOM_TRUNCATE: - case SQLCOM_REPLACE: - case SQLCOM_REPLACE_SELECT: - case SQLCOM_PREPARE: - case SQLCOM_EXECUTE: - succp = true; - goto retblock; - break; - - default: - succp = false; - goto retblock; - break; - } - -retblock: - return succp; -} - -/** - * Checks whether the buffer contains a DROP TABLE... query. - * @param querybuf Buffer to inspect - * @return true if it contains the query otherwise false - */ -bool qc_is_drop_table_query(GWBUF* querybuf) -{ - bool answer = false; - - if (querybuf) - { - if (ensure_query_is_parsed(querybuf)) - { - LEX* lex = get_lex(querybuf); - - answer = lex && lex->sql_command == SQLCOM_DROP_TABLE; - } - } - - return answer; -} - -inline void add_str(char** buf, int* buflen, int* bufsize, char* str) -{ - int isize = strlen(str) + 1; - - if (*buf == NULL || isize + *buflen >= *bufsize) - { - *bufsize = (*bufsize) * 2 + isize; - char *tmp = (char*) realloc(*buf, (*bufsize) * sizeof (char)); - - if (tmp == NULL) - { - MXS_ERROR("Error: memory reallocation failed."); - free(*buf); - *buf = NULL; - *bufsize = 0; - } - - *buf = tmp; - } - - if (*buflen > 0) - { - if (*buf) - { - strcat(*buf, " "); - } - } - - if (*buf) - { - strcat(*buf, str); - } - - *buflen += isize; - -} - -/** - * Returns all the fields that the query affects. - * @param buf Buffer to parse - * @return Pointer to newly allocated string or NULL if nothing was found - */ -char* qc_get_affected_fields(GWBUF* buf) -{ - LEX* lex; - int buffsz = 0, bufflen = 0; - char* where = NULL; - Item* item; - Item::Type itype; - - if (!buf) - { - return NULL; - } - - if (!ensure_query_is_parsed(buf)) - { - return NULL; - } - - if ((lex = get_lex(buf)) == NULL) - { - return NULL; - } - - lex->current_select = lex->all_selects_list; - - if ((where = (char*) malloc(sizeof (char)*1)) == NULL) - { - MXS_ERROR("Memory allocation failed."); - return NULL; - } - - *where = '\0'; - - while (lex->current_select) - { - - List_iterator ilist(lex->current_select->item_list); - item = (Item*) ilist.next(); - - for (; item != NULL; item = (Item*) ilist.next()) - { - - itype = item->type(); - - if (item->name && itype == Item::FIELD_ITEM) - { - add_str(&where, &buffsz, &bufflen, item->name); - } - } - - - if (lex->current_select->where) - { - for (item = lex->current_select->where; item != NULL; item = item->next) - { - - itype = item->type(); - - if (item->name && itype == Item::FIELD_ITEM) - { - add_str(&where, &buffsz, &bufflen, item->name); - } - } - } - - if (lex->current_select->having) - { - for (item = lex->current_select->having; item != NULL; item = item->next) - { - - itype = item->type(); - - if (item->name && itype == Item::FIELD_ITEM) - { - add_str(&where, &buffsz, &bufflen, item->name); - } - } - } - - lex->current_select = lex->current_select->next_select_in_list(); - } - - return where; -} - -bool qc_query_has_clause(GWBUF* buf) -{ - bool clause = false; - - if (buf) - { - if (ensure_query_is_parsed(buf)) - { - LEX* lex = get_lex(buf); - - if (lex) - { - SELECT_LEX* current = lex->all_selects_list; - - while (current && !clause) - { - if (current->where || current->having) - { - clause = true; - } - - current = current->next_select_in_list(); - } - } - } - } - - return clause; -} - -/* - * Replace user-provided literals with question marks. - * - * @param querybuf GWBUF with a COM_QUERY statement - * @return A copy of the query in its canonical form or NULL if an error occurred. - */ -char* qc_get_canonical(GWBUF* querybuf) -{ - char *querystr = NULL; - if (GWBUF_LENGTH(querybuf) > MYSQL_COM_QUERY_HEADER_SIZE && GWBUF_IS_SQL(querybuf)) - { - size_t srcsize = GWBUF_LENGTH(querybuf) - MYSQL_COM_QUERY_HEADER_SIZE; - char *src = (char*) malloc(srcsize); - size_t destsize = 0; - char *dest = NULL; - if (src) - { - memcpy(src, (uint8_t*) GWBUF_DATA(querybuf) + MYSQL_COM_QUERY_HEADER_SIZE, - srcsize); - if (replace_quoted((const char**) &src, &srcsize, &dest, &destsize) && - remove_mysql_comments((const char**) &dest, &destsize, &src, &srcsize) && - replace_values((const char**) &src, &srcsize, &dest, &destsize)) - { - querystr = dest; - } - free(src); - } - } - return querystr; -} - -/** - * 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 - */ -static 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); - - if (mysql == NULL) - { - MXS_ERROR("Call to mysql_real_connect failed due %d, %s.", - mysql_errno(mysql), - mysql_error(mysql)); - ss_dassert(mysql != NULL); - 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); - 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 - * - */ -static void parsing_info_done(void* ptr) -{ - parsing_info_t* pi; - THD* thd; - - if (ptr) - { - pi = (parsing_info_t *) ptr; - - if (pi->pi_handle != NULL) - { - MYSQL* mysql = (MYSQL *) pi->pi_handle; - - if (mysql->thd != NULL) - { - thd = (THD*) mysql->thd; - thd->end_statement(); - (*mysql->methods->free_embedded_thd)(mysql); - mysql->thd = NULL; - } - - mysql_close(mysql); - } - - /** 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; -} - -/** - * Generate a string of query type value. - * Caller must free the memory of the resulting string. - * - * @param qtype Query type value, combination of values listed in - * query_classifier.h - * - * @return string representing the query type value - */ -char* qc_get_qtype_str(qc_query_type_t qtype) -{ - int t1 = (int) qtype; - int t2 = 1; - qc_query_type_t t = QUERY_TYPE_UNKNOWN; - char* qtype_str = NULL; - - /** - * Test values (bits) and clear matching bits from t1 one by one until - * t1 is completely cleared. - */ - while (t1 != 0) - { - if (t1 & t2) - { - t = (qc_query_type_t) t2; - - if (qtype_str == NULL) - { - qtype_str = strdup(STRQTYPE(t)); - } - else - { - size_t len = strlen(STRQTYPE(t)); - /** reallocate space for delimiter, new string and termination */ - qtype_str = (char *) realloc(qtype_str, strlen(qtype_str) + 1 + len + 1); - snprintf(qtype_str + strlen(qtype_str), 1 + len + 1, "|%s", STRQTYPE(t)); - } - - /** Remove found value from t1 */ - t1 &= ~t2; - } - - t2 <<= 1; - } - - return qtype_str; -} - -/** - * Returns an array of strings of databases that this query uses. - * If the database isn't defined in the query, it is assumed that this query - * only targets the current database. - * The value of @p size is set to the number of allocated strings. The caller is - * responsible for freeing all the allocated memory. - * @param querybuf GWBUF containing the query - * @param size Size of the resulting array - * @return A new array of strings containing the database names or NULL if no - * databases were found. - */ -char** qc_get_database_names(GWBUF* querybuf, int* size) -{ - LEX* lex; - TABLE_LIST* tbl; - char **databases = NULL, **tmp = NULL; - int currsz = 0, i = 0; - - if (!querybuf) - { - goto retblock; - } - - if (!ensure_query_is_parsed(querybuf)) - { - goto retblock; - } - - if ((lex = get_lex(querybuf)) == NULL) - { - goto retblock; - } - - lex->current_select = lex->all_selects_list; - - while (lex->current_select) - { - tbl = lex->current_select->table_list.first; - - while (tbl) - { - if (strcmp(tbl->db, "skygw_virtual") != 0) - { - if (i >= currsz) - { - tmp = (char**) realloc(databases, - sizeof (char*)*(currsz * 2 + 1)); - - if (tmp == NULL) - { - goto retblock; - } - - databases = tmp; - currsz = currsz * 2 + 1; - } - - databases[i++] = strdup(tbl->db); - } - - tbl = tbl->next_local; - } - - lex->current_select = lex->current_select->next_select_in_list(); - } - -retblock: - *size = i; - return databases; -} - -qc_query_op_t qc_get_operation(GWBUF* querybuf) -{ - qc_query_op_t operation = QUERY_OP_UNDEFINED; - - if (querybuf) - { - if (ensure_query_is_parsed(querybuf)) - { - LEX* lex = get_lex(querybuf); - - if (lex) - { - switch (lex->sql_command) - { - case SQLCOM_SELECT: - operation = QUERY_OP_SELECT; - break; - - case SQLCOM_CREATE_TABLE: - operation = QUERY_OP_CREATE_TABLE; - break; - - case SQLCOM_CREATE_INDEX: - operation = QUERY_OP_CREATE_INDEX; - break; - - case SQLCOM_ALTER_TABLE: - operation = QUERY_OP_ALTER_TABLE; - break; - - case SQLCOM_UPDATE: - operation = QUERY_OP_UPDATE; - break; - - case SQLCOM_INSERT: - operation = QUERY_OP_INSERT; - break; - - case SQLCOM_INSERT_SELECT: - operation = QUERY_OP_INSERT_SELECT; - break; - - case SQLCOM_DELETE: - operation = QUERY_OP_DELETE; - break; - - case SQLCOM_TRUNCATE: - operation = QUERY_OP_TRUNCATE; - break; - - case SQLCOM_DROP_TABLE: - operation = QUERY_OP_DROP_TABLE; - break; - - case SQLCOM_DROP_INDEX: - operation = QUERY_OP_DROP_INDEX; - break; - - case SQLCOM_CHANGE_DB: - operation = QUERY_OP_CHANGE_DB; - break; - - case SQLCOM_LOAD: - operation = QUERY_OP_LOAD; - break; - - default: - operation = QUERY_OP_UNDEFINED; - } - } - } - } - - return operation; -} +static QUERY_CLASSIFIER* classifier; bool qc_init(int argc, char** argv, char** groups) { - int rc = mysql_library_init(argc, argv, groups); + QC_TRACE(); + ss_dassert(!classifier); - if (rc != 0) + bool success = false; + void* module = load_module(default_qc_name, MODULE_QUERY_CLASSIFIER); + + if (module) { - MXS_ERROR("mysql_library_init() failed. Error code: %d", rc); + classifier = (QUERY_CLASSIFIER*) module; + MXS_NOTICE("%s loaded.", default_qc_name); + + success = classifier->qc_init(argc, argv, groups); } else { - MXS_NOTICE("Query classifier initialized."); + MXS_ERROR("Could not load %s.", default_qc_name); } - return rc == 0; + return success; } void qc_end() { - mysql_library_end(); + QC_TRACE(); + ss_dassert(classifier); + + classifier->qc_end(); + classifier = NULL; } bool qc_thread_init() { - bool inited = (mysql_thread_init() == 0); + QC_TRACE(); + ss_dassert(classifier); - if (!inited) - { - MXS_ERROR("mysql_thread_init() failed."); - } - - return inited; + return classifier->qc_thread_init(); } void qc_thread_end() { - mysql_thread_end(); + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_thread_end(); +} + +qc_query_type_t qc_get_type(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_type(query); +} + +qc_query_op_t qc_get_operation(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_operation(query); +} + +char* qc_get_created_table_name(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_created_table_name(query); +} + +bool qc_is_drop_table_query(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_is_drop_table_query(query); +} + +bool qc_is_real_query(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_is_real_query(query); +} + +char** qc_get_table_names(GWBUF* query, int* tblsize, bool fullnames) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_table_names(query, tblsize, fullnames); +} + +char* qc_get_canonical(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_canonical(query); +} + +bool qc_query_has_clause(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_query_has_clause(query); +} + +char* qc_get_qtype_str(qc_query_type_t qtype) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_qtype_str(qtype); +} + +char* qc_get_affected_fields(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_affected_fields(query); +} + +char** qc_get_database_names(GWBUF* query, int* sizep) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_database_names(query, sizep); } diff --git a/server/core/gateway.c b/server/core/gateway.c index 35e2ac00b..20f3d9709 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -507,7 +507,15 @@ static void libmysqld_done(void) { if (libmysqld_started) { - mysql_library_end(); + // TODO: qc_end() (if qc_mysqlembedded is used) also calls mysql_library_end(), + // TODO: which refers to the mysql_library_end() in the embedded library. This + // TODO: one would call the mysql_library_end() in the client library. It seems + // TODO: that would work, but for the fact that both de-initialize some lower + // TODO: level library, which in turn does not work. Thus, for the time being + // TODO: this call is not made. + // TODO: Linking MaxScale with Connector-C would likely make this problem + // TODO: go away. + //mysql_library_end(); } } @@ -2012,6 +2020,8 @@ int main(int argc, char **argv) /** Release mysql thread context*/ mysql_thread_end(); + qc_end(); + utils_end(); datadir_cleanup(); MXS_NOTICE("MaxScale shutdown completed."); diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index 8897125d2..1e2035ad2 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -4,7 +4,9 @@ target_link_libraries(binlogrouter maxscale-common) install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR}) add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c) -target_link_libraries(maxbinlogcheck maxscale-common query_classifier) +# maxbinlogcheck refers to my_uuid_init and my_uuin. They are non-public functions and +# should not be used. They are found only from the embedded lib. +target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${EMBEDDED_LIB}) install(TARGETS maxbinlogcheck DESTINATION bin) diff --git a/server/modules/routing/binlog/test/CMakeLists.txt b/server/modules/routing/binlog/test/CMakeLists.txt index ff5392839..ad02de4f9 100644 --- a/server/modules/routing/binlog/test/CMakeLists.txt +++ b/server/modules/routing/binlog/test/CMakeLists.txt @@ -1,5 +1,7 @@ if(BUILD_TESTS) add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c) - target_link_libraries(testbinlogrouter maxscale-common query_classifier) + # testbinlogrouter refers to my_uuid_init and my_uuin. They are non-public functions and + # should not be used. They are found only from the embedded lib. + target_link_libraries(testbinlogrouter maxscale-common query_classifier ${EMBEDDED_LIB}) add_test(TestBinlogRouter ${CMAKE_CURRENT_BINARY_DIR}/testbinlogrouter) endif() From 7e36e1cbb155317f8da365ee7c00984bc5f8f964 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 29 Jan 2016 16:50:31 +0200 Subject: [PATCH 02/13] Link against Connector-C. Now, qc_mysqlembedded is linked against MySQL's embedded library, and MaxScale itself against Connector-C. So, in order to build MaxScale, Connector-C must be installed. This has been tested with Connector-C 2.2.1. The build variable MYSQLCLIENT_LIBRARIES is no longer used. --- CMakeLists.txt | 6 ----- cmake/FindMySQLClient.cmake | 22 +++++++++---------- query_classifier/CMakeLists.txt | 2 +- .../qc_mysqlembedded/CMakeLists.txt | 6 +++++ query_classifier/test/CMakeLists.txt | 6 +++++ rabbitmq_consumer/CMakeLists.txt | 4 ++-- server/CMakeLists.txt | 2 ++ server/core/CMakeLists.txt | 2 +- server/core/dbusers.c | 5 +++++ server/core/gateway.c | 10 +-------- server/modules/filter/mqfilter.c | 1 - .../include/mysql_client_server_protocol.h | 11 +++++++++- server/modules/routing/binlog/CMakeLists.txt | 2 +- .../routing/binlog/test/CMakeLists.txt | 2 +- .../routing/schemarouter/test/CMakeLists.txt | 4 ++-- server/modules/routing/test/CMakeLists.txt | 6 ++--- 16 files changed, 52 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b975c62db..51735eda8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,12 +157,6 @@ set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG -DLOG_ASSERT") set(CMAKE_CXX_FLAGS_RELEASE "") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-ggdb") -subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) -foreach(DIR ${MYSQL_DIR_ALL}) - include_directories(${DIR}) -endforeach() - -include_directories(${MYSQL_DIR}/..) include_directories(utils) include_directories(log_manager) include_directories(query_classifier) diff --git a/cmake/FindMySQLClient.cmake b/cmake/FindMySQLClient.cmake index 5b4f53eff..a08c9692c 100644 --- a/cmake/FindMySQLClient.cmake +++ b/cmake/FindMySQLClient.cmake @@ -2,32 +2,32 @@ # The following variables are set: # MYSQLCLIENT_FOUND - System has MySQL client # MYSQLCLIENT_STATIC_FOUND - System has statically linked MySQL client -# MYSQLCLIENT_LIBRARIES - The MySQL client library -# MYSQLCLIENT_STATIC_LIBRARIES - The static MySQL client library +# MARIADB_CONNECTOR_LIB - The MySQL client library +# MARIADB_CONNECTOR_STATIC_LIB - The static MySQL client library # MYSQLCLIENT_HEADERS - The MySQL client headers -find_library(MYSQLCLIENT_LIBRARIES NAMES mysqlclient PATH_SUFFIXES mysql mariadb) -if(${MYSQLCLIENT_LIBRARIES} MATCHES "NOTFOUND") +find_library(MARIADB_CONNECTOR_LIB NAMES mysqlclient PATH_SUFFIXES mysql mariadb) +if(${MARIADB_CONNECTOR_LIB} MATCHES "NOTFOUND") set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "") message(STATUS "Dynamic MySQL client library not found.") - unset(MYSQLCLIENT_LIBRARIES) + unset(MARIADB_CONNECTOR_LIB) else() set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "") - message(STATUS "Found dynamic MySQL client library: ${MYSQLCLIENT_LIBRARIES}") + message(STATUS "Found dynamic MySQL client library: ${MARIADB_CONNECTOR_LIB}") endif() set(OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") -find_library(MYSQLCLIENT_STATIC_LIBRARIES NAMES mysqlclient PATH_SUFFIXES mysql mariadb) +find_library(MARIADB_CONNECTOR_STATIC_LIB NAMES mysqlclient PATH_SUFFIXES mysql mariadb) set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) -if(${MYSQLCLIENT_STATIC_LIBRARIES} MATCHES "NOTFOUND") +if(${MARIADB_CONNECTOR_STATIC_LIB} MATCHES "NOTFOUND") set(MYSQLCLIENT_STATIC_FOUND FALSE CACHE INTERNAL "") message(STATUS "Static MySQL client library not found.") - unset(MYSQLCLIENT_STATIC_LIBRARIES) + unset(MARIADB_CONNECTOR_STATIC_LIB) else() set(MYSQLCLIENT_STATIC_FOUND TRUE CACHE INTERNAL "") - message(STATUS "Found statc MySQL client library: ${MYSQLCLIENT_STATIC_LIBRARIES}") + message(STATUS "Found statc MySQL client library: ${MARIADB_CONNECTOR_STATIC_LIB}") endif() -find_path(MYSQLCLIENT_HEADERS mysql.h PATH_SUFFIXES mysql mariadb) \ No newline at end of file +find_path(MYSQLCLIENT_HEADERS mysql.h PATH_SUFFIXES mysql mariadb) diff --git a/query_classifier/CMakeLists.txt b/query_classifier/CMakeLists.txt index 4780df0e9..68f4d993c 100644 --- a/query_classifier/CMakeLists.txt +++ b/query_classifier/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(query_classifier SHARED query_classifier.cc) -target_link_libraries(query_classifier ${MYSQL_EMBEDDED_LIB} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) +target_link_libraries(query_classifier maxscale-common) set_target_properties(query_classifier PROPERTIES VERSION "1.0.0") set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/query_classifier.map) set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,-z,defs) diff --git a/query_classifier/qc_mysqlembedded/CMakeLists.txt b/query_classifier/qc_mysqlembedded/CMakeLists.txt index 67066754d..d2fc724f1 100644 --- a/query_classifier/qc_mysqlembedded/CMakeLists.txt +++ b/query_classifier/qc_mysqlembedded/CMakeLists.txt @@ -1,3 +1,9 @@ +subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) +foreach(DIR ${MYSQL_DIR_ALL}) + include_directories(${DIR}) +endforeach() +include_directories(${MYSQL_INCLUDE}) + add_library(qc_mysqlembedded SHARED qc_mysqlembedded.cc) target_link_libraries(qc_mysqlembedded ${MYSQL_EMBEDDED_LIB} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) diff --git a/query_classifier/test/CMakeLists.txt b/query_classifier/test/CMakeLists.txt index 61b5702f1..f1c36e080 100644 --- a/query_classifier/test/CMakeLists.txt +++ b/query_classifier/test/CMakeLists.txt @@ -1,3 +1,9 @@ +subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) +foreach(DIR ${MYSQL_DIR_ALL}) + include_directories(${DIR}) +endforeach() +include_directories(${MYSQL_INCLUDE}) + if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND") message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=") else() diff --git a/rabbitmq_consumer/CMakeLists.txt b/rabbitmq_consumer/CMakeLists.txt index bad2de7b3..b585fd435 100644 --- a/rabbitmq_consumer/CMakeLists.txt +++ b/rabbitmq_consumer/CMakeLists.txt @@ -29,9 +29,9 @@ add_subdirectory(inih) add_executable (consumer ${CMAKE_BINARY_DIR}/consumer.c) if(MYSQLCLIENT_STATIC_FOUND) - target_link_libraries(consumer ${MYSQLCLIENT_STATIC_LIBRARIES} rabbitmq inih ssl crypt crypto dl z m pthread) + target_link_libraries(consumer ${MARIADB_CONNECTOR_STATIC_LIB} rabbitmq inih ssl crypt crypto dl z m pthread) else() - target_link_libraries(consumer ${MYSQLCLIENT_LIBRARIES} rabbitmq inih ssl crypt crypto dl z m pthread) + target_link_libraries(consumer ${MARIADB_CONNECTOR_LIB} rabbitmq inih ssl crypt crypto dl z m pthread) endif() install(TARGETS consumer DESTINATION ${MAXSCALE_BINDIR}) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 6cd174190..f4f9d1aca 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(${MARIADB_CONNECTOR_INCLUDE}) + add_subdirectory(core) add_subdirectory(modules) add_subdirectory(inih) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 4918f7fa2..173a2347a 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(maxscale-common SHARED adminusers.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c maxscale_pcre2.c memlog.c modutil.c monitor.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc ${CMAKE_SOURCE_DIR}/utils/skygw_utils.cc statistics.c) -target_link_libraries(maxscale-common ${MYSQLCLIENT_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++) +target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIB} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++) if(WITH_JEMALLOC) target_link_libraries(maxscale-common ${JEMALLOC_LIBRARIES}) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index c807e9ed4..aa554a79c 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2186,6 +2186,10 @@ MYSQL *gw_mysql_init() { if (gw_mysql_set_timeouts(con) == 0) { + // MYSQL_OPT_USE_REMOTE_CONNECTION must be set if the embedded + // libary is used. With Connector-C (at least 2.2.1) the call + // fails. +#if !defined(LIBMARIADB) if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL) != 0) { MXS_ERROR("Failed to set external connection. " @@ -2193,6 +2197,7 @@ MYSQL *gw_mysql_init() mysql_close(con); con = NULL; } +#endif } else { diff --git a/server/core/gateway.c b/server/core/gateway.c index 20f3d9709..5987735fb 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -507,15 +507,7 @@ static void libmysqld_done(void) { if (libmysqld_started) { - // TODO: qc_end() (if qc_mysqlembedded is used) also calls mysql_library_end(), - // TODO: which refers to the mysql_library_end() in the embedded library. This - // TODO: one would call the mysql_library_end() in the client library. It seems - // TODO: that would work, but for the fact that both de-initialize some lower - // TODO: level library, which in turn does not work. Thus, for the time being - // TODO: this call is not made. - // TODO: Linking MaxScale with Connector-C would likely make this problem - // TODO: go away. - //mysql_library_end(); + mysql_library_end(); } } diff --git a/server/modules/filter/mqfilter.c b/server/modules/filter/mqfilter.c index be9a07a63..002da6dca 100644 --- a/server/modules/filter/mqfilter.c +++ b/server/modules/filter/mqfilter.c @@ -80,7 +80,6 @@ #include #include #include -#include #include MODULE_INFO info = diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index 83acb80e9..b48d321ea 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -64,6 +64,7 @@ #include #include #include +#include #define GW_MYSQL_VERSION "MaxScale " MAXSCALE_VERSION #define GW_MYSQL_LOOP_TIMEOUT 300000000 @@ -224,9 +225,15 @@ typedef enum ), } gw_mysql_capabilities_t; +// mysql.h from Connector-C exposes this enum, while mysql.h from +// MariaDB does not. +// TODO: This should probably be removed as Connector-C will be +// TODO: a pre-requisite for building MaxScale. +#if defined(LIBMARIADB) +typedef enum enum_server_command mysql_server_cmd_t; +#else /** Copy from enum in mariadb-5.5 mysql_com.h */ typedef enum mysql_server_cmd { - MYSQL_COM_UNDEFINED = -1, MYSQL_COM_SLEEP = 0, MYSQL_COM_QUIT, MYSQL_COM_INIT_DB, @@ -259,7 +266,9 @@ typedef enum mysql_server_cmd { MYSQL_COM_DAEMON, MYSQL_COM_END /*< Must be the last */ } mysql_server_cmd_t; +#endif +static const mysql_server_cmd_t MYSQL_COM_UNDEFINED = (mysql_server_cmd_t)-1; /** * List of server commands, and number of response packets are stored here. diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index 1e2035ad2..0eaf5c27b 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -6,7 +6,7 @@ install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR}) add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c) # maxbinlogcheck refers to my_uuid_init and my_uuin. They are non-public functions and # should not be used. They are found only from the embedded lib. -target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${EMBEDDED_LIB}) +target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${MYSQL_EMBEDDED_LIB}) install(TARGETS maxbinlogcheck DESTINATION bin) diff --git a/server/modules/routing/binlog/test/CMakeLists.txt b/server/modules/routing/binlog/test/CMakeLists.txt index ad02de4f9..0f967487d 100644 --- a/server/modules/routing/binlog/test/CMakeLists.txt +++ b/server/modules/routing/binlog/test/CMakeLists.txt @@ -2,6 +2,6 @@ if(BUILD_TESTS) add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c) # testbinlogrouter refers to my_uuid_init and my_uuin. They are non-public functions and # should not be used. They are found only from the embedded lib. - target_link_libraries(testbinlogrouter maxscale-common query_classifier ${EMBEDDED_LIB}) + target_link_libraries(testbinlogrouter maxscale-common query_classifier ${MYSQL_EMBEDDED_LIB}) add_test(TestBinlogRouter ${CMAKE_CURRENT_BINARY_DIR}/testbinlogrouter) endif() diff --git a/server/modules/routing/schemarouter/test/CMakeLists.txt b/server/modules/routing/schemarouter/test/CMakeLists.txt index 7974c87f9..1d1bbcc3d 100644 --- a/server/modules/routing/schemarouter/test/CMakeLists.txt +++ b/server/modules/routing/schemarouter/test/CMakeLists.txt @@ -1,9 +1,9 @@ if(MYSQLCLIENT_FOUND AND BUILD_TESTS) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/test.cmake @ONLY) add_executable(testschemarouter testschemarouter.c) - target_link_libraries(testschemarouter ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread) + target_link_libraries(testschemarouter ${MARIADB_CONNECTOR_LIB} ssl crypto dl z m rt pthread) add_executable(testschemarouter2 testschemarouter2.c) - target_link_libraries(testschemarouter2 ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread) + target_link_libraries(testschemarouter2 ${MARIADB_CONNECTOR_LIB} ssl crypto dl z m rt pthread) add_test(NAME TestSchemaRouter COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/test.cmake) endif() diff --git a/server/modules/routing/test/CMakeLists.txt b/server/modules/routing/test/CMakeLists.txt index 19c51b4a1..fc622c4df 100644 --- a/server/modules/routing/test/CMakeLists.txt +++ b/server/modules/routing/test/CMakeLists.txt @@ -1,6 +1,6 @@ if(MYSQLCLIENT_FOUND) add_executable(testconnect testconnect.c) - message(STATUS "Linking against: ${MYSQLCLIENT_LIBRARIES}") - target_link_libraries(testconnect ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread) + message(STATUS "Linking against: ${MARIADB_CONNECTOR_LIB}") + target_link_libraries(testconnect ${MARIADB_CONNECTOR_LIB} ssl crypto dl z m rt pthread) add_test(NAME ReadConnRouterAuthTest COMMAND $ 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT} 1.10) -endif() \ No newline at end of file +endif() From 0c6e9f3def40b4c922cada343344f65103e3829b Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 26 Jan 2016 17:37:09 +0200 Subject: [PATCH 03/13] Make query_classifier a wrapper. The query_classifier library is now only a wrapper that loads an actual query classifier implementation. Currently it is hardwired to load qc_mysqlembedded, which implements the query classifier API using MySQL embedded. This will be changed, so that the library to load is specified when qc_init() is called. That will then allow the query classifier to be specified in the config file. Currently there seems to be a conflict between the mysql_library_end() call made in qc_mysqlembedded and the mysql_library_end() call made in gateway.c. The reason is that they both finalize a shared library. For the time being mysql_library_end() is not called in gateway.c. This problem is likely to go away by switching from the client library to the connector-c library. --- query_classifier/CMakeLists.txt | 2 +- .../qc_mysqlembedded/qc_mysqlembedded.cc | 5 +- .../qc_mysqlembedded/qc_mysqlembedded.map | 2 +- query_classifier/query_classifier.cc | 1882 ++--------------- server/core/gateway.c | 12 +- server/modules/routing/binlog/CMakeLists.txt | 4 +- .../routing/binlog/test/CMakeLists.txt | 4 +- 7 files changed, 145 insertions(+), 1766 deletions(-) diff --git a/query_classifier/CMakeLists.txt b/query_classifier/CMakeLists.txt index 7191833bf..4780df0e9 100644 --- a/query_classifier/CMakeLists.txt +++ b/query_classifier/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(query_classifier SHARED query_classifier.cc) target_link_libraries(query_classifier ${MYSQL_EMBEDDED_LIB} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) set_target_properties(query_classifier PROPERTIES VERSION "1.0.0") set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/query_classifier.map) -#set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,-z,defs) +set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,-z,defs) install(TARGETS query_classifier COMPONENT lib DESTINATION ${MAXSCALE_LIBDIR}) add_subdirectory(qc_mysqlembedded) diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index 8c312497b..c81759d64 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -1818,6 +1818,9 @@ void qc_thread_end() * EXPORTS */ +extern "C" +{ + static char version_string[] = "V1.0.0"; static QUERY_CLASSIFIER qc = @@ -1862,4 +1865,4 @@ QUERY_CLASSIFIER* GetModuleObject() return &qc; } - +} diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map index baad642c0..bff425c4e 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.map @@ -2,7 +2,7 @@ global: info; version; - GetModuleInfo; + GetModuleObject; ModuleInit; local: *; diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index b37a90d2d..401c8da1f 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -22,1794 +22,156 @@ * */ -#define EMBEDDED_LIBRARY -#define MYSQL_YACC -#define MYSQL_LEX012 -#define MYSQL_SERVER -#if defined(MYSQL_CLIENT) -#undef MYSQL_CLIENT -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// In client_settings.h mysql_server_init and mysql_server_end are defined to -// mysql_client_plugin_init and mysql_client_plugin_deinit respectively. -// Those must be undefined, so that we here really call mysql_server_[init|end]. -#undef mysql_server_init -#undef mysql_server_end -#include -#include -#include - -#include "../utils/skygw_types.h" -#include "../utils/skygw_debug.h" -#include #include -#include +#include +#include -#include -#include -#include -#include +//#define QC_TRACE_ENABLED +#undef QC_TRACE_ENABLED -#define MYSQL_COM_QUERY_HEADER_SIZE 5 /*< 3 bytes size, 1 sequence, 1 command */ -#define MAX_QUERYBUF_SIZE 2048 -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 QTYPE_LESS_RESTRICTIVE_THAN_WRITE(t) (tpi_handle; - - /** Find out the query type */ - if (mysql != NULL) - { - qtype = resolve_query_type((THD *) mysql->thd); - } - } - } - -retblock: - return qtype; -} - -/** - * 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 - */ -static bool parse_query(GWBUF* querybuf) -{ - bool succp; - THD* thd; - uint8_t* data; - size_t len; - char* query_str = NULL; - parsing_info_t* pi; - - CHK_GWBUF(querybuf); - /** Do not parse without releasing previous parse info first */ - ss_dassert(!query_is_parsed(querybuf)); - - if (querybuf == NULL || query_is_parsed(querybuf)) - { - return false; - } - - /** Create parsing info */ - pi = parsing_info_init(parsing_info_done); - - 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 */ - - - if (len < 1 || len >= ~((size_t) 0) - 1 || (query_str = (char *) malloc(len + 1)) == 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(pi, query_str); - - /** Get one or create new THD object to be use in parsing */ - thd = get_or_create_thd_for_parsing((MYSQL *) pi->pi_handle, query_str); - - if (thd == NULL) - { - /** Free parsing info data */ - parsing_info_done(pi); - succp = false; - goto retblock; - } - - /** - * Create parse_tree inside thd. - * 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; -} - -/** - * 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) -{ - CHK_GWBUF(buf); - return (buf != NULL && GWBUF_IS_PARSED(buf)); -} - -/** - * Create a thread context, thd, init embedded server, connect to it, and allocate - * query to thd. - * - * Parameters: - * @param mysql Database handle - * - * @param query_str Query in plain txt string - * - * @return Thread context pointer - * - */ -static THD* get_or_create_thd_for_parsing(MYSQL* mysql, char* query_str) -{ - THD* thd = NULL; - unsigned long client_flags; - char* db = mysql->options.db; - bool failp = FALSE; - size_t query_len; - - ss_info_dassert(mysql != NULL, ("mysql is NULL")); - ss_info_dassert(query_str != NULL, ("query_str is NULL")); - - query_len = strlen(query_str); - client_flags = set_client_flags(mysql); - - /** Get THD. - * NOTE: Instead of creating new every time, THD instance could - * be get from a pool of them. - */ - thd = (THD *) create_embedded_thd(client_flags); - - if (thd == NULL) - { - MXS_ERROR("Failed to create thread context for parsing."); - goto return_thd; - } - - mysql->thd = thd; - init_embedded_mysql(mysql, client_flags); - failp = check_embedded_connection(mysql, db); - - if (failp) - { - MXS_ERROR("Call to check_embedded_connection failed."); - goto return_err_with_thd; - } - - thd->clear_data_list(); - - /** Check that we are calling the client functions in right order */ - if (mysql->status != MYSQL_STATUS_READY) - { - set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); - MXS_ERROR("Invalid status %d in embedded server.", - mysql->status); - goto return_err_with_thd; - } - - /** Clear result variables */ - thd->current_stmt = NULL; - thd->store_globals(); - /** - * We have to call free_old_query before we start to fill mysql->fields - * for new query. In the case of embedded server we collect field data - * during query execution (not during data retrieval as it is in remote - * client). So we have to call free_old_query here - */ - free_old_query(mysql); - thd->extra_length = query_len; - thd->extra_data = query_str; - alloc_query(thd, query_str, query_len); - goto return_thd; - -return_err_with_thd: - (*mysql->methods->free_embedded_thd)(mysql); - thd = 0; - mysql->thd = 0; -return_thd: - return thd; -} - -/** - * @node Set client flags. This is copied from libmysqld.c:mysql_real_connect - * - * Parameters: - * @param mysql - - * - * - * @return - * - * - * @details (write detailed description here) - * - */ -static unsigned long set_client_flags(MYSQL* mysql) -{ - unsigned long f = 0; - - f |= mysql->options.client_flag; - - /* Send client information for access check */ - f |= CLIENT_CAPABILITIES; - - if (f & CLIENT_MULTI_STATEMENTS) - { - f |= CLIENT_MULTI_RESULTS; - } - - /** - * No compression in embedded as we don't send any data, - * and no pluggable auth, as we cannot do a client-server dialog - */ - f &= ~(CLIENT_COMPRESS | CLIENT_PLUGIN_AUTH); - - if (mysql->options.db != NULL) - { - f |= CLIENT_CONNECT_WITH_DB; - } - - return f; -} - -static bool create_parse_tree(THD* thd) -{ - Parser_state parser_state; - bool failp = FALSE; - const char* virtual_db = "skygw_virtual"; - - if (parser_state.init(thd, thd->query(), thd->query_length())) - { - failp = TRUE; - goto return_here; - } - - thd->reset_for_next_command(); - - /** - * Set some database to thd so that parsing won't fail because of - * missing database. Then parse. - */ - failp = thd->set_db(virtual_db, strlen(virtual_db)); - - if (failp) - { - MXS_ERROR("Failed to set database in thread context."); - } - - failp = parse_sql(thd, &parser_state, NULL); - - if (failp) - { - MXS_DEBUG("%lu [readwritesplit:create_parse_tree] failed to " - "create parse tree.", - pthread_self()); - } - -return_here: - return failp; -} - -/** - * Detect query type by examining parsed representation of it. - * - * @param thd MariaDB thread context. - * - * @return Copy of query type value. - * - * - * @details Query type is deduced by checking for certain properties - * of them. The order is essential. Some SQL commands have multiple - * flags set and changing the order in which flags are tested, - * the resulting type may be different. - * - */ -static qc_query_type_t resolve_query_type(THD* thd) -{ - qc_query_type_t qtype = QUERY_TYPE_UNKNOWN; - u_int32_t type = QUERY_TYPE_UNKNOWN; - int set_autocommit_stmt = -1; /*< -1 no, 0 disable, 1 enable */ - LEX* lex; - Item* item; - /** - * By default, if sql_log_bin, that is, recording data modifications - * to binary log, is disabled, gateway treats operations normally. - * Effectively nothing is replicated. - * When force_data_modify_op_replication is TRUE, gateway distributes - * all write operations to all nodes. - */ -#if defined(NOT_IN_USE) - bool force_data_modify_op_replication; - force_data_modify_op_replication = FALSE; -#endif /* NOT_IN_USE */ - ss_info_dassert(thd != NULL, ("thd is NULL\n")); - - lex = thd->lex; - - /** SELECT ..INTO variable|OUTFILE|DUMPFILE */ - if (lex->result != NULL) - { - type = QUERY_TYPE_GSYSVAR_WRITE; - goto return_qtype; - } - - if (skygw_stmt_causes_implicit_commit(lex, &set_autocommit_stmt)) - { - if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) - { - if (sql_command_flags[lex->sql_command] & - CF_IMPLICT_COMMIT_BEGIN) - { - MXS_INFO("Implicit COMMIT before executing the next command."); - } - else if (sql_command_flags[lex->sql_command] & - CF_IMPLICIT_COMMIT_END) - { - MXS_INFO("Implicit COMMIT after executing the next command."); - } - } - - if (set_autocommit_stmt == 1) - { - type |= QUERY_TYPE_ENABLE_AUTOCOMMIT; - } - - type |= QUERY_TYPE_COMMIT; - } - - if (set_autocommit_stmt == 0) - { - if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) - { - MXS_INFO("Disable autocommit : implicit START TRANSACTION" - " before executing the next command."); - } - - type |= QUERY_TYPE_DISABLE_AUTOCOMMIT; - type |= QUERY_TYPE_BEGIN_TRX; - } - - if (lex->option_type == OPT_GLOBAL) - { - /** - * SHOW syntax http://dev.mysql.com/doc/refman/5.6/en/show.html - */ - if (lex->sql_command == SQLCOM_SHOW_VARIABLES) - { - type |= QUERY_TYPE_GSYSVAR_READ; - } - /** - * SET syntax http://dev.mysql.com/doc/refman/5.6/en/set-statement.html - */ - else if (lex->sql_command == SQLCOM_SET_OPTION) - { - type |= QUERY_TYPE_GSYSVAR_WRITE; - } - - /* - * SHOW GLOBAL STATUS - Route to master - */ - else if (lex->sql_command == SQLCOM_SHOW_STATUS) - { - type = QUERY_TYPE_WRITE; - } - /** - * REVOKE ALL, ASSIGN_TO_KEYCACHE, - * PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER - */ - - else - { - type |= QUERY_TYPE_GSYSVAR_WRITE; - } - - goto return_qtype; - } - else if (lex->option_type == OPT_SESSION) - { - /** - * SHOW syntax http://dev.mysql.com/doc/refman/5.6/en/show.html - */ - if (lex->sql_command == SQLCOM_SHOW_VARIABLES) - { - type |= QUERY_TYPE_SYSVAR_READ; - } - /** - * SET syntax http://dev.mysql.com/doc/refman/5.6/en/set-statement.html - */ - else if (lex->sql_command == SQLCOM_SET_OPTION) - { - /** Either user- or system variable write */ - type |= QUERY_TYPE_GSYSVAR_WRITE; - } - - goto return_qtype; - } - - /** - * 1:ALTER TABLE, TRUNCATE, REPAIR, OPTIMIZE, ANALYZE, CHECK. - * 2:CREATE|ALTER|DROP|TRUNCATE|RENAME TABLE, LOAD, CREATE|DROP|ALTER DB, - * CREATE|DROP INDEX, CREATE|DROP VIEW, CREATE|DROP TRIGGER, - * CREATE|ALTER|DROP EVENT, UPDATE, INSERT, INSERT(SELECT), - * DELETE, REPLACE, REPLACE(SELECT), CREATE|RENAME|DROP USER, - * GRANT, REVOKE, OPTIMIZE, CREATE|ALTER|DROP FUNCTION|PROCEDURE, - * CREATE SPFUNCTION, INSTALL|UNINSTALL PLUGIN - */ - if (is_log_table_write_query(lex->sql_command) || - is_update_query(lex->sql_command)) - { -#if defined(NOT_IN_USE) - - if (thd->variables.sql_log_bin == 0 && - force_data_modify_op_replication) - { - /** Not replicated */ - type |= QUERY_TYPE_SESSION_WRITE; - } - else -#endif /* NOT_IN_USE */ - { - /** Written to binlog, that is, replicated except tmp tables */ - type |= QUERY_TYPE_WRITE; /*< to master */ - - if (lex->sql_command == SQLCOM_CREATE_TABLE && - (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) - { - type |= QUERY_TYPE_CREATE_TMP_TABLE; /*< remember in router */ - } - } - - goto return_qtype; - } - - /** Try to catch session modifications here */ - switch (lex->sql_command) - { - /** fallthrough */ - case SQLCOM_CHANGE_DB: - case SQLCOM_DEALLOCATE_PREPARE: - type |= QUERY_TYPE_SESSION_WRITE; - break; - - case SQLCOM_SELECT: - case SQLCOM_SHOW_SLAVE_STAT: - type |= QUERY_TYPE_READ; - break; - - case SQLCOM_CALL: - type |= QUERY_TYPE_WRITE; - break; - - case SQLCOM_BEGIN: - type |= QUERY_TYPE_BEGIN_TRX; - goto return_qtype; - break; - - case SQLCOM_COMMIT: - type |= QUERY_TYPE_COMMIT; - goto return_qtype; - break; - - case SQLCOM_ROLLBACK: - type |= QUERY_TYPE_ROLLBACK; - goto return_qtype; - break; - - case SQLCOM_PREPARE: - type |= QUERY_TYPE_PREPARE_NAMED_STMT; - goto return_qtype; - break; - - case SQLCOM_SHOW_DATABASES: - type |= QUERY_TYPE_SHOW_DATABASES; - goto return_qtype; - break; - - case SQLCOM_SHOW_TABLES: - type |= QUERY_TYPE_SHOW_TABLES; - goto return_qtype; - break; - - default: - break; - } - -#if defined(UPDATE_VAR_SUPPORT) - - if (QTYPE_LESS_RESTRICTIVE_THAN_WRITE(type)) -#endif - if (QUERY_IS_TYPE(qtype, QUERY_TYPE_UNKNOWN) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_LOCAL_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_READ)) - { - /** - * These values won't change qtype more restrictive than write. - * UDFs and procedures could possibly cause session-wide write, - * but unless their content is replicated this is a limitation - * of this implementation. - * In other words : UDFs and procedures are not allowed to - * perform writes which are not replicated but need to repeat - * in every node. - * It is not sure if such statements exist. vraa 25.10.13 - */ - - /** - * Search for system functions, UDFs and stored procedures. - */ - for (item = thd->free_list; item != NULL; item = item->next) - { - Item::Type itype; - - itype = item->type(); - MXS_DEBUG("%lu [resolve_query_type] Item %s:%s", - pthread_self(), item->name, STRITEMTYPE(itype)); - - if (itype == Item::SUBSELECT_ITEM) - { - continue; - } - else if (itype == Item::FUNC_ITEM) - { - int func_qtype = QUERY_TYPE_UNKNOWN; - /** - * Item types: - * FIELD_ITEM = 0, FUNC_ITEM, - * SUM_FUNC_ITEM, STRING_ITEM, INT_ITEM, - * REAL_ITEM, NULL_ITEM, VARBIN_ITEM, - * COPY_STR_ITEM, FIELD_AVG_ITEM, - * DEFAULT_VALUE_ITEM, PROC_ITEM, - * COND_ITEM, REF_ITEM, FIELD_STD_ITEM, - * FIELD_VARIANCE_ITEM, - * INSERT_VALUE_ITEM, - * SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, - * TYPE_HOLDER, PARAM_ITEM, - * TRIGGER_FIELD_ITEM, DECIMAL_ITEM, - * XPATH_NODESET, XPATH_NODESET_CMP, - * VIEW_FIXER_ITEM, - * EXPR_CACHE_ITEM == 27 - **/ - - Item_func::Functype ftype; - ftype = ((Item_func*) item)->functype(); - - /** - * Item_func types: - * - * UNKNOWN_FUNC = 0,EQ_FUNC, EQUAL_FUNC, - * NE_FUNC, LT_FUNC, LE_FUNC, - * GE_FUNC, GT_FUNC, FT_FUNC, - * LIKE_FUNC == 10, ISNULL_FUNC, ISNOTNULL_FUNC, - * COND_AND_FUNC, COND_OR_FUNC, XOR_FUNC, - * BETWEEN, IN_FUNC, - * MULT_EQUAL_FUNC, INTERVAL_FUNC, - * ISNOTNULLTEST_FUNC == 20, - * SP_EQUALS_FUNC, SP_DISJOINT_FUNC, - * SP_INTERSECTS_FUNC, - * SP_TOUCHES_FUNC, SP_CROSSES_FUNC, - * SP_WITHIN_FUNC, SP_CONTAINS_FUNC, - * SP_OVERLAPS_FUNC, - * SP_STARTPOINT, SP_ENDPOINT == 30, - * SP_EXTERIORRING, SP_POINTN, SP_GEOMETRYN, - * SP_INTERIORRINGN,NOT_FUNC, NOT_ALL_FUNC, - * NOW_FUNC, TRIG_COND_FUNC, - * SUSERVAR_FUNC, GUSERVAR_FUNC == 40, - * COLLATE_FUNC, EXTRACT_FUNC, - * CHAR_TYPECAST_FUNC, - * FUNC_SP, UDF_FUNC, NEG_FUNC, - * GSYSVAR_FUNC == 47 - **/ - switch (ftype) - { - case Item_func::FUNC_SP: - /** - * An unknown (for maxscale) function / sp - * belongs to this category. - */ - func_qtype |= QUERY_TYPE_WRITE; - MXS_DEBUG("%lu [resolve_query_type] " - "functype FUNC_SP, stored proc " - "or unknown function.", - pthread_self()); - break; - - case Item_func::UDF_FUNC: - func_qtype |= QUERY_TYPE_WRITE; - MXS_DEBUG("%lu [resolve_query_type] " - "functype UDF_FUNC, user-defined " - "function.", - pthread_self()); - break; - - case Item_func::NOW_FUNC: - func_qtype |= QUERY_TYPE_LOCAL_READ; - MXS_DEBUG("%lu [resolve_query_type] " - "functype NOW_FUNC, could be " - "executed in MaxScale.", - pthread_self()); - break; - - /** System session variable */ - case Item_func::GSYSVAR_FUNC: - func_qtype |= QUERY_TYPE_SYSVAR_READ; - MXS_DEBUG("%lu [resolve_query_type] " - "functype GSYSVAR_FUNC, system " - "variable read.", - pthread_self()); - break; - - /** User-defined variable read */ - case Item_func::GUSERVAR_FUNC: - func_qtype |= QUERY_TYPE_USERVAR_READ; - MXS_DEBUG("%lu [resolve_query_type] " - "functype GUSERVAR_FUNC, user " - "variable read.", - pthread_self()); - break; - - /** User-defined variable modification */ - case Item_func::SUSERVAR_FUNC: - /** - * Really it is user variable but we - * don't separate sql variables atm. - * 15.9.14 - */ - func_qtype |= QUERY_TYPE_GSYSVAR_WRITE; - MXS_DEBUG("%lu [resolve_query_type] " - "functype SUSERVAR_FUNC, user " - "variable write.", - pthread_self()); - break; - - case Item_func::UNKNOWN_FUNC: - - if (((Item_func*) item)->func_name() != NULL && - strcmp((char*) ((Item_func*) item)->func_name(), "last_insert_id") == 0) - { - func_qtype |= QUERY_TYPE_MASTER_READ; - } - else - { - func_qtype |= QUERY_TYPE_READ; - } - - /** - * Many built-in functions are of this - * type, for example, rand(), soundex(), - * repeat() . - */ - MXS_DEBUG("%lu [resolve_query_type] " - "functype UNKNOWN_FUNC, " - "typically some system function.", - pthread_self()); - break; - - default: - MXS_DEBUG("%lu [resolve_query_type] " - "Functype %d.", - pthread_self(), - ftype); - break; - } /**< switch */ - - /**< Set new query type */ - type |= func_qtype; - } - -#if defined(UPDATE_VAR_SUPPORT) - - /** - * Write is as restrictive as it gets due functions, - * so break. - */ - if ((type & QUERY_TYPE_WRITE) == QUERY_TYPE_WRITE) - { - break; - } - -#endif - } /**< for */ - } /**< if */ - -return_qtype: - qtype = (qc_query_type_t) type; - return qtype; -} - -/** - * Checks if statement causes implicit COMMIT. - * autocommit_stmt gets values 1, 0 or -1 if stmt is enable, disable or - * something else than autocommit. - * - * @param lex Parse tree - * @param autocommit_stmt memory address for autocommit status - * - * @return true if statement causes implicit commit and false otherwise - */ -static bool skygw_stmt_causes_implicit_commit(LEX* lex, int* autocommit_stmt) -{ - bool succp; - - if (!(sql_command_flags[lex->sql_command] & CF_AUTO_COMMIT_TRANS)) - { - succp = false; - goto return_succp; - } - - switch (lex->sql_command) - { - case SQLCOM_DROP_TABLE: - succp = !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); - break; - - case SQLCOM_ALTER_TABLE: - case SQLCOM_CREATE_TABLE: - /* If CREATE TABLE of non-temporary table, do implicit commit */ - succp = !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); - break; - - case SQLCOM_SET_OPTION: - if ((*autocommit_stmt = is_autocommit_stmt(lex)) == 1) - { - succp = true; - } - else - { - succp = false; - } - - break; - - default: - succp = true; - break; - } - -return_succp: - return succp; -} - -/** - * Finds out if stmt is SET autocommit - * and if the new value matches with the enable_cmd argument. - * - * @param lex parse tree - * - * @return 1, 0, or -1 if command was: - * enable, disable, or not autocommit, respectively. - */ -static int is_autocommit_stmt(LEX* lex) -{ - struct list_node* node; - set_var* setvar; - int rc = -1; - static char target[8]; /*< for converted string */ - Item* item = NULL; - - node = lex->var_list.first_node(); - setvar = (set_var*) node->info; - - if (setvar == NULL) - { - goto return_rc; - } - - do /*< Search for the last occurrence of 'autocommit' */ - { - if ((sys_var*) setvar->var == Sys_autocommit_ptr) - { - item = setvar->value; - } - - node = node->next; - } - while ((setvar = (set_var*) node->info) != NULL); - - if (item != NULL) /*< found autocommit command */ - { - if (item->type() == Item::INT_ITEM) /*< '0' or '1' */ - { - rc = item->val_int(); - - if (rc > 1 || rc < 0) - { - rc = -1; - } - } - else if (item->type() == Item::STRING_ITEM) /*< 'on' or 'off' */ - { - String str(target, sizeof (target), system_charset_info); - String* res = item->val_str(&str); - - if ((rc = find_type(&bool_typelib, res->ptr(), res->length(), false))) - { - ss_dassert(rc >= 0 && rc <= 2); - /** - * rc is the position of matchin string in - * typelib's value array. - * 1=OFF, 2=ON. - */ - rc -= 1; - } - } - } - -return_rc: - return rc; -} - -#if defined(NOT_USED) - -char* qc_get_stmtname(GWBUF* buf) -{ - MYSQL* mysql; - - if (buf == NULL || - buf->gwbuf_bufobj == NULL || - buf->gwbuf_bufobj->bo_data == NULL || - (mysql = (MYSQL *) ((parsing_info_t *) buf->gwbuf_bufobj->bo_data)->pi_handle) == NULL || - mysql->thd == NULL || - (THD *) (mysql->thd))->lex == NULL || - (THD *) (mysql->thd))->lex->prepared_stmt_name == NULL) - { - return NULL; - } - - return ((THD *) (mysql->thd))->lex->prepared_stmt_name.str; -} +#if defined(QC_TRACE_ENABLED) +#define QC_TRACE() MXS_NOTICE(__func__) +#else +#define QC_TRACE() #endif -/** - * Get the parse tree from parsed querybuf. - * @param querybuf The parsed GWBUF - * - * @return Pointer to the LEX struct or NULL if an error occurred or the query - * was not parsed - */ -LEX* get_lex(GWBUF* querybuf) -{ +static const char default_qc_name[] = "qc_mysqlembedded"; - parsing_info_t* pi; - MYSQL* mysql; - THD* thd; - - if (querybuf == NULL || !GWBUF_IS_PARSED(querybuf)) - { - return NULL; - } - - pi = (parsing_info_t *) gwbuf_get_buffer_object_data(querybuf, GWBUF_PARSING_INFO); - - if (pi == NULL) - { - return NULL; - } - - if ((mysql = (MYSQL *) pi->pi_handle) == NULL || - (thd = (THD *) mysql->thd) == NULL) - { - ss_dassert(mysql != NULL && thd != NULL); - return NULL; - } - - return thd->lex; -} - -/** - * Finds the head of the list of tables affected by the current select statement. - * @param thd Pointer to a valid THD - * @return Pointer to the head of the TABLE_LIST chain or NULL in case of an error - */ -static void* skygw_get_affected_tables(void* lexptr) -{ - LEX* lex = (LEX*) lexptr; - - if (lex == NULL || lex->current_select == NULL) - { - ss_dassert(lex != NULL && lex->current_select != NULL); - return NULL; - } - - return (void*) lex->current_select->table_list.first; -} - -/** - * Reads the parsetree and lists all the affected tables and views in the query. - * In the case of an error, the size of the table is set to zero and no memory - * is allocated. The caller must free the allocated memory. - * - * @param querybuf GWBUF where the table names are extracted from - * @param tblsize Pointer where the number of tables is written - * @return Array of null-terminated strings with the table names - */ -char** qc_get_table_names(GWBUF* querybuf, int* tblsize, bool fullnames) -{ - LEX* lex; - TABLE_LIST* tbl; - int i = 0, currtblsz = 0; - char **tables = NULL, **tmp = NULL; - - if (querybuf == NULL || tblsize == NULL) - { - goto retblock; - } - - if (!ensure_query_is_parsed(querybuf)) - { - goto retblock; - } - - if ((lex = get_lex(querybuf)) == NULL || lex->current_select == NULL) - { - goto retblock; - } - - lex->current_select = lex->all_selects_list; - - while (lex->current_select) - { - tbl = (TABLE_LIST*) skygw_get_affected_tables(lex); - - while (tbl) - { - if (i >= currtblsz) - { - tmp = (char**) malloc(sizeof (char*)*(currtblsz * 2 + 1)); - - if (tmp) - { - if (currtblsz > 0) - { - for (int x = 0; x < currtblsz; x++) - { - tmp[x] = tables[x]; - } - - free(tables); - } - - tables = tmp; - currtblsz = currtblsz * 2 + 1; - } - } - - if (tmp != NULL) - { - char *catnm = NULL; - - if (fullnames) - { - if (tbl->db && - strcmp(tbl->db, "skygw_virtual") != 0) - { - catnm = (char*) calloc(strlen(tbl->db) + - strlen(tbl->table_name) + - 2, - sizeof (char)); - strcpy(catnm, tbl->db); - strcat(catnm, "."); - strcat(catnm, tbl->table_name); - } - } - - if (catnm) - { - tables[i++] = catnm; - } - else - { - tables[i++] = strdup(tbl->table_name); - } - - tbl = tbl->next_local; - } - } /*< while (tbl) */ - - lex->current_select = lex->current_select->next_select_in_list(); - } /*< while(lex->current_select) */ - -retblock: - - if (tblsize) - { - *tblsize = i; - } - - return tables; -} - -/** - * Extract, allocate memory and copy the name of the created table. - * @param querybuf Buffer to use. - * @return A pointer to the name if a table was created, otherwise NULL - */ -char* qc_get_created_table_name(GWBUF* querybuf) -{ - if (querybuf == NULL) - { - return NULL; - } - - if (!ensure_query_is_parsed(querybuf)) - { - return NULL; - } - - LEX* lex = get_lex(querybuf); - - if (lex == NULL) - { - return NULL; - } - - if (lex->create_last_non_select_table && - lex->create_last_non_select_table->table_name) - { - char* name = strdup(lex->create_last_non_select_table->table_name); - return name; - } - else - { - return NULL; - } -} - -/** - * Checks whether the query is a "real" query ie. SELECT,UPDATE,INSERT,DELETE or - * any variation of these. Queries that affect the underlying database are not - * considered as real queries and the queries that target specific row or - * variable data are regarded as the real queries. - * - * @param GWBUF to analyze - * - * @return true if the query is a real query, otherwise false - */ -bool qc_is_real_query(GWBUF* querybuf) -{ - bool succp; - LEX* lex; - - if (querybuf == NULL) - { - succp = false; - goto retblock; - } - - if (!ensure_query_is_parsed(querybuf)) - { - succp = false; - goto retblock; - } - - if ((lex = get_lex(querybuf)) == NULL) - { - succp = false; - goto retblock; - } - - switch (lex->sql_command) - { - case SQLCOM_SELECT: - succp = lex->all_selects_list->table_list.elements > 0; - goto retblock; - break; - - case SQLCOM_UPDATE: - case SQLCOM_INSERT: - case SQLCOM_INSERT_SELECT: - case SQLCOM_DELETE: - case SQLCOM_TRUNCATE: - case SQLCOM_REPLACE: - case SQLCOM_REPLACE_SELECT: - case SQLCOM_PREPARE: - case SQLCOM_EXECUTE: - succp = true; - goto retblock; - break; - - default: - succp = false; - goto retblock; - break; - } - -retblock: - return succp; -} - -/** - * Checks whether the buffer contains a DROP TABLE... query. - * @param querybuf Buffer to inspect - * @return true if it contains the query otherwise false - */ -bool qc_is_drop_table_query(GWBUF* querybuf) -{ - bool answer = false; - - if (querybuf) - { - if (ensure_query_is_parsed(querybuf)) - { - LEX* lex = get_lex(querybuf); - - answer = lex && lex->sql_command == SQLCOM_DROP_TABLE; - } - } - - return answer; -} - -inline void add_str(char** buf, int* buflen, int* bufsize, char* str) -{ - int isize = strlen(str) + 1; - - if (*buf == NULL || isize + *buflen >= *bufsize) - { - *bufsize = (*bufsize) * 2 + isize; - char *tmp = (char*) realloc(*buf, (*bufsize) * sizeof (char)); - - if (tmp == NULL) - { - MXS_ERROR("Error: memory reallocation failed."); - free(*buf); - *buf = NULL; - *bufsize = 0; - } - - *buf = tmp; - } - - if (*buflen > 0) - { - if (*buf) - { - strcat(*buf, " "); - } - } - - if (*buf) - { - strcat(*buf, str); - } - - *buflen += isize; - -} - -/** - * Returns all the fields that the query affects. - * @param buf Buffer to parse - * @return Pointer to newly allocated string or NULL if nothing was found - */ -char* qc_get_affected_fields(GWBUF* buf) -{ - LEX* lex; - int buffsz = 0, bufflen = 0; - char* where = NULL; - Item* item; - Item::Type itype; - - if (!buf) - { - return NULL; - } - - if (!ensure_query_is_parsed(buf)) - { - return NULL; - } - - if ((lex = get_lex(buf)) == NULL) - { - return NULL; - } - - lex->current_select = lex->all_selects_list; - - if ((where = (char*) malloc(sizeof (char)*1)) == NULL) - { - MXS_ERROR("Memory allocation failed."); - return NULL; - } - - *where = '\0'; - - while (lex->current_select) - { - - List_iterator ilist(lex->current_select->item_list); - item = (Item*) ilist.next(); - - for (; item != NULL; item = (Item*) ilist.next()) - { - - itype = item->type(); - - if (item->name && itype == Item::FIELD_ITEM) - { - add_str(&where, &buffsz, &bufflen, item->name); - } - } - - - if (lex->current_select->where) - { - for (item = lex->current_select->where; item != NULL; item = item->next) - { - - itype = item->type(); - - if (item->name && itype == Item::FIELD_ITEM) - { - add_str(&where, &buffsz, &bufflen, item->name); - } - } - } - - if (lex->current_select->having) - { - for (item = lex->current_select->having; item != NULL; item = item->next) - { - - itype = item->type(); - - if (item->name && itype == Item::FIELD_ITEM) - { - add_str(&where, &buffsz, &bufflen, item->name); - } - } - } - - lex->current_select = lex->current_select->next_select_in_list(); - } - - return where; -} - -bool qc_query_has_clause(GWBUF* buf) -{ - bool clause = false; - - if (buf) - { - if (ensure_query_is_parsed(buf)) - { - LEX* lex = get_lex(buf); - - if (lex) - { - SELECT_LEX* current = lex->all_selects_list; - - while (current && !clause) - { - if (current->where || current->having) - { - clause = true; - } - - current = current->next_select_in_list(); - } - } - } - } - - return clause; -} - -/* - * Replace user-provided literals with question marks. - * - * @param querybuf GWBUF with a COM_QUERY statement - * @return A copy of the query in its canonical form or NULL if an error occurred. - */ -char* qc_get_canonical(GWBUF* querybuf) -{ - char *querystr = NULL; - if (GWBUF_LENGTH(querybuf) > MYSQL_COM_QUERY_HEADER_SIZE && GWBUF_IS_SQL(querybuf)) - { - size_t srcsize = GWBUF_LENGTH(querybuf) - MYSQL_COM_QUERY_HEADER_SIZE; - char *src = (char*) malloc(srcsize); - size_t destsize = 0; - char *dest = NULL; - if (src) - { - memcpy(src, (uint8_t*) GWBUF_DATA(querybuf) + MYSQL_COM_QUERY_HEADER_SIZE, - srcsize); - if (replace_quoted((const char**) &src, &srcsize, &dest, &destsize) && - remove_mysql_comments((const char**) &dest, &destsize, &src, &srcsize) && - replace_values((const char**) &src, &srcsize, &dest, &destsize)) - { - querystr = dest; - } - free(src); - } - } - return querystr; -} - -/** - * 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 - */ -static 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); - - if (mysql == NULL) - { - MXS_ERROR("Call to mysql_real_connect failed due %d, %s.", - mysql_errno(mysql), - mysql_error(mysql)); - ss_dassert(mysql != NULL); - 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); - 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 - * - */ -static void parsing_info_done(void* ptr) -{ - parsing_info_t* pi; - THD* thd; - - if (ptr) - { - pi = (parsing_info_t *) ptr; - - if (pi->pi_handle != NULL) - { - MYSQL* mysql = (MYSQL *) pi->pi_handle; - - if (mysql->thd != NULL) - { - thd = (THD*) mysql->thd; - thd->end_statement(); - (*mysql->methods->free_embedded_thd)(mysql); - mysql->thd = NULL; - } - - mysql_close(mysql); - } - - /** 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; -} - -/** - * Generate a string of query type value. - * Caller must free the memory of the resulting string. - * - * @param qtype Query type value, combination of values listed in - * query_classifier.h - * - * @return string representing the query type value - */ -char* qc_get_qtype_str(qc_query_type_t qtype) -{ - int t1 = (int) qtype; - int t2 = 1; - qc_query_type_t t = QUERY_TYPE_UNKNOWN; - char* qtype_str = NULL; - - /** - * Test values (bits) and clear matching bits from t1 one by one until - * t1 is completely cleared. - */ - while (t1 != 0) - { - if (t1 & t2) - { - t = (qc_query_type_t) t2; - - if (qtype_str == NULL) - { - qtype_str = strdup(STRQTYPE(t)); - } - else - { - size_t len = strlen(STRQTYPE(t)); - /** reallocate space for delimiter, new string and termination */ - qtype_str = (char *) realloc(qtype_str, strlen(qtype_str) + 1 + len + 1); - snprintf(qtype_str + strlen(qtype_str), 1 + len + 1, "|%s", STRQTYPE(t)); - } - - /** Remove found value from t1 */ - t1 &= ~t2; - } - - t2 <<= 1; - } - - return qtype_str; -} - -/** - * Returns an array of strings of databases that this query uses. - * If the database isn't defined in the query, it is assumed that this query - * only targets the current database. - * The value of @p size is set to the number of allocated strings. The caller is - * responsible for freeing all the allocated memory. - * @param querybuf GWBUF containing the query - * @param size Size of the resulting array - * @return A new array of strings containing the database names or NULL if no - * databases were found. - */ -char** qc_get_database_names(GWBUF* querybuf, int* size) -{ - LEX* lex; - TABLE_LIST* tbl; - char **databases = NULL, **tmp = NULL; - int currsz = 0, i = 0; - - if (!querybuf) - { - goto retblock; - } - - if (!ensure_query_is_parsed(querybuf)) - { - goto retblock; - } - - if ((lex = get_lex(querybuf)) == NULL) - { - goto retblock; - } - - lex->current_select = lex->all_selects_list; - - while (lex->current_select) - { - tbl = lex->current_select->table_list.first; - - while (tbl) - { - if (strcmp(tbl->db, "skygw_virtual") != 0) - { - if (i >= currsz) - { - tmp = (char**) realloc(databases, - sizeof (char*)*(currsz * 2 + 1)); - - if (tmp == NULL) - { - goto retblock; - } - - databases = tmp; - currsz = currsz * 2 + 1; - } - - databases[i++] = strdup(tbl->db); - } - - tbl = tbl->next_local; - } - - lex->current_select = lex->current_select->next_select_in_list(); - } - -retblock: - *size = i; - return databases; -} - -qc_query_op_t qc_get_operation(GWBUF* querybuf) -{ - qc_query_op_t operation = QUERY_OP_UNDEFINED; - - if (querybuf) - { - if (ensure_query_is_parsed(querybuf)) - { - LEX* lex = get_lex(querybuf); - - if (lex) - { - switch (lex->sql_command) - { - case SQLCOM_SELECT: - operation = QUERY_OP_SELECT; - break; - - case SQLCOM_CREATE_TABLE: - operation = QUERY_OP_CREATE_TABLE; - break; - - case SQLCOM_CREATE_INDEX: - operation = QUERY_OP_CREATE_INDEX; - break; - - case SQLCOM_ALTER_TABLE: - operation = QUERY_OP_ALTER_TABLE; - break; - - case SQLCOM_UPDATE: - operation = QUERY_OP_UPDATE; - break; - - case SQLCOM_INSERT: - operation = QUERY_OP_INSERT; - break; - - case SQLCOM_INSERT_SELECT: - operation = QUERY_OP_INSERT_SELECT; - break; - - case SQLCOM_DELETE: - operation = QUERY_OP_DELETE; - break; - - case SQLCOM_TRUNCATE: - operation = QUERY_OP_TRUNCATE; - break; - - case SQLCOM_DROP_TABLE: - operation = QUERY_OP_DROP_TABLE; - break; - - case SQLCOM_DROP_INDEX: - operation = QUERY_OP_DROP_INDEX; - break; - - case SQLCOM_CHANGE_DB: - operation = QUERY_OP_CHANGE_DB; - break; - - case SQLCOM_LOAD: - operation = QUERY_OP_LOAD; - break; - - default: - operation = QUERY_OP_UNDEFINED; - } - } - } - } - - return operation; -} +static QUERY_CLASSIFIER* classifier; bool qc_init(int argc, char** argv, char** groups) { - int rc = mysql_library_init(argc, argv, groups); + QC_TRACE(); + ss_dassert(!classifier); - if (rc != 0) + bool success = false; + void* module = load_module(default_qc_name, MODULE_QUERY_CLASSIFIER); + + if (module) { - MXS_ERROR("mysql_library_init() failed. Error code: %d", rc); + classifier = (QUERY_CLASSIFIER*) module; + MXS_NOTICE("%s loaded.", default_qc_name); + + success = classifier->qc_init(argc, argv, groups); } else { - MXS_NOTICE("Query classifier initialized."); + MXS_ERROR("Could not load %s.", default_qc_name); } - return rc == 0; + return success; } void qc_end() { - mysql_library_end(); + QC_TRACE(); + ss_dassert(classifier); + + classifier->qc_end(); + classifier = NULL; } bool qc_thread_init() { - bool inited = (mysql_thread_init() == 0); + QC_TRACE(); + ss_dassert(classifier); - if (!inited) - { - MXS_ERROR("mysql_thread_init() failed."); - } - - return inited; + return classifier->qc_thread_init(); } void qc_thread_end() { - mysql_thread_end(); + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_thread_end(); +} + +qc_query_type_t qc_get_type(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_type(query); +} + +qc_query_op_t qc_get_operation(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_operation(query); +} + +char* qc_get_created_table_name(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_created_table_name(query); +} + +bool qc_is_drop_table_query(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_is_drop_table_query(query); +} + +bool qc_is_real_query(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_is_real_query(query); +} + +char** qc_get_table_names(GWBUF* query, int* tblsize, bool fullnames) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_table_names(query, tblsize, fullnames); +} + +char* qc_get_canonical(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_canonical(query); +} + +bool qc_query_has_clause(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_query_has_clause(query); +} + +char* qc_get_qtype_str(qc_query_type_t qtype) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_qtype_str(qtype); +} + +char* qc_get_affected_fields(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_affected_fields(query); +} + +char** qc_get_database_names(GWBUF* query, int* sizep) +{ + QC_TRACE(); + ss_dassert(classifier); + + return classifier->qc_get_database_names(query, sizep); } diff --git a/server/core/gateway.c b/server/core/gateway.c index 35e2ac00b..20f3d9709 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -507,7 +507,15 @@ static void libmysqld_done(void) { if (libmysqld_started) { - mysql_library_end(); + // TODO: qc_end() (if qc_mysqlembedded is used) also calls mysql_library_end(), + // TODO: which refers to the mysql_library_end() in the embedded library. This + // TODO: one would call the mysql_library_end() in the client library. It seems + // TODO: that would work, but for the fact that both de-initialize some lower + // TODO: level library, which in turn does not work. Thus, for the time being + // TODO: this call is not made. + // TODO: Linking MaxScale with Connector-C would likely make this problem + // TODO: go away. + //mysql_library_end(); } } @@ -2012,6 +2020,8 @@ int main(int argc, char **argv) /** Release mysql thread context*/ mysql_thread_end(); + qc_end(); + utils_end(); datadir_cleanup(); MXS_NOTICE("MaxScale shutdown completed."); diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index 8897125d2..1e2035ad2 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -4,7 +4,9 @@ target_link_libraries(binlogrouter maxscale-common) install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR}) add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c) -target_link_libraries(maxbinlogcheck maxscale-common query_classifier) +# maxbinlogcheck refers to my_uuid_init and my_uuin. They are non-public functions and +# should not be used. They are found only from the embedded lib. +target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${EMBEDDED_LIB}) install(TARGETS maxbinlogcheck DESTINATION bin) diff --git a/server/modules/routing/binlog/test/CMakeLists.txt b/server/modules/routing/binlog/test/CMakeLists.txt index ff5392839..ad02de4f9 100644 --- a/server/modules/routing/binlog/test/CMakeLists.txt +++ b/server/modules/routing/binlog/test/CMakeLists.txt @@ -1,5 +1,7 @@ if(BUILD_TESTS) add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c) - target_link_libraries(testbinlogrouter maxscale-common query_classifier) + # testbinlogrouter refers to my_uuid_init and my_uuin. They are non-public functions and + # should not be used. They are found only from the embedded lib. + target_link_libraries(testbinlogrouter maxscale-common query_classifier ${EMBEDDED_LIB}) add_test(TestBinlogRouter ${CMAKE_CURRENT_BINARY_DIR}/testbinlogrouter) endif() From fc6f49fe8a89c993b75d67d28cad2a2a345e3c34 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Fri, 29 Jan 2016 16:50:31 +0200 Subject: [PATCH 04/13] Link against Connector-C. Now, qc_mysqlembedded is linked against MySQL's embedded library, and MaxScale itself against Connector-C. So, in order to build MaxScale, Connector-C must be installed. This has been tested with Connector-C 2.2.1. The build variable MYSQLCLIENT_LIBRARIES is no longer used. --- CMakeLists.txt | 6 ----- cmake/FindMySQLClient.cmake | 22 +++++++++---------- query_classifier/CMakeLists.txt | 2 +- .../qc_mysqlembedded/CMakeLists.txt | 6 +++++ query_classifier/test/CMakeLists.txt | 6 +++++ rabbitmq_consumer/CMakeLists.txt | 4 ++-- server/CMakeLists.txt | 2 ++ server/core/CMakeLists.txt | 2 +- server/core/dbusers.c | 5 +++++ server/core/gateway.c | 10 +-------- server/modules/filter/mqfilter.c | 1 - .../include/mysql_client_server_protocol.h | 11 +++++++++- server/modules/routing/binlog/CMakeLists.txt | 2 +- .../routing/binlog/test/CMakeLists.txt | 2 +- .../routing/schemarouter/test/CMakeLists.txt | 4 ++-- server/modules/routing/test/CMakeLists.txt | 6 ++--- 16 files changed, 52 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b975c62db..51735eda8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,12 +157,6 @@ set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_FLAGS} -DSS_DEBUG -DLOG_ASSERT") set(CMAKE_CXX_FLAGS_RELEASE "") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-ggdb") -subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) -foreach(DIR ${MYSQL_DIR_ALL}) - include_directories(${DIR}) -endforeach() - -include_directories(${MYSQL_DIR}/..) include_directories(utils) include_directories(log_manager) include_directories(query_classifier) diff --git a/cmake/FindMySQLClient.cmake b/cmake/FindMySQLClient.cmake index 5b4f53eff..a08c9692c 100644 --- a/cmake/FindMySQLClient.cmake +++ b/cmake/FindMySQLClient.cmake @@ -2,32 +2,32 @@ # The following variables are set: # MYSQLCLIENT_FOUND - System has MySQL client # MYSQLCLIENT_STATIC_FOUND - System has statically linked MySQL client -# MYSQLCLIENT_LIBRARIES - The MySQL client library -# MYSQLCLIENT_STATIC_LIBRARIES - The static MySQL client library +# MARIADB_CONNECTOR_LIB - The MySQL client library +# MARIADB_CONNECTOR_STATIC_LIB - The static MySQL client library # MYSQLCLIENT_HEADERS - The MySQL client headers -find_library(MYSQLCLIENT_LIBRARIES NAMES mysqlclient PATH_SUFFIXES mysql mariadb) -if(${MYSQLCLIENT_LIBRARIES} MATCHES "NOTFOUND") +find_library(MARIADB_CONNECTOR_LIB NAMES mysqlclient PATH_SUFFIXES mysql mariadb) +if(${MARIADB_CONNECTOR_LIB} MATCHES "NOTFOUND") set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "") message(STATUS "Dynamic MySQL client library not found.") - unset(MYSQLCLIENT_LIBRARIES) + unset(MARIADB_CONNECTOR_LIB) else() set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "") - message(STATUS "Found dynamic MySQL client library: ${MYSQLCLIENT_LIBRARIES}") + message(STATUS "Found dynamic MySQL client library: ${MARIADB_CONNECTOR_LIB}") endif() set(OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") -find_library(MYSQLCLIENT_STATIC_LIBRARIES NAMES mysqlclient PATH_SUFFIXES mysql mariadb) +find_library(MARIADB_CONNECTOR_STATIC_LIB NAMES mysqlclient PATH_SUFFIXES mysql mariadb) set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) -if(${MYSQLCLIENT_STATIC_LIBRARIES} MATCHES "NOTFOUND") +if(${MARIADB_CONNECTOR_STATIC_LIB} MATCHES "NOTFOUND") set(MYSQLCLIENT_STATIC_FOUND FALSE CACHE INTERNAL "") message(STATUS "Static MySQL client library not found.") - unset(MYSQLCLIENT_STATIC_LIBRARIES) + unset(MARIADB_CONNECTOR_STATIC_LIB) else() set(MYSQLCLIENT_STATIC_FOUND TRUE CACHE INTERNAL "") - message(STATUS "Found statc MySQL client library: ${MYSQLCLIENT_STATIC_LIBRARIES}") + message(STATUS "Found statc MySQL client library: ${MARIADB_CONNECTOR_STATIC_LIB}") endif() -find_path(MYSQLCLIENT_HEADERS mysql.h PATH_SUFFIXES mysql mariadb) \ No newline at end of file +find_path(MYSQLCLIENT_HEADERS mysql.h PATH_SUFFIXES mysql mariadb) diff --git a/query_classifier/CMakeLists.txt b/query_classifier/CMakeLists.txt index 4780df0e9..68f4d993c 100644 --- a/query_classifier/CMakeLists.txt +++ b/query_classifier/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(query_classifier SHARED query_classifier.cc) -target_link_libraries(query_classifier ${MYSQL_EMBEDDED_LIB} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) +target_link_libraries(query_classifier maxscale-common) set_target_properties(query_classifier PROPERTIES VERSION "1.0.0") set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/query_classifier.map) set_target_properties(query_classifier PROPERTIES LINK_FLAGS -Wl,-z,defs) diff --git a/query_classifier/qc_mysqlembedded/CMakeLists.txt b/query_classifier/qc_mysqlembedded/CMakeLists.txt index 67066754d..d2fc724f1 100644 --- a/query_classifier/qc_mysqlembedded/CMakeLists.txt +++ b/query_classifier/qc_mysqlembedded/CMakeLists.txt @@ -1,3 +1,9 @@ +subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) +foreach(DIR ${MYSQL_DIR_ALL}) + include_directories(${DIR}) +endforeach() +include_directories(${MYSQL_INCLUDE}) + add_library(qc_mysqlembedded SHARED qc_mysqlembedded.cc) target_link_libraries(qc_mysqlembedded ${MYSQL_EMBEDDED_LIB} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) diff --git a/query_classifier/test/CMakeLists.txt b/query_classifier/test/CMakeLists.txt index 61b5702f1..f1c36e080 100644 --- a/query_classifier/test/CMakeLists.txt +++ b/query_classifier/test/CMakeLists.txt @@ -1,3 +1,9 @@ +subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) +foreach(DIR ${MYSQL_DIR_ALL}) + include_directories(${DIR}) +endforeach() +include_directories(${MYSQL_INCLUDE}) + if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND") message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=") else() diff --git a/rabbitmq_consumer/CMakeLists.txt b/rabbitmq_consumer/CMakeLists.txt index bad2de7b3..b585fd435 100644 --- a/rabbitmq_consumer/CMakeLists.txt +++ b/rabbitmq_consumer/CMakeLists.txt @@ -29,9 +29,9 @@ add_subdirectory(inih) add_executable (consumer ${CMAKE_BINARY_DIR}/consumer.c) if(MYSQLCLIENT_STATIC_FOUND) - target_link_libraries(consumer ${MYSQLCLIENT_STATIC_LIBRARIES} rabbitmq inih ssl crypt crypto dl z m pthread) + target_link_libraries(consumer ${MARIADB_CONNECTOR_STATIC_LIB} rabbitmq inih ssl crypt crypto dl z m pthread) else() - target_link_libraries(consumer ${MYSQLCLIENT_LIBRARIES} rabbitmq inih ssl crypt crypto dl z m pthread) + target_link_libraries(consumer ${MARIADB_CONNECTOR_LIB} rabbitmq inih ssl crypt crypto dl z m pthread) endif() install(TARGETS consumer DESTINATION ${MAXSCALE_BINDIR}) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 6cd174190..f4f9d1aca 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(${MARIADB_CONNECTOR_INCLUDE}) + add_subdirectory(core) add_subdirectory(modules) add_subdirectory(inih) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 4918f7fa2..173a2347a 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(maxscale-common SHARED adminusers.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c maxscale_pcre2.c memlog.c modutil.c monitor.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc ${CMAKE_SOURCE_DIR}/utils/skygw_utils.cc statistics.c) -target_link_libraries(maxscale-common ${MYSQLCLIENT_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++) +target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIB} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++) if(WITH_JEMALLOC) target_link_libraries(maxscale-common ${JEMALLOC_LIBRARIES}) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index c807e9ed4..aa554a79c 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2186,6 +2186,10 @@ MYSQL *gw_mysql_init() { if (gw_mysql_set_timeouts(con) == 0) { + // MYSQL_OPT_USE_REMOTE_CONNECTION must be set if the embedded + // libary is used. With Connector-C (at least 2.2.1) the call + // fails. +#if !defined(LIBMARIADB) if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL) != 0) { MXS_ERROR("Failed to set external connection. " @@ -2193,6 +2197,7 @@ MYSQL *gw_mysql_init() mysql_close(con); con = NULL; } +#endif } else { diff --git a/server/core/gateway.c b/server/core/gateway.c index 20f3d9709..5987735fb 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -507,15 +507,7 @@ static void libmysqld_done(void) { if (libmysqld_started) { - // TODO: qc_end() (if qc_mysqlembedded is used) also calls mysql_library_end(), - // TODO: which refers to the mysql_library_end() in the embedded library. This - // TODO: one would call the mysql_library_end() in the client library. It seems - // TODO: that would work, but for the fact that both de-initialize some lower - // TODO: level library, which in turn does not work. Thus, for the time being - // TODO: this call is not made. - // TODO: Linking MaxScale with Connector-C would likely make this problem - // TODO: go away. - //mysql_library_end(); + mysql_library_end(); } } diff --git a/server/modules/filter/mqfilter.c b/server/modules/filter/mqfilter.c index be9a07a63..002da6dca 100644 --- a/server/modules/filter/mqfilter.c +++ b/server/modules/filter/mqfilter.c @@ -80,7 +80,6 @@ #include #include #include -#include #include MODULE_INFO info = diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index 83acb80e9..b48d321ea 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -64,6 +64,7 @@ #include #include #include +#include #define GW_MYSQL_VERSION "MaxScale " MAXSCALE_VERSION #define GW_MYSQL_LOOP_TIMEOUT 300000000 @@ -224,9 +225,15 @@ typedef enum ), } gw_mysql_capabilities_t; +// mysql.h from Connector-C exposes this enum, while mysql.h from +// MariaDB does not. +// TODO: This should probably be removed as Connector-C will be +// TODO: a pre-requisite for building MaxScale. +#if defined(LIBMARIADB) +typedef enum enum_server_command mysql_server_cmd_t; +#else /** Copy from enum in mariadb-5.5 mysql_com.h */ typedef enum mysql_server_cmd { - MYSQL_COM_UNDEFINED = -1, MYSQL_COM_SLEEP = 0, MYSQL_COM_QUIT, MYSQL_COM_INIT_DB, @@ -259,7 +266,9 @@ typedef enum mysql_server_cmd { MYSQL_COM_DAEMON, MYSQL_COM_END /*< Must be the last */ } mysql_server_cmd_t; +#endif +static const mysql_server_cmd_t MYSQL_COM_UNDEFINED = (mysql_server_cmd_t)-1; /** * List of server commands, and number of response packets are stored here. diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index 1e2035ad2..0eaf5c27b 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -6,7 +6,7 @@ install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR}) add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c) # maxbinlogcheck refers to my_uuid_init and my_uuin. They are non-public functions and # should not be used. They are found only from the embedded lib. -target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${EMBEDDED_LIB}) +target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${MYSQL_EMBEDDED_LIB}) install(TARGETS maxbinlogcheck DESTINATION bin) diff --git a/server/modules/routing/binlog/test/CMakeLists.txt b/server/modules/routing/binlog/test/CMakeLists.txt index ad02de4f9..0f967487d 100644 --- a/server/modules/routing/binlog/test/CMakeLists.txt +++ b/server/modules/routing/binlog/test/CMakeLists.txt @@ -2,6 +2,6 @@ if(BUILD_TESTS) add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c) # testbinlogrouter refers to my_uuid_init and my_uuin. They are non-public functions and # should not be used. They are found only from the embedded lib. - target_link_libraries(testbinlogrouter maxscale-common query_classifier ${EMBEDDED_LIB}) + target_link_libraries(testbinlogrouter maxscale-common query_classifier ${MYSQL_EMBEDDED_LIB}) add_test(TestBinlogRouter ${CMAKE_CURRENT_BINARY_DIR}/testbinlogrouter) endif() diff --git a/server/modules/routing/schemarouter/test/CMakeLists.txt b/server/modules/routing/schemarouter/test/CMakeLists.txt index 7974c87f9..1d1bbcc3d 100644 --- a/server/modules/routing/schemarouter/test/CMakeLists.txt +++ b/server/modules/routing/schemarouter/test/CMakeLists.txt @@ -1,9 +1,9 @@ if(MYSQLCLIENT_FOUND AND BUILD_TESTS) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/test.cmake @ONLY) add_executable(testschemarouter testschemarouter.c) - target_link_libraries(testschemarouter ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread) + target_link_libraries(testschemarouter ${MARIADB_CONNECTOR_LIB} ssl crypto dl z m rt pthread) add_executable(testschemarouter2 testschemarouter2.c) - target_link_libraries(testschemarouter2 ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread) + target_link_libraries(testschemarouter2 ${MARIADB_CONNECTOR_LIB} ssl crypto dl z m rt pthread) add_test(NAME TestSchemaRouter COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/test.cmake) endif() diff --git a/server/modules/routing/test/CMakeLists.txt b/server/modules/routing/test/CMakeLists.txt index 19c51b4a1..fc622c4df 100644 --- a/server/modules/routing/test/CMakeLists.txt +++ b/server/modules/routing/test/CMakeLists.txt @@ -1,6 +1,6 @@ if(MYSQLCLIENT_FOUND) add_executable(testconnect testconnect.c) - message(STATUS "Linking against: ${MYSQLCLIENT_LIBRARIES}") - target_link_libraries(testconnect ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread) + message(STATUS "Linking against: ${MARIADB_CONNECTOR_LIB}") + target_link_libraries(testconnect ${MARIADB_CONNECTOR_LIB} ssl crypto dl z m rt pthread) add_test(NAME ReadConnRouterAuthTest COMMAND $ 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT} 1.10) -endif() \ No newline at end of file +endif() From 21b415eec09ed01278a9da2032cf36236427e86a Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 1 Feb 2016 15:53:34 +0200 Subject: [PATCH 05/13] Remove MySQL arguments from qc_init. There can be any sort of library behind qc_init. Hence the arguments cannot be hardwired to be like the embedded library wants them. Eventually it might make sense to allow passing arguments from maxscale.cnf. --- .../qc_mysqlembedded/qc_mysqlembedded.cc | 118 ++++++++++++++++-- query_classifier/query_classifier.cc | 10 +- query_classifier/query_classifier.h | 16 +-- server/core/gateway.c | 2 +- 4 files changed, 122 insertions(+), 24 deletions(-) diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index c81759d64..2bc2be681 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -1775,29 +1776,126 @@ qc_query_op_t qc_get_operation(GWBUF* querybuf) return operation; } - -bool qc_init(int argc, char** argv, char** groups) +namespace { - int rc = mysql_library_init(argc, argv, groups); - if (rc != 0) +// Do not change the order without making corresponding changes to IDX_... below. +const char* server_options[] = +{ + "MariaDB Corporation MaxScale", + "--no-defaults", + "--datadir=", + "--language=", + "--skip-innodb", + "--default-storage-engine=myisam", + NULL +}; + +const int IDX_DATADIR = 2; +const int IDX_LANGUAGE = 3; +const int N_OPTIONS = (sizeof(server_options) / sizeof(server_options[0])) - 1; + +const char* server_groups[] = { + "embedded", + "server", + "server", + "embedded", + "server", + "server", + NULL +}; + +const int OPTIONS_DATADIR_SIZE = 10 + PATH_MAX; // strlen("--datadir="); +const int OPTIONS_LANGUAGE_SIZE = 11 + PATH_MAX; // strlen("--language="); + +char datadir_arg[OPTIONS_DATADIR_SIZE]; +char language_arg[OPTIONS_LANGUAGE_SIZE]; + + +bool create_datadir(const char* base, char* datadir) +{ + bool created = false; + + if (snprintf(datadir, PATH_MAX, "%s/data%d", base, getpid()) < PATH_MAX) { - MXS_ERROR("mysql_library_init() failed. Error code: %d", rc); + int rc = mkdir(datadir, 0777); + + if ((rc == 0) || (errno == EEXIST)) + { + created = true; + } + else + { + char errbuf[STRERROR_BUFLEN]; + fprintf(stderr, "MaxScale: error: Cannot create data directory '%s': %d %s\n", + datadir, errno, strerror_r(errno, errbuf, sizeof(errbuf))); + } } else { - MXS_NOTICE("Query classifier initialized."); + fprintf(stderr, "MaxScale: error: Too long data directory: %s/data%d.", base, getpid()); } - return rc == 0; + return created; } -void qc_end() +void configure_options(const char* datadir, const char* langdir) +{ + int rv; + + rv = snprintf(datadir_arg, OPTIONS_DATADIR_SIZE, "--datadir=%s", datadir); + ss_dassert(rv < OPTIONS_DATADIR_SIZE); // Ensured by create_datadir(). + server_options[IDX_DATADIR] = datadir_arg; + + rv = sprintf(language_arg, "--language=%s", langdir); + ss_dassert(rv < OPTIONS_LANGUAGE_SIZE); // Ensured by qc_init(). + server_options[IDX_LANGUAGE] = language_arg; +} + +} + +bool qc_init(void) +{ + bool inited = false; + char datadir[PATH_MAX]; + + if (strlen(get_langdir()) >= PATH_MAX) + { + fprintf(stderr, "MaxScale: error: Language path is too long: %s.", get_langdir()); + } + else + { + if (create_datadir(get_datadir(), datadir)) + { + configure_options(datadir, get_langdir()); + + int argc = N_OPTIONS; + char** argv = const_cast(server_options); + char** groups = const_cast(server_groups); + + int rc = mysql_library_init(argc, argv, groups); + + if (rc != 0) + { + MXS_ERROR("mysql_library_init() failed. Error code: %d", rc); + } + else + { + MXS_NOTICE("Query classifier initialized."); + inited = true; + } + } + } + + return inited; +} + +void qc_end(void) { mysql_library_end(); } -bool qc_thread_init() +bool qc_thread_init(void) { bool inited = (mysql_thread_init() == 0); @@ -1809,7 +1907,7 @@ bool qc_thread_init() return inited; } -void qc_thread_end() +void qc_thread_end(void) { mysql_thread_end(); } diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 401c8da1f..cf7224bb8 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -40,7 +40,7 @@ static const char default_qc_name[] = "qc_mysqlembedded"; static QUERY_CLASSIFIER* classifier; -bool qc_init(int argc, char** argv, char** groups) +bool qc_init(void) { QC_TRACE(); ss_dassert(!classifier); @@ -53,7 +53,7 @@ bool qc_init(int argc, char** argv, char** groups) classifier = (QUERY_CLASSIFIER*) module; MXS_NOTICE("%s loaded.", default_qc_name); - success = classifier->qc_init(argc, argv, groups); + success = classifier->qc_init(); } else { @@ -63,7 +63,7 @@ bool qc_init(int argc, char** argv, char** groups) return success; } -void qc_end() +void qc_end(void) { QC_TRACE(); ss_dassert(classifier); @@ -72,7 +72,7 @@ void qc_end() classifier = NULL; } -bool qc_thread_init() +bool qc_thread_init(void) { QC_TRACE(); ss_dassert(classifier); @@ -80,7 +80,7 @@ bool qc_thread_init() return classifier->qc_thread_init(); } -void qc_thread_end() +void qc_thread_end(void) { QC_TRACE(); ss_dassert(classifier); diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index 172b0b255..86d8146c2 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -74,11 +74,11 @@ typedef enum #define QUERY_IS_TYPE(mask,type) ((mask & type) == type) -bool qc_init(int argc, char** argv, char** groups); -void qc_end(); +bool qc_init(void); +void qc_end(void); -bool qc_thread_init(); -void qc_thread_end(); +bool qc_thread_init(void); +void qc_thread_end(void); /** * Create THD and use it for creating parse tree. Examine parse tree and @@ -99,11 +99,11 @@ char** qc_get_database_names(GWBUF* querybuf, int* size); typedef struct query_classifier { - bool (*qc_init)(int argc, char** argv, char** groups); - void (*qc_end)(); + bool (*qc_init)(void); + void (*qc_end)(void); - bool (*qc_thread_init)(); - void (*qc_thread_end)(); + bool (*qc_thread_init)(void); + void (*qc_thread_end)(void); qc_query_type_t (*qc_get_type)(GWBUF* querybuf); qc_query_op_t (*qc_get_operation)(GWBUF* querybuf); diff --git a/server/core/gateway.c b/server/core/gateway.c index 5987735fb..689e1ecc1 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1829,7 +1829,7 @@ int main(int argc, char **argv) } } - if (!qc_init(num_elements, server_options, server_groups)) + if (!qc_init()) { char* logerr = "Failed to initialise query classifier library."; print_log_n_stderr(true, true, logerr, logerr, eno); From 018e2426f91f3533b3ee50d937c572f95c678f91 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 1 Feb 2016 18:19:40 +0200 Subject: [PATCH 06/13] Remove MySQL embedded initilization from gateway.c. Since maxscale-common is linked to the connector-c library and no longer to the MySql embedded library, mysql_library_init does not need any parameters. --- server/core/gateway.c | 76 ++++--------------------------------------- 1 file changed, 7 insertions(+), 69 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 689e1ecc1..a2ecaab0b 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -104,33 +104,6 @@ time_t MaxScaleStarted; extern char *program_invocation_name; extern char *program_invocation_short_name; -/* - * Server options are passed to the mysql_server_init. Each gateway must have a unique - * data directory that is passed to the mysql_server_init, therefore the data directory - * is not fixed here and will be updated elsewhere. - */ -static char* server_options[] = { - "MariaDB Corporation MaxScale", - "--no-defaults", - "--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", - "embedded", - "server", - "server", - NULL -}; - /* The data directory we created for this gateway instance */ static char datadir[PATH_MAX + 1] = ""; static bool datadir_defined = false; /*< If the datadir was already set */ @@ -144,9 +117,9 @@ static int pidfd = PIDFD_CLOSED; static bool do_exit = FALSE; /** - * Flag to indicate whether libmysqld is successfully initialized. + * Flag to indicate whether MySQL is successfully initialized. */ -static bool libmysqld_started = FALSE; +static bool libmysql_initialized = FALSE; /** * If MaxScale is started to run in daemon process the value is true. @@ -505,7 +478,7 @@ void datadir_cleanup() static void libmysqld_done(void) { - if (libmysqld_started) + if (libmysql_initialized) { mysql_library_end(); } @@ -1062,8 +1035,6 @@ int main(int argc, char **argv) int child_status; THREAD* threads = NULL; /*< thread list */ char mysql_home[PATH_MAX+1]; - char datadir_arg[10+PATH_MAX+1]; /*< '--datadir=' + PATH_MAX */ - char language_arg[11+PATH_MAX+1]; /*< '--language=' + PATH_MAX */ char* cnf_file_path = NULL; /*< conf file, to be freed */ char* cnf_file_arg = NULL; /*< conf filename from cmd-line arg */ THREAD log_flush_thr; @@ -1777,19 +1748,6 @@ int main(int argc, char **argv) } } - snprintf(datadir, PATH_MAX, "%s/data%d", get_datadir(), getpid()); - - if (mkdir(datadir, 0777) != 0){ - - if (errno != EEXIST){ - char errbuf[STRERROR_BUFLEN]; - fprintf(stderr, - "Error: Cannot create data directory '%s': %d %s\n", - datadir, errno, strerror_r(errno, errbuf, sizeof(errbuf))); - goto return_main; - } - } - if (!daemon_mode) { fprintf(stderr, @@ -1811,24 +1769,6 @@ int main(int argc, char **argv) MXS_NOTICE("Module directory: %s", get_libdir()); MXS_NOTICE("Service cache: %s", get_cachedir()); - /*< Update the server options */ - for (i = 0; server_options[i]; i++) - { - if (!strcmp(server_options[i], "--datadir=")) - { - snprintf(datadir_arg, 10+PATH_MAX+1, "--datadir=%s", datadir); - server_options[i] = datadir_arg; - } - else if (!strcmp(server_options[i], "--language=")) - { - snprintf(language_arg, - 11+PATH_MAX+1, - "--language=%s", - get_langdir()); - server_options[i] = language_arg; - } - } - if (!qc_init()) { char* logerr = "Failed to initialise query classifier library."; @@ -1837,7 +1777,7 @@ int main(int argc, char **argv) goto return_main; } - if (mysql_library_init(num_elements, server_options, server_groups)) + if (mysql_library_init(0, NULL, NULL)) { if (!daemon_mode) { @@ -1876,15 +1816,13 @@ int main(int argc, char **argv) } MXS_ERROR("mysql_library_init failed. It is a " "mandatory component, required by router services and " - "the MaxScale core. Error %d, %s, %s : %d. Exiting.", + "the MaxScale core. Error %d, %s. Exiting.", mysql_errno(NULL), - mysql_error(NULL), - __FILE__, - __LINE__); + mysql_error(NULL)); rc = MAXSCALE_NOLIBRARY; goto return_main; } - libmysqld_started = TRUE; + libmysql_initialized = TRUE; if (!config_load(cnf_file_path)) { From 72f02164fc473ab5146db4c333052831de43e6d3 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 1 Feb 2016 19:18:09 +0200 Subject: [PATCH 07/13] Reformat maxconfig.h --- server/include/maxconfig.h | 196 +++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 95 deletions(-) diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index 5061538f2..0e56798dc 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -27,132 +27,138 @@ * @verbatim * Revision History * - * Date Who Description - * 21/06/13 Mark Riddoch Initial implementation - * 07/05/14 Massimiliano Pinto Added version_string to global configuration - * 23/05/14 Massimiliano Pinto Added id to global configuration - * 17/10/14 Mark Riddoch Added poll tuning configuration parameters - * 05/03/15 Massimiliano Pinto Added sysname, release, sha1_mac to gateway struct + * Date Who Description + * 21/06/13 Mark Riddoch Initial implementation + * 07/05/14 Massimiliano Pinto Added version_string to global configuration + * 23/05/14 Massimiliano Pinto Added id to global configuration + * 17/10/14 Mark Riddoch Added poll tuning configuration parameters + * 05/03/15 Massimiliano Pinto Added sysname, release, sha1_mac to gateway struct * * @endverbatim */ -#define DEFAULT_NBPOLLS 3 /**< Default number of non block polls before we block */ -#define DEFAULT_POLLSLEEP 1000 /**< Default poll wait time (milliseconds) */ -#define _SYSNAME_STR_LENGTH 256 /**< sysname len */ -#define _RELEASE_STR_LENGTH 256 /**< release len */ -#define DEFAULT_NTHREADS 1 /**< Default number of polling threads */ +#define DEFAULT_NBPOLLS 3 /**< Default number of non block polls before we block */ +#define DEFAULT_POLLSLEEP 1000 /**< Default poll wait time (milliseconds) */ +#define _SYSNAME_STR_LENGTH 256 /**< sysname len */ +#define _RELEASE_STR_LENGTH 256 /**< release len */ +#define DEFAULT_NTHREADS 1 /**< Default number of polling threads */ /** * Maximum length for configuration parameter value. */ -enum {MAX_PARAM_LEN=256}; +enum +{ + MAX_PARAM_LEN = 256 +}; -typedef enum { - UNDEFINED_TYPE = 0x00, - STRING_TYPE = 0x01, - COUNT_TYPE = 0x02, - PERCENT_TYPE = 0x04, - BOOL_TYPE = 0x08, - SQLVAR_TARGET_TYPE = 0x10 +typedef enum +{ + UNDEFINED_TYPE = 0x00, + STRING_TYPE = 0x01, + COUNT_TYPE = 0x02, + PERCENT_TYPE = 0x04, + BOOL_TYPE = 0x08, + SQLVAR_TARGET_TYPE = 0x10 } config_param_type_t; -typedef enum { - TYPE_UNDEFINED = 0, - TYPE_MASTER, - TYPE_ALL +typedef enum +{ + TYPE_UNDEFINED = 0, + TYPE_MASTER, + TYPE_ALL } target_t; -enum {MAX_RLAG_NOT_AVAILABLE=-1, MAX_RLAG_UNDEFINED=-2}; +enum +{ + MAX_RLAG_NOT_AVAILABLE = -1, + MAX_RLAG_UNDEFINED = -2 +}; #define PARAM_IS_TYPE(p,t) ((p) & (t)) /** * The config parameter */ -typedef struct config_parameter { - char *name; /**< The name of the parameter */ - char *value; /**< The value of the parameter */ - union { /*< qualified parameter value by type */ - char* valstr; /*< terminated char* array */ - int valcount; /*< int */ - int valpercent; /*< int */ - bool valbool; /*< bool */ - target_t valtarget; /*< sql variable route target */ - } qfd; - config_param_type_t qfd_param_type; - struct config_parameter *next; /**< Next pointer in the linked list */ +typedef struct config_parameter +{ + char *name; /**< The name of the parameter */ + char *value; /**< The value of the parameter */ + union + { /*< qualified parameter value by type */ + char* valstr; /*< terminated char* array */ + int valcount; /*< int */ + int valpercent; /*< int */ + bool valbool; /*< bool */ + target_t valtarget; /*< sql variable route target */ + } qfd; + config_param_type_t qfd_param_type; + struct config_parameter *next; /**< Next pointer in the linked list */ } CONFIG_PARAMETER; /** * The config context structure, used to build the configuration * data during the parse process */ -typedef struct config_context { - char *object; /**< The name of the object being configured */ - CONFIG_PARAMETER *parameters; /**< The list of parameter values */ - void *element; /**< The element created from the data */ - struct config_context *next; /**< Next pointer in the linked list */ +typedef struct config_context +{ + char *object; /**< The name of the object being configured */ + CONFIG_PARAMETER *parameters; /**< The list of parameter values */ + void *element; /**< The element created from the data */ + struct config_context *next; /**< Next pointer in the linked list */ } CONFIG_CONTEXT; /** * The gateway global configuration data */ -typedef struct { - int n_threads; /**< Number of polling threads */ - char *version_string; /**< The version string of embedded database library */ - char release_string[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */ - char sysname[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */ - uint8_t mac_sha1[SHA_DIGEST_LENGTH]; /*< The SHA1 digest of an interface MAC address */ - unsigned long id; /**< MaxScale ID */ - unsigned int n_nbpoll; /**< Tune number of non-blocking polls */ - unsigned int pollsleep; /**< Wait time in blocking polls */ - int syslog; /*< Log to syslog */ - int maxlog; /*< Log to MaxScale's own logs */ - int log_to_shm; /*< Write log-file to shared memory */ - unsigned int auth_conn_timeout; /*< Connection timeout for the user authentication */ - unsigned int auth_read_timeout; /*< Read timeout for the user authentication */ - unsigned int auth_write_timeout; /*< Write timeout for the user authentication */ +typedef struct +{ + int n_threads; /**< Number of polling threads */ + char *version_string; /**< The version string of embedded db library */ + char release_string[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */ + char sysname[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */ + uint8_t mac_sha1[SHA_DIGEST_LENGTH]; /**< The SHA1 digest of an interface MAC address */ + unsigned long id; /**< MaxScale ID */ + unsigned int n_nbpoll; /**< Tune number of non-blocking polls */ + unsigned int pollsleep; /**< Wait time in blocking polls */ + int syslog; /**< Log to syslog */ + int maxlog; /**< Log to MaxScale's own logs */ + int log_to_shm; /**< Write log-file to shared memory */ + unsigned int auth_conn_timeout; /**< Connection timeout for the user authentication */ + unsigned int auth_read_timeout; /**< Read timeout for the user authentication */ + unsigned int auth_write_timeout; /**< Write timeout for the user authentication */ } GATEWAY_CONF; -extern int config_load(char *); -extern int config_reload(); -extern int config_threadcount(); -extern unsigned int config_nbpolls(); -extern unsigned int config_pollsleep(); -CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name); -config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param); -CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param); -void free_config_parameter(CONFIG_PARAMETER* p1); -extern int config_truth_value(char *); -extern double config_percentage_value(char *str); -bool config_set_qualified_param( - CONFIG_PARAMETER* param, - void* val, - config_param_type_t type); +char* config_clean_string_list(char* str); +CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param); +void config_enable_feedback_task(void); +void config_disable_feedback_task(void); +unsigned long config_get_gateway_id(void); +GATEWAY_CONF* config_get_global_options(); +CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name); +config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param); +bool config_get_valint(int* val, + CONFIG_PARAMETER* param, + const char* name, /*< if NULL examine current param only */ + config_param_type_t ptype); +bool config_get_valbool(bool* val, + CONFIG_PARAMETER* param, + const char* name, /*< if NULL examine current param only */ + config_param_type_t ptype); +bool config_get_valtarget(target_t* val, + CONFIG_PARAMETER* param, + const char* name, /*< if NULL examine current param only */ + config_param_type_t ptype); +int config_load(char *); +unsigned int config_nbpolls(); +double config_percentage_value(char *str); +unsigned int config_pollsleep(); +int config_reload(); +bool config_set_qualified_param(CONFIG_PARAMETER* param, + void* val, + config_param_type_t type); +int config_threadcount(); +int config_truth_value(char *); +void free_config_parameter(CONFIG_PARAMETER* p1); +bool is_internal_service(const char *router); -bool config_get_valint( - int* val, - CONFIG_PARAMETER* param, - const char* name, /*< if NULL examine current param only */ - config_param_type_t ptype); - -bool config_get_valbool( - bool* val, - CONFIG_PARAMETER* param, - const char* name, /*< if NULL examine current param only */ - config_param_type_t ptype); - -bool config_get_valtarget( - target_t* val, - CONFIG_PARAMETER* param, - const char* name, /*< if NULL examine current param only */ - config_param_type_t ptype); - -void config_enable_feedback_task(void); -void config_disable_feedback_task(void); -unsigned long config_get_gateway_id(void); -GATEWAY_CONF* config_get_global_options(); -bool is_internal_service(const char *router); -char* config_clean_string_list(char* str); #endif From bbfcbc18c630fcb12242951685ff560916521009 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 1 Feb 2016 19:46:16 +0200 Subject: [PATCH 08/13] Make it possible to specify query classifier name. If none provided or if the name is empty, the default query classifier is loaded. --- query_classifier/query_classifier.cc | 13 +++++++++---- query_classifier/query_classifier.h | 2 +- server/core/gateway.c | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index cf7224bb8..c7e689028 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -40,24 +40,29 @@ static const char default_qc_name[] = "qc_mysqlembedded"; static QUERY_CLASSIFIER* classifier; -bool qc_init(void) +bool qc_init(const char* plugin_name) { QC_TRACE(); ss_dassert(!classifier); + if (!plugin_name || (*plugin_name == 0)) + { + plugin_name = default_qc_name; + } + bool success = false; - void* module = load_module(default_qc_name, MODULE_QUERY_CLASSIFIER); + void* module = load_module(plugin_name, MODULE_QUERY_CLASSIFIER); if (module) { classifier = (QUERY_CLASSIFIER*) module; - MXS_NOTICE("%s loaded.", default_qc_name); + MXS_NOTICE("%s loaded.", plugin_name); success = classifier->qc_init(); } else { - MXS_ERROR("Could not load %s.", default_qc_name); + MXS_ERROR("Could not load %s.", plugin_name); } return success; diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index 86d8146c2..cb8fd8aa4 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -74,7 +74,7 @@ typedef enum #define QUERY_IS_TYPE(mask,type) ((mask & type) == type) -bool qc_init(void); +bool qc_init(const char* plugin_name); void qc_end(void); bool qc_thread_init(void); diff --git a/server/core/gateway.c b/server/core/gateway.c index a2ecaab0b..6adde6022 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1769,7 +1769,7 @@ int main(int argc, char **argv) MXS_NOTICE("Module directory: %s", get_libdir()); MXS_NOTICE("Service cache: %s", get_cachedir()); - if (!qc_init()) + if (!qc_init(NULL)) { char* logerr = "Failed to initialise query classifier library."; print_log_n_stderr(true, true, logerr, logerr, eno); From e668abec5bca88fcaaeb03e51766dbfb5fbd23ef Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 1 Feb 2016 20:38:51 +0200 Subject: [PATCH 09/13] Load config before qc is initialized. That way a query_classifier entry in maxscale.cnf will be available before the initialization takes place. --- server/core/gateway.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 6adde6022..387ca5c27 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1769,6 +1769,19 @@ int main(int argc, char **argv) MXS_NOTICE("Module directory: %s", get_libdir()); MXS_NOTICE("Service cache: %s", get_cachedir()); + if (!config_load(cnf_file_path)) + { + char* fprerr = + "Failed to open, read or process the MaxScale configuration " + "file. Exiting. See the error log for details."; + print_log_n_stderr(false, !daemon_mode, fprerr, fprerr, 0); + MXS_ERROR("Failed to open, read or process the MaxScale configuration file %s. " + "Exiting.", + cnf_file_path); + rc = MAXSCALE_BADCONFIG; + goto return_main; + } + if (!qc_init(NULL)) { char* logerr = "Failed to initialise query classifier library."; @@ -1824,18 +1837,6 @@ int main(int argc, char **argv) } libmysql_initialized = TRUE; - if (!config_load(cnf_file_path)) - { - char* fprerr = - "Failed to open, read or process the MaxScale configuration " - "file. Exiting. See the error log for details."; - print_log_n_stderr(false, !daemon_mode, fprerr, fprerr, 0); - MXS_ERROR("Failed to open, read or process the MaxScale configuration file %s. " - "Exiting.", - cnf_file_path); - rc = MAXSCALE_BADCONFIG; - goto return_main; - } MXS_NOTICE("MariaDB Corporation MaxScale %s (C) MariaDB Corporation Ab 2013-2015", MAXSCALE_VERSION); MXS_NOTICE("MaxScale is running in process %i", getpid()); From 2bbe1b068ae7b9298ac872e214702ae0cf55121c Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 1 Feb 2016 20:44:36 +0200 Subject: [PATCH 10/13] Allow the query classifier to be specified in maxscale.cnf --- query_classifier/query_classifier.cc | 1 + server/core/config.c | 19 +++++++++++++++++++ server/core/gateway.c | 5 ++++- server/include/maxconfig.h | 3 ++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index c7e689028..e41470328 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -47,6 +47,7 @@ bool qc_init(const char* plugin_name) if (!plugin_name || (*plugin_name == 0)) { + MXS_NOTICE("No query classifier specified, using default '%s'.", default_qc_name); plugin_name = default_qc_name; } diff --git a/server/core/config.c b/server/core/config.c index 5bbcc69c6..d1c123476 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1002,6 +1002,22 @@ handle_global_item(const char *name, const char *value) MXS_ERROR("Invalid timeout value for 'auth_write_timeout': %s", value); } } + else if (strcmp(name, "query_classifier") == 0) + { + int len = strlen(value); + int max_len = sizeof(gateway.qc_name) - 1; + + if (len <= max_len) + { + strcpy(gateway.qc_name, value); + } + else + { + MXS_ERROR("The length of '%s' is %d, while the maximum length is %d.", + value, len, max_len); + return 0; + } + } else { for (i = 0; lognames[i].name; i++) @@ -1110,6 +1126,9 @@ global_defaults() { strncpy(gateway.sysname, uname_data.sysname, _SYSNAME_STR_LENGTH); } + + /* query_classifier */ + memset(gateway.qc_name, 0, sizeof(gateway.qc_name)); } /** diff --git a/server/core/gateway.c b/server/core/gateway.c index 387ca5c27..06367ee5a 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1782,7 +1782,10 @@ int main(int argc, char **argv) goto return_main; } - if (!qc_init(NULL)) + GATEWAY_CONF* cnf = config_get_global_options(); + ss_dassert(cnf); + + if (!qc_init(cnf->qc_name)) { char* logerr = "Failed to initialise query classifier library."; print_log_n_stderr(true, true, logerr, logerr, eno); diff --git a/server/include/maxconfig.h b/server/include/maxconfig.h index 0e56798dc..6de4f3a2c 100644 --- a/server/include/maxconfig.h +++ b/server/include/maxconfig.h @@ -124,7 +124,8 @@ typedef struct int log_to_shm; /**< Write log-file to shared memory */ unsigned int auth_conn_timeout; /**< Connection timeout for the user authentication */ unsigned int auth_read_timeout; /**< Read timeout for the user authentication */ - unsigned int auth_write_timeout; /**< Write timeout for the user authentication */ + unsigned int auth_write_timeout; /**< Write timeout for the user authentication */ + char qc_name[PATH_MAX]; /**< The name of the query classifier to load */ } GATEWAY_CONF; From 081bacdc64c2f243824f2cf2d7095c10d5cab3be Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 1 Feb 2016 11:52:47 +0200 Subject: [PATCH 11/13] Switched to MySQL client library headers for MaxScale The include directories previously used by MaxScale were from the embedded library. All parts of MaxScale apart from the query classifier now use the client libraries. --- CMakeLists.txt | 2 +- ...lient.cmake => FindMariaDBConnector.cmake} | 22 +-- cmake/FindMySQL.cmake | 138 ++++++++++++------ cmake/macros.cmake | 62 ++------ .../qc_mysqlembedded/CMakeLists.txt | 10 +- query_classifier/test/CMakeLists.txt | 7 +- server/CMakeLists.txt | 3 +- server/core/CMakeLists.txt | 1 + server/modules/routing/binlog/CMakeLists.txt | 2 +- .../routing/binlog/test/CMakeLists.txt | 2 +- 10 files changed, 124 insertions(+), 125 deletions(-) rename cmake/{FindMySQLClient.cmake => FindMariaDBConnector.cmake} (54%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51735eda8..0c276ea6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ check_deps() check_dirs() find_package(OpenSSL) find_package(Valgrind) -find_package(MySQLClient) +find_package(MariaDBConnector) find_package(MySQL) find_package(Pandoc) find_package(TCMalloc) diff --git a/cmake/FindMySQLClient.cmake b/cmake/FindMariaDBConnector.cmake similarity index 54% rename from cmake/FindMySQLClient.cmake rename to cmake/FindMariaDBConnector.cmake index a08c9692c..b425f8976 100644 --- a/cmake/FindMySQLClient.cmake +++ b/cmake/FindMariaDBConnector.cmake @@ -1,18 +1,18 @@ -# This CMake file tries to find the the MySQL client library +# This CMake file tries to find the the MariaDB Connector-C # The following variables are set: -# MYSQLCLIENT_FOUND - System has MySQL client -# MYSQLCLIENT_STATIC_FOUND - System has statically linked MySQL client -# MARIADB_CONNECTOR_LIB - The MySQL client library -# MARIADB_CONNECTOR_STATIC_LIB - The static MySQL client library -# MYSQLCLIENT_HEADERS - The MySQL client headers +# MARIADB_CONNECTOR_FOUND - System has the connector +# MARIADB_CONNECTOR_STATIC_FOUND - System has static version of the connector library +# MARIADB_CONNECTOR_LIBRARIES - The dynamic connector libraries +# MARIADB_CONNECTOR_STATIC_LIBRARIES - The static connector libraries +# MARIADB_CONNECTOR_INCLUDE_DIR - The connector headers find_library(MARIADB_CONNECTOR_LIB NAMES mysqlclient PATH_SUFFIXES mysql mariadb) if(${MARIADB_CONNECTOR_LIB} MATCHES "NOTFOUND") - set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "") + set(MARIADB_CONNECTOR_FOUND FALSE CACHE INTERNAL "") message(STATUS "Dynamic MySQL client library not found.") unset(MARIADB_CONNECTOR_LIB) else() - set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "") + set(MARIADB_CONNECTOR_FOUND TRUE CACHE INTERNAL "") message(STATUS "Found dynamic MySQL client library: ${MARIADB_CONNECTOR_LIB}") endif() @@ -22,12 +22,12 @@ find_library(MARIADB_CONNECTOR_STATIC_LIB NAMES mysqlclient PATH_SUFFIXES mysql set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) if(${MARIADB_CONNECTOR_STATIC_LIB} MATCHES "NOTFOUND") - set(MYSQLCLIENT_STATIC_FOUND FALSE CACHE INTERNAL "") + set(MARIADB_CONNECTOR_STATIC_FOUND FALSE CACHE INTERNAL "") message(STATUS "Static MySQL client library not found.") unset(MARIADB_CONNECTOR_STATIC_LIB) else() - set(MYSQLCLIENT_STATIC_FOUND TRUE CACHE INTERNAL "") + set(MARIADB_CONNECTOR_STATIC_FOUND TRUE CACHE INTERNAL "") message(STATUS "Found statc MySQL client library: ${MARIADB_CONNECTOR_STATIC_LIB}") endif() -find_path(MYSQLCLIENT_HEADERS mysql.h PATH_SUFFIXES mysql mariadb) +find_path(MARIADB_CONNECTOR_INCLUDE_DIR mysql.h PATH_SUFFIXES mysql mariadb) diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake index a3cbfe8ec..2eb83155c 100644 --- a/cmake/FindMySQL.cmake +++ b/cmake/FindMySQL.cmake @@ -1,42 +1,43 @@ # This CMake file tries to find the the mysql_version.h header # and to parse it for version and provider strings # The following variables are set: -# MYSQL_VERSION - The MySQL version number -# MYSQL_PROVIDER - The MySQL provider e.g. MariaDB -# MYSQL_EMBEDDED_LIB - The MySQL embedded library +# MYSQL_EMBEDDED_VERSION - The MySQL version number +# MYSQL_EMBEDDED_PROVIDER - The MySQL provider e.g. MariaDB +# MYSQL_EMBEDDED_LIBRARIES - The MySQL embedded library +# MYSQL_EMBEDDED_INCLUDE_DIR - Path to MySQL headers -find_file(MYSQL_VERSION_H mysql_version.h PATH_SUFFIXES mysql) -if(MYSQL_VERSION_H MATCHES "MYSQL_VERSION_H-NOTFOUND") +find_file(MYSQL_EMBEDDED_VERSION_H mysql_version.h PATH_SUFFIXES mysql) +if(MYSQL_EMBEDDED_VERSION_H MATCHES "MYSQL_EMBEDDED_VERSION_H-NOTFOUND") message(FATAL_ERROR "Cannot find the mysql_version.h header") else() - message(STATUS "Found mysql_version.h: ${MYSQL_VERSION_H}") + message(STATUS "Found mysql_version.h: ${MYSQL_EMBEDDED_VERSION_H}") endif() -file(READ ${MYSQL_VERSION_H} MYSQL_VERSION_CONTENTS) -string(REGEX REPLACE ".*MYSQL_SERVER_VERSION[^0-9.]+([0-9.]+).*" "\\1" MYSQL_VERSION ${MYSQL_VERSION_CONTENTS}) -string(REGEX REPLACE ".*MYSQL_COMPILATION_COMMENT[[:space:]]+\"(.+)\".*" "\\1" MYSQL_PROVIDER ${MYSQL_VERSION_CONTENTS}) -string(TOLOWER ${MYSQL_PROVIDER} MYSQL_PROVIDER) -if(MYSQL_PROVIDER MATCHES "[mM]aria[dD][bB]") - set(MYSQL_PROVIDER "MariaDB" CACHE INTERNAL "The MySQL provider") -elseif(MYSQL_PROVIDER MATCHES "mysql") - set(MYSQL_PROVIDER "MySQL" CACHE INTERNAL "The MySQL provider") -elseif(MYSQL_PROVIDER MATCHES "percona") - set(MYSQL_PROVIDER "Percona" CACHE INTERNAL "The MySQL provider") +file(READ ${MYSQL_EMBEDDED_VERSION_H} MYSQL_EMBEDDED_VERSION_CONTENTS) +string(REGEX REPLACE ".*MYSQL_SERVER_VERSION[^0-9.]+([0-9.]+).*" "\\1" MYSQL_EMBEDDED_VERSION ${MYSQL_EMBEDDED_VERSION_CONTENTS}) +string(REGEX REPLACE ".*MYSQL_COMPILATION_COMMENT[[:space:]]+\"(.+)\".*" "\\1" MYSQL_EMBEDDED_PROVIDER ${MYSQL_EMBEDDED_VERSION_CONTENTS}) +string(TOLOWER ${MYSQL_EMBEDDED_PROVIDER} MYSQL_EMBEDDED_PROVIDER) +if(MYSQL_EMBEDDED_PROVIDER MATCHES "[mM]aria[dD][bB]") + set(MYSQL_EMBEDDED_PROVIDER "MariaDB" CACHE INTERNAL "The MySQL provider") +elseif(MYSQL_EMBEDDED_PROVIDER MATCHES "mysql") + set(MYSQL_EMBEDDED_PROVIDER "MySQL" CACHE INTERNAL "The MySQL provider") +elseif(MYSQL_EMBEDDED_PROVIDER MATCHES "percona") + set(MYSQL_EMBEDDED_PROVIDER "Percona" CACHE INTERNAL "The MySQL provider") else() - set(MYSQL_PROVIDER "Unknown" CACHE INTERNAL "The MySQL provider") + set(MYSQL_EMBEDDED_PROVIDER "Unknown" CACHE INTERNAL "The MySQL provider") endif() -message(STATUS "MySQL version: ${MYSQL_VERSION}") -message(STATUS "MySQL provider: ${MYSQL_PROVIDER}") +message(STATUS "MySQL version: ${MYSQL_EMBEDDED_VERSION}") +message(STATUS "MySQL provider: ${MYSQL_EMBEDDED_PROVIDER}") -if(NOT MYSQL_PROVIDER STREQUAL "MariaDB") -message(WARNING "Not using a release version of MariaDB server. If this is intentional, please ignore this warning. Otherwise make sure the right libraries are installed and CMake finds the right libraries.") +if(NOT MYSQL_EMBEDDED_PROVIDER STREQUAL "MariaDB") + message(WARNING "Not using a release version of MariaDB server. If this is intentional, please ignore this warning. Otherwise make sure the right libraries are installed and CMake finds the right libraries.") endif() -if(MYSQL_VERSION VERSION_LESS 5.5.41) -message(WARNING "MySQL version is ${MYSQL_VERSION}. Minimum supported version is 5.5.41.") +if(MYSQL_EMBEDDED_VERSION VERSION_LESS 5.5.41) + message(WARNING "MySQL version is ${MYSQL_EMBEDDED_VERSION}. Minimum supported version is 5.5.41.") endif() -if(NOT (MYSQL_VERSION VERSION_LESS 10.1)) +if(NOT (MYSQL_EMBEDDED_VERSION VERSION_LESS 10.1)) # 10.1 needs lzma find_library(HAVE_LIBLZMA NAMES lzma) @@ -47,18 +48,18 @@ if(NOT (MYSQL_VERSION VERSION_LESS 10.1)) endif() -if (DEFINED MYSQL_EMBEDDED_LIB) - if( NOT (IS_DIRECTORY ${MYSQL_EMBEDDED_LIB}) ) - debugmsg("MYSQL_EMBEDDED_LIB is not a directory: ${MYSQL_EMBEDDED_LIB}") +if (DEFINED MYSQL_EMBEDDED_LIBRARIES) + if( NOT (IS_DIRECTORY ${MYSQL_EMBEDDED_LIBRARIES}) ) + debugmsg("MYSQL_EMBEDDED_LIBRARIES is not a directory: ${MYSQL_EMBEDDED_LIBRARIES}") if(${CMAKE_VERSION} VERSION_LESS 2.8.12 ) set(COMP_VAR PATH) else() set(COMP_VAR DIRECTORY) endif() - get_filename_component(MYSQL_EMBEDDED_LIB ${MYSQL_EMBEDDED_LIB} ${COMP_VAR}) - debugmsg("MYSQL_EMBEDDED_LIB directory component: ${MYSQL_EMBEDDED_LIB}") + get_filename_component(MYSQL_EMBEDDED_LIBRARIES ${MYSQL_EMBEDDED_LIBRARIES} ${COMP_VAR}) + debugmsg("MYSQL_EMBEDDED_LIBRARIES directory component: ${MYSQL_EMBEDDED_LIBRARIES}") endif() - debugmsg("Searching for the embedded library at: ${MYSQL_EMBEDDED_LIB}") + debugmsg("Searching for the embedded library at: ${MYSQL_EMBEDDED_LIBRARIES}") endif() if(STATIC_EMBEDDED) @@ -66,38 +67,38 @@ if(STATIC_EMBEDDED) debugmsg("Using the static embedded library...") set(OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - if (DEFINED MYSQL_EMBEDDED_LIB) - debugmsg("Searching for libmysqld.a at: ${MYSQL_EMBEDDED_LIB}") - find_library(MYSQL_EMBEDDED_LIB_STATIC libmysqld.a PATHS ${MYSQL_EMBEDDED_LIB} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) + if (DEFINED MYSQL_EMBEDDED_LIBRARIES) + debugmsg("Searching for libmysqld.a at: ${MYSQL_EMBEDDED_LIBRARIES}") + find_library(MYSQL_EMBEDDED_LIBRARIES_STATIC libmysqld.a PATHS ${MYSQL_EMBEDDED_LIBRARIES} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) else() - find_library(MYSQL_EMBEDDED_LIB_STATIC libmysqld.a PATH_SUFFIXES mysql mariadb) + find_library(MYSQL_EMBEDDED_LIBRARIES_STATIC libmysqld.a PATH_SUFFIXES mysql mariadb) endif() - debugmsg("Search returned: ${MYSQL_EMBEDDED_LIB_STATIC}") + debugmsg("Search returned: ${MYSQL_EMBEDDED_LIBRARIES_STATIC}") - set(MYSQL_EMBEDDED_LIB ${MYSQL_EMBEDDED_LIB_STATIC} CACHE FILEPATH "Path to libmysqld" FORCE) + set(MYSQL_EMBEDDED_LIBRARIES ${MYSQL_EMBEDDED_LIBRARIES_STATIC} CACHE FILEPATH "Path to libmysqld" FORCE) set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) else() debugmsg("Using the dynamic embedded library...") set(OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ".so") - if (DEFINED MYSQL_EMBEDDED_LIB) - debugmsg("Searching for libmysqld.so at: ${MYSQL_EMBEDDED_LIB}") - find_library(MYSQL_EMBEDDED_LIB_DYNAMIC mysqld PATHS ${MYSQL_EMBEDDED_LIB} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) + if (DEFINED MYSQL_EMBEDDED_LIBRARIES) + debugmsg("Searching for libmysqld.so at: ${MYSQL_EMBEDDED_LIBRARIES}") + find_library(MYSQL_EMBEDDED_LIBRARIES_DYNAMIC mysqld PATHS ${MYSQL_EMBEDDED_LIBRARIES} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) else() - find_library(MYSQL_EMBEDDED_LIB_DYNAMIC mysqld PATH_SUFFIXES mysql mariadb) + find_library(MYSQL_EMBEDDED_LIBRARIES_DYNAMIC mysqld PATH_SUFFIXES mysql mariadb) endif() - debugmsg("Search returned: ${MYSQL_EMBEDDED_LIB_DYNAMIC}") - set(MYSQL_EMBEDDED_LIB ${MYSQL_EMBEDDED_LIB_DYNAMIC} CACHE FILEPATH "Path to libmysqld" FORCE) + debugmsg("Search returned: ${MYSQL_EMBEDDED_LIBRARIES_DYNAMIC}") + set(MYSQL_EMBEDDED_LIBRARIES ${MYSQL_EMBEDDED_LIBRARIES_DYNAMIC} CACHE FILEPATH "Path to libmysqld" FORCE) set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) endif() -unset(MYSQL_EMBEDDED_LIB_DYNAMIC) -unset(MYSQL_EMBEDDED_LIB_STATIC) +unset(MYSQL_EMBEDDED_LIBRARIES_DYNAMIC) +unset(MYSQL_EMBEDDED_LIBRARIES_STATIC) unset(OLD_SUFFIXES) -check_library_exists(${MYSQL_EMBEDDED_LIB} pcre_stack_guard ${MYSQL_EMBEDDED_LIB} HAVE_EMBEDDED_PCRE) +check_library_exists(${MYSQL_EMBEDDED_LIBRARIES} pcre_stack_guard ${MYSQL_EMBEDDED_LIBRARIES} HAVE_EMBEDDED_PCRE) if(HAVE_EMBEDDED_PCRE) set(PCRE_LINK_FLAGS "" CACHE INTERNAL "pcre linker flags") @@ -110,8 +111,49 @@ else() message(FATAL_ERROR "Could not find PCRE libraries.") endif() endif() -if( (${MYSQL_EMBEDDED_LIB} MATCHES "NOTFOUND") OR (${MYSQL_EMBEDDED_LIB} MATCHES "NOTFOUND")) - message(FATAL_ERROR "Library not found: libmysqld. If your install of MySQL is in a non-default location, please provide the location with -DMYSQL_EMBEDDED_LIB=") +if( (${MYSQL_EMBEDDED_LIBRARIES} MATCHES "NOTFOUND") OR (${MYSQL_EMBEDDED_LIBRARIES} MATCHES "NOTFOUND")) + message(FATAL_ERROR "Library not found: libmysqld. If your install of MySQL is in a non-default location, please provide the location with -DMYSQL_EMBEDDED_LIBRARIES=") else() - message(STATUS "Using embedded library: ${MYSQL_EMBEDDED_LIB}") + message(STATUS "Using embedded library: ${MYSQL_EMBEDDED_LIBRARIES}") endif() + +# Find the MySQL headers if they were not defined +if(DEFINED MYSQL_EMBEDDED_INCLUDE_DIR) + list(APPEND CMAKE_INCLUDE_PATH ${MYSQL_EMBEDDED_INCLUDE_DIR}) + find_path(MYSQL_EMBEDDED_INCLUDE_DIR_LOC mysql.h PATHS ${MYSQL_EMBEDDED_INCLUDE_DIR} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) +else() + find_path(MYSQL_EMBEDDED_INCLUDE_DIR_LOC mysql.h PATH_SUFFIXES mysql mariadb) +endif() + +if(${MYSQL_EMBEDDED_INCLUDE_DIR_LOC} MATCHES "NOTFOUND") + message(FATAL_ERROR "Fatal Error: MySQL headers were not found.") +else() + set(MYSQL_EMBEDDED_INCLUDE_DIR ${MYSQL_EMBEDDED_INCLUDE_DIR_LOC} CACHE PATH "Path to MySQL headers" FORCE) + message(STATUS "Using MySQL headers found at: ${MYSQL_EMBEDDED_INCLUDE_DIR}") +endif() + +unset(MYSQL_EMBEDDED_INCLUDE_DIR_LOC) + +# Find the errmsg.sys file if it was not defied +if( DEFINED ERRMSG ) + debugmsg("Looking for errmsg.sys at: ${ERRMSG}") + if(NOT(IS_DIRECTORY ${ERRMSG})) + get_filename_component(ERRMSG ${ERRMSG} PATH) + debugmsg("Path to file is: ${ERRMSG}") + endif() + find_file(ERRMSG_FILE errmsg.sys PATHS ${ERRMSG} NO_DEFAULT_PATH) + if(${ERRMSG_FILE} MATCHES "NOTFOUND") + message(FATAL_ERROR "Fatal Error: The errmsg.sys file was not found at ${ERRMSG}") + else() + message(STATUS "Using custom errmsg.sys found at: ${ERRMSG_FILE}") + endif() +else() + find_file(ERRMSG_FILE errmsg.sys PATHS /usr/share /usr/share/mysql /usr/local/share/mysql PATH_SUFFIXES english mysql/english) + if(${ERRMSG_FILE} MATCHES "NOTFOUND") + message(FATAL_ERROR "Fatal Error: The errmsg.sys file was not found, please define the path to it by using -DERRMSG=") + else() + message(STATUS "Using errmsg.sys found at: ${ERRMSG_FILE}") + endif() +endif() +set(ERRMSG ${ERRMSG_FILE} CACHE FILEPATH "Path to the errmsg.sys file." FORCE) +unset(ERRMSG_FILE) diff --git a/cmake/macros.cmake b/cmake/macros.cmake index 3999cbe72..b49747f5d 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -144,60 +144,15 @@ macro(check_deps) endmacro() macro(check_dirs) - # Find the MySQL headers if they were not defined - - if(DEFINED MYSQL_DIR) - debugmsg("Searching for MySQL headers at: ${MYSQL_DIR}") - list(APPEND CMAKE_INCLUDE_PATH ${MYSQL_DIR}) - find_path(MYSQL_DIR_LOC mysql.h PATHS ${MYSQL_DIR} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH) - else() - find_path(MYSQL_DIR_LOC mysql.h PATH_SUFFIXES mysql mariadb) - endif() - -debugmsg("Search returned: ${MYSQL_DIR_LOC}") - - if(${MYSQL_DIR_LOC} MATCHES "NOTFOUND") - message(FATAL_ERROR "Fatal Error: MySQL headers were not found.") - else() - set(MYSQL_DIR ${MYSQL_DIR_LOC} CACHE PATH "Path to MySQL headers" FORCE) - message(STATUS "Using MySQL headers found at: ${MYSQL_DIR}") - endif() - - unset(MYSQL_DIR_LOC) - - # Find the errmsg.sys file if it was not defied - if( DEFINED ERRMSG ) - debugmsg("Looking for errmsg.sys at: ${ERRMSG}") - if(NOT(IS_DIRECTORY ${ERRMSG})) - get_filename_component(ERRMSG ${ERRMSG} PATH) - debugmsg("Path to file is: ${ERRMSG}") - endif() - find_file(ERRMSG_FILE errmsg.sys PATHS ${ERRMSG} NO_DEFAULT_PATH) - if(${ERRMSG_FILE} MATCHES "NOTFOUND") - message(FATAL_ERROR "Fatal Error: The errmsg.sys file was not found at ${ERRMSG}") - else() - message(STATUS "Using custom errmsg.sys found at: ${ERRMSG_FILE}") - endif() - else() - find_file(ERRMSG_FILE errmsg.sys PATHS /usr/share /usr/share/mysql /usr/local/share/mysql PATH_SUFFIXES english mysql/english) - if(${ERRMSG_FILE} MATCHES "NOTFOUND") - message(FATAL_ERROR "Fatal Error: The errmsg.sys file was not found, please define the path to it by using -DERRMSG=") - else() - message(STATUS "Using errmsg.sys found at: ${ERRMSG_FILE}") - endif() - endif() - set(ERRMSG ${ERRMSG_FILE} CACHE FILEPATH "Path to the errmsg.sys file." FORCE) - unset(ERRMSG_FILE) - # Check which init.d script to install if(WITH_SCRIPTS) find_file(RPM_FNC functions PATHS /etc/rc.d/init.d) if(${RPM_FNC} MATCHES "RPM_FNC-NOTFOUND") find_file(DEB_FNC init-functions PATHS /lib/lsb) if(${DEB_FNC} MATCHES "DEB_FNC-NOTFOUND") - message(FATAL_ERROR "Cannot find required init-functions in /lib/lsb/ or /etc/rc.d/init.d/, please confirm that your system files are OK.") + message(FATAL_ERROR "Cannot find required init-functions in /lib/lsb/ or /etc/rc.d/init.d/, please confirm that your system files are OK.") else() - set(DEB_BASED TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") + set(DEB_BASED TRUE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") endif() else() set(DEB_BASED FALSE CACHE BOOL "If init.d script uses /lib/lsb/init-functions instead of /etc/rc.d/init.d/functions.") @@ -215,16 +170,17 @@ endmacro() function(subdirs VAR DIRPATH) -if(${CMAKE_VERSION} VERSION_LESS 2.8.12 ) -set(COMP_VAR PATH) -else() -set(COMP_VAR DIRECTORY) -endif() + if(${CMAKE_VERSION} VERSION_LESS 2.8.12 ) + set(COMP_VAR PATH) + else() + set(COMP_VAR DIRECTORY) + endif() file(GLOB_RECURSE SDIR ${DIRPATH}/*) foreach(LOOP ${SDIR}) get_filename_component(LOOP ${LOOP} ${COMP_VAR}) list(APPEND ALLDIRS ${LOOP}) endforeach() list(REMOVE_DUPLICATES ALLDIRS) - set(${VAR} "${ALLDIRS}" CACHE PATH " " FORCE) + set(${VAR} "${ALLDIRS}" CACHE PATH " " FORCE) + endfunction() diff --git a/query_classifier/qc_mysqlembedded/CMakeLists.txt b/query_classifier/qc_mysqlembedded/CMakeLists.txt index d2fc724f1..202f99c30 100644 --- a/query_classifier/qc_mysqlembedded/CMakeLists.txt +++ b/query_classifier/qc_mysqlembedded/CMakeLists.txt @@ -1,12 +1,14 @@ -subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) -foreach(DIR ${MYSQL_DIR_ALL}) +# Include the embedded library headers +subdirs(MYSQL_INCLUDE_DIR_ALL ${MYSQL_EMBEDDED_INCLUDE_DIR}) +foreach(DIR ${MYSQL_INCLUDE_DIR_ALL}) include_directories(${DIR}) endforeach() -include_directories(${MYSQL_INCLUDE}) +include_directories(${MYSQL_EMMBEDDED_INCLUDE_DIR}/..) add_library(qc_mysqlembedded SHARED qc_mysqlembedded.cc) -target_link_libraries(qc_mysqlembedded ${MYSQL_EMBEDDED_LIB} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) +include_directories(${MYSQL_INCLUDE_DIR}) +target_link_libraries(qc_mysqlembedded ${MYSQL_EMBEDDED_LIBRARIES} aio crypt crypto dl m ${PCRE_LINK_FLAGS} ssl stdc++ z) set_target_properties(qc_mysqlembedded PROPERTIES VERSION "1.0.0") set_target_properties(qc_mysqlembedded PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/qc_mysqlembedded.map) #set_target_properties(qc_mysqlembedded PROPERTIES LINK_FLAGS -Wl,-z,defs) diff --git a/query_classifier/test/CMakeLists.txt b/query_classifier/test/CMakeLists.txt index f1c36e080..59ec4d7a8 100644 --- a/query_classifier/test/CMakeLists.txt +++ b/query_classifier/test/CMakeLists.txt @@ -1,8 +1,5 @@ -subdirs(MYSQL_DIR_ALL ${MYSQL_DIR}) -foreach(DIR ${MYSQL_DIR_ALL}) - include_directories(${DIR}) -endforeach() -include_directories(${MYSQL_INCLUDE}) +# Use the client libraries in the tests +include_directories(${MYSQLCLIENT_INCLUDE_DIR}) if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND") message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=") diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index f4f9d1aca..69361c9d6 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,4 +1,5 @@ -include_directories(${MARIADB_CONNECTOR_INCLUDE}) +# Use the client libraries in MaxScale core +include_directories(${MARIADB_CONNECTOR_INCLUDE_DIR}) add_subdirectory(core) add_subdirectory(modules) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 173a2347a..7eae4fdeb 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -10,6 +10,7 @@ endif() add_dependencies(maxscale-common pcre2) install(TARGETS maxscale-common DESTINATION ${MAXSCALE_LIBDIR}) +set_target_properties(maxscale-common PROPERTIES VERSION "1.0.0") add_executable(maxscale gateway.c) add_dependencies(maxscale pcre2) diff --git a/server/modules/routing/binlog/CMakeLists.txt b/server/modules/routing/binlog/CMakeLists.txt index 0eaf5c27b..6eace29d0 100644 --- a/server/modules/routing/binlog/CMakeLists.txt +++ b/server/modules/routing/binlog/CMakeLists.txt @@ -6,7 +6,7 @@ install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR}) add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c) # maxbinlogcheck refers to my_uuid_init and my_uuin. They are non-public functions and # should not be used. They are found only from the embedded lib. -target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${MYSQL_EMBEDDED_LIB}) +target_link_libraries(maxbinlogcheck maxscale-common query_classifier ${MYSQL_EMBEDDED_LIBRARIES}) install(TARGETS maxbinlogcheck DESTINATION bin) diff --git a/server/modules/routing/binlog/test/CMakeLists.txt b/server/modules/routing/binlog/test/CMakeLists.txt index 0f967487d..418b8f6b9 100644 --- a/server/modules/routing/binlog/test/CMakeLists.txt +++ b/server/modules/routing/binlog/test/CMakeLists.txt @@ -2,6 +2,6 @@ if(BUILD_TESTS) add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c) # testbinlogrouter refers to my_uuid_init and my_uuin. They are non-public functions and # should not be used. They are found only from the embedded lib. - target_link_libraries(testbinlogrouter maxscale-common query_classifier ${MYSQL_EMBEDDED_LIB}) + target_link_libraries(testbinlogrouter maxscale-common query_classifier ${MYSQL_EMBEDDED_LIBRARIES}) add_test(TestBinlogRouter ${CMAKE_CURRENT_BINARY_DIR}/testbinlogrouter) endif() From 2f65c3a7b818e565ba7ace040832180a913f6873 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 3 Feb 2016 11:35:01 +0200 Subject: [PATCH 12/13] MariaDB Connector C is build from source if it is not found on the system The FindMariaDBConnector.cmake file now checks if the found library actually is the MariaDB Connector-C library. If the found library is not the MariaDB Connector-C, it will be built from source. --- CMakeLists.txt | 6 ++++++ cmake/BuildMariaDBConnector.cmake | 26 ++++++++++++++++++++++++++ cmake/FindMariaDBConnector.cmake | 25 +++++++++++++++++-------- query_classifier/test/CMakeLists.txt | 8 ++++++-- server/core/CMakeLists.txt | 2 +- 5 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 cmake/BuildMariaDBConnector.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c276ea6e..b81ba7f09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,12 @@ find_package(RabbitMQ) # Read BuildPCRE2 for details about how to add pcre2 as a dependency to a target include(cmake/BuildPCRE2.cmake) +# If the connector was not found, download and build it from source +if(NOT MARIADB_CONNECTOR_FOUND) + message(STATUS "Building MariaDB Connector-C from source.") + include(cmake/BuildMariaDBConnector.cmake) +endif() + # You can find the variables set by this in the FindCURL.cmake file # which is a default module in CMake. diff --git a/cmake/BuildMariaDBConnector.cmake b/cmake/BuildMariaDBConnector.cmake new file mode 100644 index 000000000..ed316c7ec --- /dev/null +++ b/cmake/BuildMariaDBConnector.cmake @@ -0,0 +1,26 @@ +# Build the MariaDB Connector-C +# +# If the MariaDB connector is not found, the last option is to download it +# and build it from source. This file downloads and builds the connector and +# sets the variables set by FindMariaDBConnector.cmake so that it appears that +# the system has the connector. + +include(ExternalProject) + +ExternalProject_Add(connector-c + GIT_REPOSITORY https://github.com/MariaDB/mariadb-connector-c.git + GIT_TAG v2.2.1 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/connector-c/install + BINARY_DIR ${CMAKE_BINARY_DIR}/connector-c + INSTALL_DIR ${CMAKE_BINARY_DIR}/connector-c/install) + +set(MARIADB_CONNECTOR_FOUND TRUE CACHE INTERNAL "" FORCE) +set(MARIADB_CONNECTOR_STATIC_FOUND TRUE CACHE INTERNAL "" FORCE) +set(MARIADB_CONNECTOR_INCLUDE_DIR + ${CMAKE_BINARY_DIR}/connector-c/install/include/mariadb CACHE INTERNAL "" FORCE) +set(MARIADB_CONNECTOR_STATIC_LIBRARIES + ${CMAKE_BINARY_DIR}/connector-c/install/lib/mariadb/libmariadbclient.a + CACHE INTERNAL "" FORCE) +set(MARIADB_CONNECTOR_LIBRARIES + ${CMAKE_BINARY_DIR}/connector-c/install/lib/mariadb/libmariadbclient.a + CACHE INTERNAL "" FORCE) diff --git a/cmake/FindMariaDBConnector.cmake b/cmake/FindMariaDBConnector.cmake index b425f8976..787bd90fa 100644 --- a/cmake/FindMariaDBConnector.cmake +++ b/cmake/FindMariaDBConnector.cmake @@ -6,28 +6,37 @@ # MARIADB_CONNECTOR_STATIC_LIBRARIES - The static connector libraries # MARIADB_CONNECTOR_INCLUDE_DIR - The connector headers -find_library(MARIADB_CONNECTOR_LIB NAMES mysqlclient PATH_SUFFIXES mysql mariadb) -if(${MARIADB_CONNECTOR_LIB} MATCHES "NOTFOUND") +find_library(MARIADB_CONNECTOR_LIBRARIES NAMES mysqlclient PATH_SUFFIXES mysql mariadb) +if(${MARIADB_CONNECTOR_LIBRARIES} MATCHES "NOTFOUND") set(MARIADB_CONNECTOR_FOUND FALSE CACHE INTERNAL "") message(STATUS "Dynamic MySQL client library not found.") - unset(MARIADB_CONNECTOR_LIB) + unset(MARIADB_CONNECTOR_LIBRARIES) else() set(MARIADB_CONNECTOR_FOUND TRUE CACHE INTERNAL "") - message(STATUS "Found dynamic MySQL client library: ${MARIADB_CONNECTOR_LIB}") + message(STATUS "Found dynamic MySQL client library: ${MARIADB_CONNECTOR_LIBRARIES}") endif() set(OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") -find_library(MARIADB_CONNECTOR_STATIC_LIB NAMES mysqlclient PATH_SUFFIXES mysql mariadb) +find_library(MARIADB_CONNECTOR_STATIC_LIBRARIES NAMES mysqlclient PATH_SUFFIXES mysql mariadb) set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) -if(${MARIADB_CONNECTOR_STATIC_LIB} MATCHES "NOTFOUND") +if(${MARIADB_CONNECTOR_STATIC_LIBRARIES} MATCHES "NOTFOUND") set(MARIADB_CONNECTOR_STATIC_FOUND FALSE CACHE INTERNAL "") message(STATUS "Static MySQL client library not found.") - unset(MARIADB_CONNECTOR_STATIC_LIB) + unset(MARIADB_CONNECTOR_STATIC_LIBRARIES) else() set(MARIADB_CONNECTOR_STATIC_FOUND TRUE CACHE INTERNAL "") - message(STATUS "Found statc MySQL client library: ${MARIADB_CONNECTOR_STATIC_LIB}") + message(STATUS "Found statc MySQL client library: ${MARIADB_CONNECTOR_STATIC_LIBRARIES}") endif() find_path(MARIADB_CONNECTOR_INCLUDE_DIR mysql.h PATH_SUFFIXES mysql mariadb) + +include(CheckSymbolExists) +check_symbol_exists(LIBMARIADB ${MARIADB_CONNECTOR_INCLUDE_DIR}/mysql.h HAVE_MARIADB_CONNECTOR) + +if(HAVE_MARIADB_CONNECTOR) + message(STATUS "Found MariaDB Connector-C") +else() + set(MARIADB_CONNECTOR_FOUND FALSE CACHE INTERNAL "" FORCE) +endif() diff --git a/query_classifier/test/CMakeLists.txt b/query_classifier/test/CMakeLists.txt index 59ec4d7a8..0741f9fba 100644 --- a/query_classifier/test/CMakeLists.txt +++ b/query_classifier/test/CMakeLists.txt @@ -1,5 +1,9 @@ -# Use the client libraries in the tests -include_directories(${MYSQLCLIENT_INCLUDE_DIR}) +# Include the embedded library headers +subdirs(MYSQL_INCLUDE_DIR_ALL ${MYSQL_EMBEDDED_INCLUDE_DIR}) +foreach(DIR ${MYSQL_INCLUDE_DIR_ALL}) + include_directories(${DIR}) +endforeach() +include_directories(${MYSQL_EMMBEDDED_INCLUDE_DIR}/..) if(${ERRMSG} MATCHES "ERRMSG-NOTFOUND") message(FATAL_ERROR "The errmsg.sys file was not found, please define the path with -DERRMSG=") diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 7eae4fdeb..015ffa22c 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(maxscale-common SHARED adminusers.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c maxscale_pcre2.c memlog.c modutil.c monitor.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc ${CMAKE_SOURCE_DIR}/utils/skygw_utils.cc statistics.c) -target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIB} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++) +target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++) if(WITH_JEMALLOC) target_link_libraries(maxscale-common ${JEMALLOC_LIBRARIES}) From 0cc3db963421d5bd6578781699b14819a2bbb9a3 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 8 Feb 2016 10:55:08 +0200 Subject: [PATCH 13/13] Fix forgotten merge conflict. --- query_classifier/query_classifier.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index fa0af5e12..e41470328 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -133,21 +133,12 @@ bool qc_is_real_query(GWBUF* query) return classifier->qc_is_real_query(query); } -<<<<<<< HEAD char** qc_get_table_names(GWBUF* query, int* tblsize, bool fullnames) { QC_TRACE(); ss_dassert(classifier); -======= - -char** qc_get_table_names(GWBUF* query, int* tblsize, bool fullnames) -{ - QC_TRACE(); - ss_dassert(classifier); - ->>>>>>> origin/MXS-517 return classifier->qc_get_table_names(query, tblsize, fullnames); }