diff --git a/CMakeLists.txt b/CMakeLists.txt index d0e22ae1..10cd1bda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,8 @@ # This is the LGPL libmariadb project. PROJECT(mariadb-connector-c C) -SET(CPACK_PACKAGE_VERSION_MAJOR 3) -SET(CPACK_PACKAGE_VERSION_MINOR 0) +SET(CPACK_PACKAGE_VERSION_MAJOR 2) +SET(CPACK_PACKAGE_VERSION_MINOR 2) SET(CPACK_PACKAGE_VERSION_PATCH 0) SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") @@ -26,6 +26,7 @@ ELSE() OPTION(WITH_SIGNCODE "digitally sign files" OFF) OPTION(WITH_RTC "enables run time checks for debug builds" OFF) ENDIF() +OPTION(WITH_REMOTEIO "enables remote io support (requires libcurl)" OFF) OPTION(WITH_EXTERNAL_ZLIB "Enables use of external zlib" OFF) ############### @@ -204,15 +205,18 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include) IF(WIN32) SET(SYSTEM_LIBS ws2_32 advapi32 kernel32) ELSE() - SET(SYSTEM_LIBS ${LIBPTHREAD} ${LIBDL} ${LIBM} ${LIBICONV}) + SET(SYSTEM_LIBS ${LIBPTHREAD} ${LIBDL} ${LIBM}) + IF(ICONV_EXTERNAL) + SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${ICONV_LIBRARIES}) + ENDIF() ENDIF() IF(WITH_SSL) SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${SSL_LIBRARIES}) ENDIF() ADD_SUBDIRECTORY(include) -ADD_SUBDIRECTORY(plugins) ADD_SUBDIRECTORY(libmariadb) +ADD_SUBDIRECTORY(plugins) IF(NOT WIN32) ADD_SUBDIRECTORY(mariadb_config) ENDIF() @@ -246,35 +250,29 @@ INCLUDE(cmake/ConnectorName.cmake) SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mariadb-connector-c-${CPACK_PACKAGE_VERSION}-src") SET(CPACK_SOURCE_IGNORE_FILES -.bzr/ -.bzrignore -CMakeCache.txt -cmake_dist.cmake -CPackSourceConfig.cmake -CPackConfig.cmake -mariadb_config/mariadb_config.c$ -.build/ +\\\\.git/ +\\\\.gitignore +CMakeCache\\\\.txt +cmake_dist\\\\.cmake +CPackConfig\\\\.cmake +mariadb_config/mariadb_config\\\\.c$ +\\\\.build/ html/ unittest /cmake_install.cmake /CTestTestfile.cmake +/CPackSourceConfig.cmake /CMakeFiles/ /version_resources/ /_CPack_Packages/ -.*gz$ -.*zip$ -.*so$ -.*so.*$ -.*dll$ -.*a$ -.*pdb$ +\\\\.gz$ +\\\\.zip$ +mariadb_config/mariadb_config /CMakeFiles/ /version_resources/ /_CPack_Packages/ Makefile$ -include/my_config.h$ -/autom4te.cache/ -errmsg.sys$ +include/my_config\\\\.h$ ) IF(WIN32) diff --git a/cmake/FindIconv.cmake b/cmake/FindIconv.cmake index 8f344d3d..f0768f71 100644 --- a/cmake/FindIconv.cmake +++ b/cmake/FindIconv.cmake @@ -19,6 +19,7 @@ IF(APPLE) /opt/local/lib/ /usr/lib/ NO_CMAKE_SYSTEM_PATH) + SET(ICONV_EXTERNAL TRUE) ELSE() find_library(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2) IF(ICONV_LIBRARIES) diff --git a/cmake/symlink.cmake b/cmake/symlink.cmake new file mode 100644 index 00000000..559f3e81 --- /dev/null +++ b/cmake/symlink.cmake @@ -0,0 +1,30 @@ +MACRO(create_symlink symlink_name target install_path) +# According to cmake documentation symlinks work on unix systems only +IF(UNIX) + # Get target components + GET_TARGET_PROPERTY(target_location ${target} LOCATION) + GET_FILENAME_COMPONENT(target_path ${target_location} PATH) + GET_FILENAME_COMPONENT(target_name ${target_location} NAME) + + ADD_CUSTOM_COMMAND( + OUTPUT ${target_path}/${symlink_name} + COMMAND ${CMAKE_COMMAND} ARGS -E remove -f ${target_path}/${symlink_name} + COMMAND ${CMAKE_COMMAND} ARGS -E create_symlink ${target_name} ${symlink_name} + WORKING_DIRECTORY ${target_path} + DEPENDS ${target} + ) + + ADD_CUSTOM_TARGET(SYM_${symlink_name} + ALL + DEPENDS ${target_path}/${symlink_name}) + SET_TARGET_PROPERTIES(SYM_${symlink_name} PROPERTIES CLEAN_DIRECT_OUTPUT 1) + + IF(CMAKE_GENERATOR MATCHES "Xcode") + # For Xcode, replace project config with install config + STRING(REPLACE "${CMAKE_CFG_INTDIR}" + "\${CMAKE_INSTALL_CONFIG_NAME}" output ${target_path}/${symlink_name}) + ENDIF() + + INSTALL(FILES ${target_path}/${symlink_name} DESTINATION ${install_path}) +ENDIF() +ENDMACRO() diff --git a/include/mariadb/ma_io.h b/include/mariadb/ma_io.h index 0035f28a..b9881833 100644 --- a/include/mariadb/ma_io.h +++ b/include/mariadb/ma_io.h @@ -18,6 +18,7 @@ #ifndef _ma_io_h_ #define _ma_io_h_ + #ifdef HAVE_CURL #include #endif @@ -34,6 +35,7 @@ typedef struct void *ptr; } MA_FILE; +#ifdef HAVE_REMOTEIO struct st_rio_methods { MA_FILE *(*open)(const char *url, const char *mode); int (*close)(MA_FILE *ptr); @@ -41,6 +43,7 @@ struct st_rio_methods { size_t (*read)(void *ptr, size_t size, size_t nmemb, MA_FILE *file); char * (*gets)(char *ptr, size_t size, MA_FILE *file); }; +#endif /* function prototypes */ MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql); diff --git a/include/my_global.h b/include/my_global.h index 1925e4b3..7ec47051 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -454,7 +454,7 @@ typedef SOCKET_SIZE_TYPE size_socket; #ifdef _WIN32 #define NO_DIR_LIBRARY /* Not standar dir-library */ #define USE_MY_STAT_STRUCT /* For my_lib */ -#ifdef _SIZE_T_DEFINED +#ifdef _MSC_VER typedef SSIZE_T ssize_t; #endif #endif diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index 76feb01e..ed386b72 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -144,6 +144,7 @@ typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql, int type, const char *prompt, char *buf, int buf_len); /********************** remote IO plugin **********************/ +#ifdef HAVE_REMOTEIO #include /* Remote IO plugin */ @@ -152,6 +153,7 @@ struct st_mysql_client_plugin_REMOTEIO MYSQL_CLIENT_PLUGIN_HEADER struct st_rio_methods *methods; }; +#endif /******** using plugins ************/ diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 520a1d9a..f00ce557 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -224,6 +224,12 @@ IF(WITH_SSL) SET(EXPORTS ${EXPORTS} mariadb_deinitialize_ssl) ENDIF() +# some gcc versions fail to compile asm parts of my_context.c, +# if build type is "Release" (see CONC-133), so we need to add -g flag +IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_BUILD_TYPE MATCHES "Release") + SET_SOURCE_FILES_PROPERTIES(my_context.c PROPERTIES COMPILE_FLAGS -g) +ENDIF() + IF(WIN32) SET(EXPORT_CONTENT "EXPORTS\n") FOREACH(SYMBOL ${EXPORT_SYMBOLS}) @@ -366,10 +372,17 @@ IF(UNIX) SET_TARGET_PROPERTIES(mariadb_obj PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}") ENDIF() -ADD_LIBRARY(mariadbclient STATIC $ ${EXPORT_LINK}) +# Xcode doesn't support targets that have only object files, +# so let's add an empty file to keep Xcode happy +IF(CMAKE_GENERATOR MATCHES Xcode) + FILE(WRITE ${CMAKE_SOURCE_DIR}/libmariadb/empty.c "") + SET(EMPTY_FILE ${CMAKE_SOURCE_DIR}/libmariadb/empty.c) +ENDIF() + +ADD_LIBRARY(mariadbclient STATIC $ ${EMPTY_FILE} ${EXPORT_LINK}) TARGET_LINK_LIBRARIES(mariadbclient ${SYSTEM_LIBS}) -ADD_LIBRARY(libmariadb SHARED $ ${EXPORT_LINK}) +ADD_LIBRARY(libmariadb SHARED $ ${EMPTY_FILE} ${EXPORT_LINK}) TARGET_LINK_LIBRARIES(libmariadb ${SYSTEM_LIBS}) IF(UNIX) SET_TARGET_PROPERTIES(libmariadb PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}") @@ -391,33 +404,18 @@ SET_TARGET_PROPERTIES(libmariadb PROPERTIES VERSION # # Installation # +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/symlink.cmake) + # There are still several projects which don't make use # of the config program. To make sure these programs can # use mariadb client library we provide libmysql symlinks IF(NOT WIN32 AND WITH_MYSQLCOMPAT) - ADD_CUSTOM_COMMAND(OUTPUT "libmysql${CMAKE_SHARED_LIBRARY_SUFFIX}" - "libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX}" - "libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX}" - "libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX}" - COMMAND ${CMAKE_COMMAND} ARGS -E remove -f libmysql${CMAKE_SHARED_LIBRARY_SUFFIX} - COMMAND ${CMAKE_COMMAND} ARGS -E create_symlink libmariadb${CMAKE_SHARED_LIBRARY_SUFFIX} libmysql${CMAKE_SHARED_LIBRARY_SUFFIX} - COMMAND ${CMAKE_COMMAND} ARGS -E remove -f libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX} - COMMAND ${CMAKE_COMMAND} ARGS -E create_symlink libmariadb${CMAKE_SHARED_LIBRARY_SUFFIX} libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX} - COMMAND ${CMAKE_COMMAND} ARGS -E remove -f libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX} - COMMAND ${CMAKE_COMMAND} ARGS -E create_symlink libmariadb${CMAKE_SHARED_LIBRARY_SUFFIX} libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX} - COMMAND ${CMAKE_COMMAND} ARGS -E remove -f libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX} - COMMAND ${CMAKE_COMMAND} ARGS -E create_symlink libmariadbclient${CMAKE_STATIC_LIBRARY_SUFFIX} libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/libmariadb - DEPENDS libmariadb mariadbclient) - -ADD_CUSTOM_TARGET(LIBMYSQL_SYMLINKS - ALL - DEPENDS "libmysql${CMAKE_SHARED_LIBRARY_SUFFIX}" - "libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX}" - "libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX}" - "libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX}" - VERBATIM) + SET(INSTALL_PATH ${LIB_INSTALL_DIR}/${SUFFIX_INSTALL_DIR}) + create_symlink(libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_PATH}) + create_symlink(libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_PATH}) + create_symlink(libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_PATH}) + create_symlink(libmysqlclient_r${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_PATH}) ENDIF() @@ -427,14 +425,6 @@ INSTALL(TARGETS LIBRARY DESTINATION "${LIB_INSTALL_DIR}/${SUFFIX_INSTALL_DIR}" ARCHIVE DESTINATION "${LIB_INSTALL_DIR}/${SUFFIX_INSTALL_DIR}") -IF(NOT WIN32 AND WITH_MYSQLCOMPAT) - INSTALL(FILES "${CMAKE_BINARY_DIR}/libmariadb/libmysql${CMAKE_SHARED_LIBRARY_SUFFIX}" - "${CMAKE_BINARY_DIR}/libmariadb/libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX}" - "${CMAKE_BINARY_DIR}/libmariadb/libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX}" - "${CMAKE_BINARY_DIR}/libmariadb/libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX}" - DESTINATION ${LIB_INSTALL_DIR}/mariadb) -ENDIF() - INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${INCLUDE_INSTALL_DIR}/${SUFFIX_INSTALL_DIR}) INSTALL(FILES diff --git a/libmariadb/client_plugin.c.in b/libmariadb/client_plugin.c.in index 5b9cadcb..88bf0c5f 100644 --- a/libmariadb/client_plugin.c.in +++ b/libmariadb/client_plugin.c.in @@ -58,6 +58,7 @@ static MEM_ROOT mem_root; #define plugin_declarations_sym "_mysql_client_plugin_declaration_" + static uint valid_plugins[][2]= { {MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION}, {MARIADB_CLIENT_CIO_PLUGIN, MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION}, @@ -138,7 +139,6 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) int plugin_nr= get_plugin_nr(type); DBUG_ASSERT(initialized); - DBUG_ASSERT(plugin_nr != -1); if (plugin_nr == -1) return 0; @@ -472,7 +472,7 @@ struct st_mysql_client_plugin * STDCALL mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) { struct st_mysql_client_plugin *p; - int plugin_nr= find_plugin(name, type); + int plugin_nr= get_plugin_nr(type); if (is_not_initialized(mysql, name)) return NULL; diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index c3e839b7..882287e4 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -1380,8 +1380,6 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, unix_socket=mysql->options.unix_socket; - /* Since 5.0.3 reconnect is not enabled by default!! - mysql->reconnect=1; */ mysql->server_status=SERVER_STATUS_AUTOCOMMIT; @@ -2729,7 +2727,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) break; case MYSQL_OPT_SSL_CERT: my_free(mysql->options.ssl_cert); - mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); break; case MYSQL_OPT_SSL_CA: my_free(mysql->options.ssl_ca); diff --git a/libmariadb/ma_io.c b/libmariadb/ma_io.c index 4d4e5a6e..3e8abb7b 100644 --- a/libmariadb/ma_io.c +++ b/libmariadb/ma_io.c @@ -23,10 +23,13 @@ #include #include #include +#include #include #include +#ifdef HAVE_REMOTEIO struct st_mysql_client_plugin_REMOTEIO *rio_plugin= NULL; +#endif /* {{{ ma_open */ MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql) @@ -37,9 +40,10 @@ MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql) if (!location || !location[0]) return NULL; - +#ifdef HAVE_REMOTEIO if (strstr(location, "://")) goto remote; +#endif #ifdef _WIN32 if (mysql && mysql->charset) @@ -118,6 +122,7 @@ MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql) ma_file->ptr= (void *)fp; } return ma_file; +#ifdef HAVE_REMOTEIO remote: /* check if plugin for remote io is available and try * to open location */ @@ -128,6 +133,7 @@ remote: return rio_plugin->methods->open(location, mode); return NULL; } +#endif } /* }}} */ @@ -143,9 +149,11 @@ int ma_close(MA_FILE *file) rc= fclose((FILE *)file->ptr); my_free(file); break; +#ifdef HAVE_REMOTEIO case MA_FILE_REMOTE: rc= rio_plugin->methods->close(file); break; +#endif default: return -1; } @@ -163,9 +171,11 @@ int ma_feof(MA_FILE *file) case MA_FILE_LOCAL: return feof((FILE *)file->ptr); break; +#ifdef HAVE_REMOTEIO case MA_FILE_REMOTE: return rio_plugin->methods->feof(file); break; +#endif default: return -1; } @@ -184,9 +194,11 @@ size_t ma_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file) s= fread(ptr, size, nmemb, (FILE *)file->ptr); return s; break; +#ifdef HAVE_REMOTEIO case MA_FILE_REMOTE: return rio_plugin->methods->read(ptr, size, nmemb, file); break; +#endif default: return -1; } @@ -203,9 +215,11 @@ char *ma_gets(char *ptr, size_t size, MA_FILE *file) case MA_FILE_LOCAL: return fgets(ptr, size, (FILE *)file->ptr); break; +#ifdef HAVE_REMOTEIO case MA_FILE_REMOTE: return rio_plugin->methods->gets(ptr, size, file); break; +#endif default: return NULL; } diff --git a/libmariadb/my_malloc.c b/libmariadb/my_malloc.c index 429932d4..665d767c 100644 --- a/libmariadb/my_malloc.c +++ b/libmariadb/my_malloc.c @@ -79,12 +79,10 @@ my_string my_strdup(const char *from, myf MyFlags) gptr ptr; uint length; - if (MyFlags & MY_ALLOW_ZERO_PTR) - if (!from) - return NULL; + if ((MyFlags & MY_ALLOW_ZERO_PTR) && !from) + return NULL; length=(uint) strlen(from)+1; - if ((ptr=my_malloc(length,MyFlags)) != 0) memcpy((unsigned char*) ptr, (unsigned char*) from,(size_t) length); return((my_string) ptr); diff --git a/libmariadb/my_stmt.c b/libmariadb/my_stmt.c index e5a7ac53..3417347f 100644 --- a/libmariadb/my_stmt.c +++ b/libmariadb/my_stmt.c @@ -1887,6 +1887,7 @@ int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) if (mysql_next_result(stmt->mysql)) { + stmt->state= MYSQL_STMT_FETCH_DONE; SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, stmt->mysql->net.last_error); DBUG_RETURN(1); diff --git a/libmariadb/mysql_async.c b/libmariadb/mysql_async.c index b384afe9..6ad4f260 100644 --- a/libmariadb/mysql_async.c +++ b/libmariadb/mysql_async.c @@ -825,7 +825,7 @@ mysql_close_start(MYSQL *sock) int res; /* It is legitimate to have NULL sock argument, which will do nothing. */ - if (sock) + if (sock && sock->net.cio) { res= mysql_close_slow_part_start(sock); /* If we need to block, return now and do the rest in mysql_close_cont(). */ diff --git a/libmariadb/net.c b/libmariadb/net.c index 892b9dfe..e24eaf60 100644 --- a/libmariadb/net.c +++ b/libmariadb/net.c @@ -164,7 +164,7 @@ static my_bool net_realloc(NET *net, size_t length) DBUG_RETURN(1); } pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); - /* reallocate buffer: + /* reallocate buffer: size= pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE */ if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE, diff --git a/plugins/connection/CMakeLists.txt b/plugins/connection/CMakeLists.txt new file mode 100644 index 00000000..347b2ed8 --- /dev/null +++ b/plugins/connection/CMakeLists.txt @@ -0,0 +1,19 @@ +IF(WIN32) + SET(EXPORT_FILE "../plugin.def") +ENDIF() + +SET(CMAKE_SHARED_LIBRARY_PREFIX "") + +IF(REPLICATION_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_REPLICATION_DYNAMIC=1) + ADD_LIBRARY(replication SHARED replication.c ${EXPORT_FILE}) + SET(INSTALL_LIBS replication) +ENDIF() + +IF(INSTALL_LIBS) + INSTALL(TARGETS + ${INSTALL_LIBS} + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") +ENDIF() diff --git a/plugins/connection/replication.c b/plugins/connection/replication.c new file mode 100644 index 00000000..2caaec84 --- /dev/null +++ b/plugins/connection/replication.c @@ -0,0 +1,325 @@ +/************************************************************************************ + Copyright (C) 2015 MariaDB Corporation AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ + +/* MariaDB Connection plugin for load balancing */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* function prototypes */ +MYSQL *repl_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, + const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag); +void repl_close(MYSQL *mysql); +int repl_command(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg); +int repl_set_options(MYSQL *msql, enum mysql_option option, void *arg); + +#ifdef HAVE_REPLICATION_DYNAMIC + +#undef my_free +#define my_malloc(a,b) malloc(a) +#define my_free(a) free(a) +#endif + +#define MARIADB_MASTER 0 +#define MARIADB_SLAVE 1 + +#ifndef HAVE_REPLICATION_DYNAMIC +MARIADB_CONNECTION_PLUGIN connection_replication_plugin = +#else +MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ = +#endif +{ + MARIADB_CLIENT_CONNECTION_PLUGIN, + MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION, + "replication", + "Georg Richter", + "MariaDB connection plugin for load balancing", + {1, 0, 0}, + "LGPL", + NULL, + NULL, + repl_connect, + repl_close, + repl_set_options, + repl_command +}; + +typedef struct st_conn_repl { + MARIADB_CIO *cio[2]; + MYSQL *slave_mysql; + my_bool read_only; + my_bool round_robin; + char *url; + char *host[2]; + int port[2]; +} REPL_DATA; + +#define SET_SLAVE(mysql, data) mysql->net.cio= data->cio[MARIADB_SLAVE] +#define SET_MASTER(mysql, data) mysql->net.cio= data->cio[MARIADB_MASTER] + + +/* parse url + * Url has the following format: + * master[:port],slave1[:port],slave2[:port],..,slaven[:port] + * + */ + +my_bool repl_parse_url(const char *url, REPL_DATA *data) +{ + char *p; + char *slaves[64]; + int port[64], i,num_slaves= 0; + + if (!url || url[0] == 0) + return 1; + + bzero(slaves, 64 * sizeof(char *)); + bzero(&port, 64 * sizeof(int)); + + memset(data->host, 0, 2 * sizeof(char *)); + memset(data->port, 0, 2 * sizeof(int)); + + if (data->url) + my_free(data->url); + + data->url= my_strdup(url, MYF(0)); + data->host[MARIADB_MASTER]= p= data->url; + + /* get slaves */ + while((p && (p= strchr(p, ',')))) + { + *p= '\0'; + p++; + if (*p) + { + slaves[num_slaves]= p; + num_slaves++; + } + } + + if (!num_slaves) + return 0; + if (num_slaves == 1) + data->host[MARIADB_SLAVE]= slaves[0]; + else + { + struct timeval tp; + int random_nr; + gettimeofday(&tp,NULL); + srand(tp.tv_usec / 1000 + tp.tv_sec * 1000); + + random_nr= rand() % num_slaves; + data->host[MARIADB_SLAVE]= slaves[random_nr]; + } + + /* check ports */ + for (i=0; i < 2 && data->host[i]; i++) + { + /* We need to be aware of IPv6 addresses: According to RFC3986 sect. 3.2.2 + hostnames have to be enclosed in square brackets if a port is given */ + if (data->host[i][0]= '[' && strchr(data->host[i], ':') && (p= strchr(data->host[i],']'))) + { + /* ignore first square bracket */ + data->host[i]++; + *p= 0; + p++; + } + else + p= data->host[i]; + if (p && (p= strchr(p, ':'))) + { + *p= '\0'; + p++; + data->port[i]= atoi(p); + } + } + + return 0; +} + +MYSQL *repl_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, + const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag) +{ + REPL_DATA *data= NULL; + MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr; + + if (!(data= calloc(1, sizeof(REPL_DATA)))) + { + mysql->methods->set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return NULL; + } + + memset(data->cio, 0, 2 * sizeof(MARIADB_CIO *)); + + if (repl_parse_url(host, data)) + goto error; + + /* try to connect to master */ + if (!(mysql->methods->db_connect(mysql, data->host[MARIADB_MASTER], user, passwd, db, + data->port[MARIADB_MASTER] ? data->port[MARIADB_MASTER] : port, unix_socket, clientflag))) + goto error; + + data->cio[MARIADB_MASTER]= mysql->net.cio; + hdlr->data= data; + + /* to allow immediate access without connection delay, we will start + * connecting to slave(s) in background */ + + /* if slave connection will fail, we will not return error but use master instead */ + if (!(data->slave_mysql= mysql_init(NULL)) || + !(mysql->methods->db_connect(data->slave_mysql, data->host[MARIADB_SLAVE], user, passwd, db, + data->port[MARIADB_SLAVE] ? data->port[MARIADB_SLAVE] : port, unix_socket, clientflag))) + { + if (data->slave_mysql) + mysql_close(data->slave_mysql); + data->cio[MARIADB_SLAVE]= NULL; + } + else + { + data->cio[MARIADB_SLAVE]= data->slave_mysql->net.cio; + data->slave_mysql->net.cio->mysql= mysql; + } + return mysql; +error: + if (data) + { + if (data->url) + my_free(data->url); + my_free(data); + } + return NULL; +} + +void repl_close(MYSQL *mysql) +{ + MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr; + REPL_DATA *data= (REPL_DATA *)hdlr->data; + + /* restore master */ + SET_MASTER(mysql, data); + + /* free slave information and close connection */ + if (data->cio[MARIADB_SLAVE]) + { + /* restore mysql */ + data->cio[MARIADB_SLAVE]->mysql= data->slave_mysql; + mysql_close(data->slave_mysql); + data->cio[MARIADB_SLAVE]= NULL; + data->slave_mysql= NULL; + } + + /* free masrwe information and close connection */ + my_free(data->url); + my_free(data); + mysql->net.conn_hdlr->data= NULL; + return mysql_close(mysql); +} + +static my_bool is_slave_command(const char *buffer, size_t buffer_len) +{ + const char *buffer_end= buffer + buffer_len; + + for (; buffer < buffer_end; ++buffer) + { + char c; + if (isalpha(c=*buffer)) + { + if (tolower(c) == 's') + return 1; + return 0; + } + } + return 0; +} + +static my_bool is_slave_stmt(MYSQL *mysql, const char *buffer) +{ + unsigned long stmt_id= uint4korr(buffer); + LIST *stmt_list= mysql->stmts; + + for (; stmt_list; stmt_list= stmt_list->next) + { + MYSQL_STMT *stmt= (MYSQL_STMT *)stmt_list->data; + if (stmt->stmt_id == stmt_id) + return 1; + } + return 0; +} + + +int repl_command(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg) +{ + REPL_DATA *data= (REPL_DATA *)mysql->net.conn_hdlr->data; + + /* if we don't have slave or slave became unavailable root traffic to master */ + if (!data->cio[MARIADB_SLAVE] || !data->read_only) + { + SET_MASTER(mysql, data); + return 0; + } + switch(command) { + case MYSQL_COM_QUERY: + case MYSQL_COM_STMT_PREPARE: + if (is_slave_command(arg, length)) + SET_SLAVE(mysql, data); + else + SET_MASTER(mysql,data); + break; + case MYSQL_COM_STMT_EXECUTE: + case MYSQL_COM_STMT_FETCH: + if (data->cio[MARIADB_SLAVE]->mysql->stmts && is_slave_stmt(data->cio[MARIADB_SLAVE]->mysql, arg)) + SET_SLAVE(mysql, data); + else + SET_MASTER(mysql,data); + break; + + default: + SET_MASTER(mysql,data); + break; + } + return 0; +} + +int repl_set_options(MYSQL *mysql, enum mysql_option option, void *arg) +{ + REPL_DATA *data= (REPL_DATA *)mysql->net.conn_hdlr->data; + + switch(option) { + case MARIADB_OPT_CONNECTION_READ_ONLY: + data->read_only= *(my_bool *)arg; + return 0; +/* + case MARIADB_OPT_CONNECTION_ROUND_ROBIN: + data->round_robin= *(my_bool *)arg; + return 0; */ + default: + return -1; + } +} diff --git a/unittest/libmariadb/async.c b/unittest/libmariadb/async.c index 4a947ab9..e54fb74d 100644 --- a/unittest/libmariadb/async.c +++ b/unittest/libmariadb/async.c @@ -200,10 +200,17 @@ static int test_conc131(MYSQL *my) return OK; } +static int test_conc129(MYSQL *my) +{ + MYSQL *mysql= mysql_init(NULL); + FAIL_IF(mysql_close_start(mysql), "No error expected"); +} + struct my_tests_st my_tests[] = { {"async1", async1, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc131", test_conc131, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc129", test_conc129, TEST_CONNECTION_NONE, 0, NULL, NULL}, {NULL, NULL, 0, 0, NULL, NULL} }; diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index dc89d9c6..5896b6d5 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -57,7 +57,7 @@ static int test_conc66(MYSQL *my) diag("Error: %s", mysql_error(mysql)); return FAIL; } - rc= mysql_query(my, "DROP USER conc66@%"); + rc= mysql_query(my, "DROP USER conc66@localhost"); check_mysql_rc(rc, my); mysql_close(mysql); diff --git a/unittest/libmariadb/misc.c b/unittest/libmariadb/misc.c index bf1aaa27..a5df2bc0 100644 --- a/unittest/libmariadb/misc.c +++ b/unittest/libmariadb/misc.c @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include -void *remote_plugin; /* Bug#28075 "COM_DEBUG crashes mysqld" @@ -974,6 +973,8 @@ static int test_conc117(MYSQL *mysql) return OK; } +#ifdef HAVE_REMOTEIO +void *remote_plugin; static int test_remote1(MYSQL *mysql) { int rc; @@ -1019,11 +1020,14 @@ static int test_remote2(MYSQL *my) mysql_close(mysql); return OK; } +#endif struct my_tests_st my_tests[] = { +#ifdef HAVE_REMOTEIO {"test_remote1", test_remote1, TEST_CONNECTION_NEW, 0, NULL, NULL}, - {"test_remote2", test_remote2, TEST_CONNECTION_NEW, 0, NULL, NULL}, - {"test_conc117", test_conc117, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_remote2", test_remote2, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#endif + {"test_conc117", test_conc117, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc_114", test_conc_114, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc49", test_conc49, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, diff --git a/unittest/libmariadb/ps_bugs.c b/unittest/libmariadb/ps_bugs.c index a55dc00c..d4d6fa0f 100644 --- a/unittest/libmariadb/ps_bugs.c +++ b/unittest/libmariadb/ps_bugs.c @@ -3868,7 +3868,45 @@ static int test_conc_5(MYSQL *mysql) return OK; } +static int test_conc141(MYSQL *mysql) +{ + int rc; + char *query= "CALL p_conc141"; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc141"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE conc141 (KeyVal int not null primary key)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO conc141 VALUES(1)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p_conc141"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE PROCEDURE p_conc141()\n" + "BEGIN\n" + "select * from conc141;\n" + "insert into conc141(KeyVal) VALUES(1);\n" + "END"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + /* skip first result */ + rc= mysql_stmt_next_result(stmt); + FAIL_IF(rc==-1, "No more results and error expected"); + mysql_stmt_free_result(stmt); + FAIL_IF(mysql_stmt_errno(stmt), "No Error expected"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + return OK; +} + struct my_tests_st my_tests[] = { + {"test_conc141", test_conc141, TEST_CONNECTION_NEW, 0, NULL , NULL}, {"test_conc67", test_conc67, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, {"test_conc_5", test_conc_5, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, {"test_bug1115", test_bug1115, TEST_CONNECTION_DEFAULT, 0, NULL , NULL},