1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-07 02:42:49 +03:00

Initial aurora implementation

This commit is contained in:
Georg Richter
2015-12-10 12:52:51 +01:00
parent ebe3cc9935
commit bec245a913
22 changed files with 1143 additions and 110 deletions

View File

@@ -21,6 +21,14 @@ IF(COMMAND CMAKE_POLICY)
CMAKE_POLICY(SET CMP0003 NEW)
ENDIF()
#Allow access to non existing targets
IF(CMAKE_VERSION VERSION_GREATER "2.9.9")
CMAKE_POLICY(SET CMP0026 OLD)
CMAKE_POLICY(SET CMP0042 OLD)
CMAKE_POLICY(SET CMP0045 OLD)
ENDIF()
SET(MARIADB_CONNECTOR_C_COPYRIGHT "2013-2015 MariaDB Corporation Ab")
### Options ###
@@ -149,6 +157,10 @@ IF(CMAKE_HAVE_PTHREAD_H)
SET(CMAKE_REQUIRED_INCLUDES pthread.h)
ENDIF()
IF(DBUG_OFF)
ADD_DEFINITIONS(-DDBUG_OFF=1)
ENDIF()
IF(WIN32)
SET(HAVE_THREADS 1)
ADD_DEFINITIONS(-DHAVE_DLOPEN)

View File

@@ -1,12 +1,16 @@
# plugin configuration
MACRO(REGISTER_PLUGIN name source struct type target allow)
SET(PLUGIN_TYPE ${${name}})
IF(NOT PLUGIN_TYPE STREQUAL "OFF")
SET(PLUGIN_TYPE ${type})
ENDIF()
IF(PLUGINS)
LIST(REMOVE_ITEM PLUGINS ${name})
ENDIF()
SET(${name}_PLUGIN_SOURCE ${source})
MARK_AS_ADVANCED(${name}_PLUGIN_SOURCE})
SET(${name}_PLUGIN_TYPE ${type})
SET(${name}_PLUGIN_TYPE ${PLUGIN_TYPE})
IF(NOT ${target} STREQUAL "")
SET(${name}_PLUGIN_TARGET ${target})
ENDIF()
@@ -41,6 +45,7 @@ REGISTER_PLUGIN("TRACE_EXAMPLE" "${CMAKE_SOURCE_DIR}/plugins/trace/trace_example
#Connection
REGISTER_PLUGIN("REPLICATION" "${CMAKE_SOURCE_DIR}/plugins/connection/replication.c" "connection_replication_plugin" "STATIC" "" 1)
REGISTER_PLUGIN("AURORA" "${CMAKE_SOURCE_DIR}/plugins/connection/aurora.c" "connection_aurora_plugin" "STATIC" "" 1)
# Allow registration of additional plugins
IF(PLUGIN_CONF_FILE)
@@ -50,7 +55,7 @@ ENDIF()
SET(LIBMARIADB_SOURCES "")
MESSAGE(STATUS "Plugin configuration")
MESSAGE(STATUS "Plugin configuration:")
FOREACH(PLUGIN ${PLUGINS})
IF(WITH_${PLUGIN}_PLUGIN AND ${${PLUGIN}_PLUGIN_CHG} GREATER 0)
SET(${PLUGIN}_PLUGIN_TYPE ${WITH_${PLUGIN}_PLUGIN})

View File

@@ -52,6 +52,8 @@ struct st_mysql_options_extension {
char *ssl_fp; /* finger print of server certificate */
char *ssl_fp_list; /* white list of finger prints */
char *ssl_pw; /* password for encrypted certificates */
char *url; /* for connection handler we need to save URL for reconnect */
my_bool read_only;
};
#define OPT_HAS_EXT_VAL(a,key) \

View File

@@ -482,8 +482,8 @@ const char * STDCALL mysql_get_client_info(void);
unsigned long STDCALL mysql_get_client_version(void);
my_bool STDCALL mariadb_connection(MYSQL *mysql);
const char * STDCALL mysql_get_server_name(MYSQL *mysql);
CHARSET_INFO * STDCALL mysql_get_charset_by_name(const char *csname);
CHARSET_INFO * STDCALL mysql_get_charset_by_nr(unsigned int csnr);
CHARSET_INFO * STDCALL mariadb_get_charset_by_name(const char *csname);
CHARSET_INFO * STDCALL mariadb_get_charset_by_nr(unsigned int csnr);
size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, CHARSET_INFO *from_cs,
char *to, size_t *to_len, CHARSET_INFO *to_cs, int *errorcode);
int STDCALL mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...);
@@ -493,6 +493,7 @@ my_socket STDCALL mysql_get_socket(const MYSQL *mysql);
unsigned int STDCALL mysql_get_timeout_value(const MYSQL *mysql);
unsigned int STDCALL mysql_get_timeout_value_ms(const MYSQL *mysql);
int STDCALL mariadb_get_connection_type(MYSQL *mysql);
my_bool STDCALL mysql_reconnect(MYSQL *mysql);
/* Async API */
int STDCALL mysql_close_start(MYSQL *sock);
@@ -628,6 +629,7 @@ struct st_mysql_methods {
int (*db_stmt_fetch_to_bind)(MYSQL_STMT *stmt, unsigned char *row);
void (*db_stmt_flush_unbuffered)(MYSQL_STMT *stmt);
void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...);
my_bool (*reconnect)(MYSQL *mysql);
};
/* synonyms/aliases functions */

View File

@@ -95,6 +95,7 @@ typedef struct st_ma_connection_plugin
int (*options)(MYSQL *mysql, enum mysql_option, void *arg);
int (*set_connection)(MYSQL *mysql,enum enum_server_command command, const char *arg,
size_t length, my_bool skipp_check, void *opt_arg);
my_bool (*reconnect)(MYSQL *mysql);
} MARIADB_CONNECTION_PLUGIN;
#define MARIADB_DB_DRIVER(a) ((a)->ext_db)

View File

@@ -230,6 +230,7 @@ typedef struct st_connection_handler
{
struct st_ma_connection_plugin *plugin;
void *data;
my_bool active;
my_bool free_data;
} MA_CONNECTION_HANDLER;

View File

@@ -34,6 +34,8 @@ SET(EXPORT_SYMBOLS
mariadb_dyncol_val_double
mariadb_dyncol_val_long
mariadb_dyncol_val_str
mariadb_get_charset_by_name
mariadb_get_charset_by_nr
mariadb_get_connection_type
mysql_affected_rows
mysql_autocommit
@@ -124,6 +126,7 @@ SET(EXPORT_SYMBOLS
mysql_query
mysql_query_cont
mysql_query_start
mysql_reconnect
mysql_read_query_result
mysql_read_query_result_cont
mysql_read_query_result_start

View File

@@ -124,11 +124,14 @@ struct st_mysql_methods MARIADB_DEFAULT_METHODS;
#define native_password_plugin_name "mysql_native_password"
#define IS_CONNHDLR_ACTIVE(mysql)\
((mysql)->net.conn_hdlr && (mysql)->net.conn_hdlr->active)
static void end_server(MYSQL *mysql);
static void mysql_close_memory(MYSQL *mysql);
void read_user_name(char *name);
static void append_wild(char *to,char *end,const char *wild);
static my_bool mysql_reconnect(MYSQL *mysql);
my_bool mysql_reconnect(MYSQL *mysql);
static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length);
extern int mysql_client_plugin_init();
@@ -354,13 +357,6 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg,
DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length));
if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data)
{
result= mysql->net.conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg);
if (result== -1)
DBUG_RETURN(result);
}
if (mysql->net.pvio == 0)
{ /* Do reconnect if possible */
if (mysql_reconnect(mysql))
@@ -376,6 +372,14 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg,
goto end;
}
if (IS_CONNHDLR_ACTIVE(mysql))
{
result= mysql->net.conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg);
if (result== -1)
DBUG_RETURN(result);
}
CLEAR_CLIENT_ERROR(mysql);
mysql->info=0;
@@ -384,13 +388,6 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg,
if (!arg)
arg="";
/* check if connection kills itself */
if (command == COM_PROCESS_KILL)
{
unsigned long thread_id= uint4korr(arg);
if (thread_id == mysql->thread_id)
skipp_check= 1;
}
if (net_write_command(net,(uchar) command,arg,
length ? length : (ulong) strlen(arg)))
{
@@ -660,7 +657,7 @@ enum option_val
#define OPT_SET_EXTENDED_VALUE_INT(OPTS, KEY, VAL) \
CHECK_OPT_EXTENSION_SET(OPTS) \
+ (OPTS)->extension->KEY= (VAL)
(OPTS)->extension->KEY= (VAL)
static TYPELIB option_types={array_elements(default_options)-1,
@@ -1145,6 +1142,7 @@ mysql_init(MYSQL *mysql)
bzero((char*) (mysql),sizeof(*(mysql)));
mysql->options.connect_timeout=CONNECT_TIMEOUT;
mysql->charset= default_charset_info;
mysql->methods= &MARIADB_DEFAULT_METHODS;
strmov(mysql->net.sqlstate, "00000");
mysql->net.last_error[0]= mysql->net.last_errno= 0;
@@ -1291,9 +1289,38 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
const char *passwd, const char *db,
uint port, const char *unix_socket,unsigned long client_flag)
{
char *end;
if (!mysql->methods)
mysql->methods= &MARIADB_DEFAULT_METHODS;
if (host && (end= strstr(host, "://")))
{
MARIADB_CONNECTION_PLUGIN *plugin;
char plugin_name[64];
bzero(plugin_name, 64);
strncpy(plugin_name, host, MIN(end - host, 63));
end+= 3;
if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN)))
return NULL;
if (!(mysql->net.conn_hdlr= (MA_CONNECTION_HANDLER *)my_malloc(sizeof(MA_CONNECTION_HANDLER), MYF(MY_ZEROFILL))))
{
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
return NULL;
}
/* save URL for reconnect */
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, url, host);
mysql->net.conn_hdlr->plugin= plugin;
if (plugin && plugin->connect)
return plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag);
}
return mysql->methods->db_connect(mysql, host, user, passwd,
db, port, unix_socket, client_flag);
}
@@ -1320,32 +1347,6 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
if (!mysql->methods)
mysql->methods= &MARIADB_DEFAULT_METHODS;
/* special case:
* If hostname contains "://", e.g. "repl://localhost", we need to process connection
* by connection plugin
*/
if (host && (end= strstr(host, "://")))
{
MARIADB_CONNECTION_PLUGIN *plugin;
char plugin_name[64];
bzero(plugin_name, 64);
strncpy(plugin_name, host, MIN(end - host, 63));
end+= 3;
if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN)))
DBUG_RETURN(NULL);
if (!(mysql->net.conn_hdlr= (MA_CONNECTION_HANDLER *)my_malloc(sizeof(MA_CONNECTION_HANDLER), MYF(MY_ZEROFILL))))
{
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
DBUG_RETURN(NULL);
}
mysql->net.conn_hdlr->plugin= plugin;
if (plugin->connect)
return plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag);
}
ma_set_connect_attrs(mysql);
@@ -1715,14 +1716,22 @@ my_suspend_hook(my_bool suspend, void *data)
}
static my_bool mysql_reconnect(MYSQL *mysql)
my_bool STDCALL mysql_reconnect(MYSQL *mysql)
{
MYSQL tmp_mysql;
struct my_hook_data hook_data;
struct mysql_async_context *ctxt= NULL;
LIST *li_stmt= mysql->stmts;
DBUG_ENTER("mysql_reconnect");
/* check if connection handler is active */
if (IS_CONNHDLR_ACTIVE(mysql))
{
if (mysql->net.conn_hdlr->plugin && mysql->net.conn_hdlr->plugin->connect)
DBUG_RETURN(mysql->net.conn_hdlr->plugin->reconnect(mysql));
}
if (!mysql->reconnect ||
(mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info)
{
@@ -1734,6 +1743,13 @@ static my_bool mysql_reconnect(MYSQL *mysql)
mysql_init(&tmp_mysql);
tmp_mysql.options=mysql->options;
if (mysql->net.conn_hdlr)
{
tmp_mysql.net.conn_hdlr= mysql->net.conn_hdlr;
mysql->net.conn_hdlr= 0;
}
/* don't reread options from configuration files */
tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL;
@@ -1762,6 +1778,17 @@ static my_bool mysql_reconnect(MYSQL *mysql)
DBUG_RETURN(1);
}
for (;li_stmt;li_stmt= li_stmt->next)
{
MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data;
if (stmt->state != MYSQL_STMT_INITTED)
{
stmt->state= MYSQL_STMT_INITTED;
SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
}
}
tmp_mysql.reconnect= mysql->reconnect;
tmp_mysql.free_me= mysql->free_me;
tmp_mysql.stmts= mysql->stmts;
@@ -1923,6 +1950,7 @@ static void mysql_close_options(MYSQL *mysql)
my_free(mysql->options.extension->ssl_fp);
my_free(mysql->options.extension->ssl_fp_list);
my_free(mysql->options.extension->ssl_pw);
my_free(mysql->options.extension->url);
if(hash_inited(&mysql->options.extension->connect_attrs))
hash_free(&mysql->options.extension->connect_attrs);
if ((ctxt = mysql->options.extension->async_context) != 0)
@@ -1985,7 +2013,8 @@ mysql_close(MYSQL *mysql)
DBUG_ENTER("mysql_close");
if (mysql) /* Some simple safety */
{
if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data)
if (IS_CONNHDLR_ACTIVE(mysql))
{
void *p= (void *)mysql->net.conn_hdlr;
mysql->net.conn_hdlr->plugin->close(mysql);
@@ -1998,6 +2027,7 @@ mysql_close(MYSQL *mysql)
/* reset the connection in all active statements */
ma_invalidate_stmts(mysql, "mysql_close()");
mysql_close_memory(mysql);
mysql_close_options(mysql);
mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
@@ -2048,7 +2078,9 @@ int mthd_my_read_query_result(MYSQL *mysql)
DBUG_ENTER("mthd_my_read_query_result");
if (!mysql || (length = net_safe_read(mysql)) == packet_error)
{
DBUG_RETURN(1);
}
free_old_query(mysql); /* Free old result */
get_info:
pos=(uchar*) mysql->net.read_pos;
@@ -2496,7 +2528,6 @@ mysql_stat(MYSQL *mysql)
DBUG_RETURN((char*) mysql->net.read_pos);
}
int STDCALL
mysql_ping(MYSQL *mysql)
{
@@ -2510,7 +2541,6 @@ mysql_ping(MYSQL *mysql)
return rc;
}
char * STDCALL
mysql_get_server_info(MYSQL *mysql)
{
@@ -2878,10 +2908,8 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_pw, (char *)arg1);
break;
case MARIADB_OPT_CONNECTION_READ_ONLY:
if (mysql->net.conn_hdlr)
DBUG_RETURN(mysql->net.conn_hdlr->plugin->options(mysql, MARIADB_OPT_CONNECTION_READ_ONLY, arg1));
else
return -1;
OPT_SET_EXTENDED_VALUE_INT(&mysql->options, read_only, *(my_bool *)arg1);
break;
default:
va_end(ap);
DBUG_RETURN(-1);
@@ -3278,6 +3306,15 @@ int STDCALL mariadb_get_connection_type(MYSQL *mysql)
return (int)mysql->net.pvio->type;
}
CHARSET_INFO * STDCALL mariadb_get_charset_by_name(const char *csname)
{
return (CHARSET_INFO *)mysql_find_charset_name(csname);
}
CHARSET_INFO * STDCALL mariadb_get_charset_by_nr(unsigned int csnr)
{
return (CHARSET_INFO *)mysql_find_charset_nr(csnr);
}
/*
* Default methods for a connection. These methods are
* stored in mysql->methods and can be overwritten by
@@ -3315,5 +3352,9 @@ struct st_mysql_methods MARIADB_DEFAULT_METHODS = {
/* store values in bind buffer */
mthd_stmt_fetch_to_bind,
/* skip unbuffered stmt result */
mthd_stmt_flush_unbuffered
mthd_stmt_flush_unbuffered,
/* set error */
my_set_error,
/* reconnect */
mysql_reconnect
};

View File

@@ -416,7 +416,6 @@ int store_param(MYSQL_STMT *stmt, int column, unsigned char **p)
{
DBUG_ENTER("store_param");
DBUG_PRINT("info", ("column: %d type: %d", column, stmt->params[column].buffer_type));
printf("type: %d\n", column, stmt->params[column].buffer_type);
switch (stmt->params[column].buffer_type) {
case MYSQL_TYPE_TINY:
int1store(*p, *(uchar *)stmt->params[column].buffer);

View File

@@ -182,7 +182,9 @@ static my_bool net_realloc(NET *net, size_t length)
/* Remove unwanted characters from connection */
void net_clear(NET *net)
{
size_t len;
DBUG_ENTER("net_clear");
ma_pvio_has_data(net->pvio, &len);
net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
net->write_pos=net->buff;
DBUG_VOID_RETURN;
@@ -193,6 +195,7 @@ int net_flush(NET *net)
{
int error=0;
DBUG_ENTER("net_flush");
if (net->buff != net->write_pos)
{
error=net_real_write(net,(char*) net->buff,

View File

@@ -13,10 +13,24 @@ IF(REPLICATION_PLUGIN_TYPE MATCHES "DYNAMIC")
"FILE_DESCRIPTION:Connection plugin for master/slave environment")
ENDIF()
ADD_DEFINITIONS(-DHAVE_REPLICATION_DYNAMIC=1)
ADD_LIBRARY(replication SHARED replication.c ${EXPORT_FILE})
ADD_LIBRARY(replication SHARED ${replication_RC} replication.c ${EXPORT_FILE})
SET(INSTALL_LIBS replication)
ENDIF()
IF(REPLICATION_PLUGIN_TYPE MATCHES "DYNAMIC")
IF(WIN32)
SET_VERSION_INFO("TARGET:aurora"
"FILE_TYPE:VFT_DLL"
"SOURCE_FILE:plugins/connection/aurora.c"
"ORIGINAL_FILE_NAME:aurora.dll"
"FILE_DESCRIPTION:Connection plugin for Amazon AWS Aurora")
ENDIF()
ADD_DEFINITIONS(-DHAVE_AURORA_DYNAMIC=1)
ADD_LIBRARY(aurora SHARED ${aurora_RC} aurora.c ${EXPORT_FILE})
SET(INSTALL_LIBS ${INSTALL_LIBS} aurora)
ENDIF()
IF(INSTALL_LIBS)
INSTALL(TARGETS
${INSTALL_LIBS}

820
plugins/connection/aurora.c Normal file
View File

@@ -0,0 +1,820 @@
/************************************************************************************
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 <http://www.gnu.org/licenses>
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 Aurora failover */
#include <my_global.h>
#include <my_sys.h>
#include <errmsg.h>
#include <ma_common.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <m_string.h>
#ifndef WIN32
#include <sys/time.h>
#endif
/* function prototypes */
int aurora_init(char *errormsg, size_t errormsg_size,
int unused __attribute__((unused)),
va_list unused1 __attribute__((unused)));
MYSQL *aurora_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 aurora_close(MYSQL *mysql);
int aurora_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
size_t length, my_bool skipp_check, void *opt_arg);
int aurora_set_options(MYSQL *msql, enum mysql_option option, void *arg);
my_bool aurora_reconnect(MYSQL *mysql);
#define AURORA_MAX_INSTANCES 16
#define AURORA_UNKNOWN -1
#define AURORA_PRIMARY 0
#define AURORA_REPLICA 1
#define AURORA_UNAVAILABLE 2
#define ENABLE_AURORA(mysql)\
(mysql)->net.conn_hdlr->active= 1;
#define DISABLE_AURORA(mysql)\
(mysql)->net.conn_hdlr->active= 0;
#ifndef HAVE_REPLICATION_DYNAMIC
MARIADB_CONNECTION_PLUGIN connection_aurora_plugin =
#else
MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ =
#endif
{
MARIADB_CLIENT_CONNECTION_PLUGIN,
MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION,
"aurora",
"Georg Richter",
"MariaDB connection plugin for Aurora failover",
{1, 0, 0},
"LGPL",
aurora_init,
NULL,
aurora_connect,
aurora_close,
aurora_set_options,
aurora_command,
aurora_reconnect
};
typedef struct st_aurora_instance {
char *host;
int port;
time_t blacklisted;
int type;
} AURORA_INSTANCE;
typedef struct st_conn_aurora {
MARIADB_PVIO *pvio[2];
MYSQL *mysql[2];
my_bool active[2];
char *url;
unsigned int num_instances;
AURORA_INSTANCE instance[AURORA_MAX_INSTANCES];
char *username, *password, *database;
unsigned int port;
unsigned long client_flag;
unsigned int last_instance_type; /* Primary or Replica */
char primary_id[100];
} AURORA;
#define AURORA_BLACKLIST_TIMEOUT 150
#define AURORA_IS_BLACKLISTED(a, i) \
((time(NULL) - (a)->instance[(i)].blacklisted) < AURORA_BLACKLIST_TIMEOUT)
/* {{{ my_bool aurora_swutch_connection */
my_bool aurora_switch_connection(MYSQL *mysql, AURORA *aurora, int type)
{
switch (type)
{
case AURORA_REPLICA:
if (aurora->mysql[AURORA_REPLICA])
{
mysql->net.pvio= aurora->pvio[AURORA_REPLICA];
aurora->pvio[AURORA_REPLICA]->mysql= mysql;
mysql->thread_id= aurora->mysql[AURORA_REPLICA]->thread_id;
aurora->last_instance_type= AURORA_REPLICA;
}
break;
case AURORA_PRIMARY:
if (aurora->mysql[AURORA_PRIMARY])
{
if (aurora->mysql[AURORA_REPLICA])
aurora->mysql[AURORA_REPLICA]->net.pvio->mysql= aurora->mysql[AURORA_REPLICA];
mysql->net.pvio= aurora->pvio[AURORA_PRIMARY];
mysql->thread_id= aurora->mysql[AURORA_PRIMARY]->thread_id;
aurora->last_instance_type= AURORA_PRIMARY;
}
break;
}
}
/* }}} */
/* {{{ int aurora_init
*
* plugin initialization function
*/
int aurora_init(char *errormsg, size_t errormsg_size,
int unused __attribute__((unused)),
va_list unused1 __attribute__((unused)))
{
/* random generator initialization */
#ifndef WIN32
struct timeval tp;
gettimeofday(&tp,NULL);
srand(tp.tv_usec / 1000 + tp.tv_sec * 1000);
#else
srand(GetTickCount());
#endif
return 0;
}
/* }}} */
/* {{{ void aurora_close_memory */
void aurora_close_memory(AURORA *aurora)
{
free(aurora->url);
free(aurora->username);
free(aurora->password);
free(aurora->database);
free(aurora);
}
/* }}} */
/* {{{ my_bool aurora_parse_url
*
* parse url
* Url has the following format:
* instance1:port, instance2:port, .., instanceN:port
*
*/
my_bool aurora_parse_url(const char *url, AURORA *aurora)
{
char *p, *c;
int i;
if (!url || url[0] == 0)
return 1;
bzero(aurora->instance, (AURORA_MAX_INSTANCES + 1) * sizeof(char *));
bzero(&aurora->port, (AURORA_MAX_INSTANCES + 1) * sizeof(int));
if (aurora->url)
free(aurora->url);
aurora->url= strdup(url);
c= aurora->url;
/* get instances */
while((c))
{
if (p= strchr(c, ','))
{
*p= '\0';
p++;
}
if (*c)
{
aurora->instance[aurora->num_instances].host= c;
aurora->num_instances++;
}
c= p;
}
if (!aurora->num_instances)
return 0;
/* check ports */
for (i=0; i < aurora->num_instances && aurora->instance[i].host; i++)
{
aurora->instance[i].type= AURORA_UNKNOWN;
/* 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 (aurora->instance[i].host[0]== '[' &&
strchr(aurora->instance[i].host, ':') &&
(p= strchr(aurora->instance[i].host,']')))
{
/* ignore first square bracket */
memmove(aurora->instance[i].host,
aurora->instance[i].host+1,
strlen(aurora->instance[i].host) - 1);
p= strchr(aurora->instance[i].host,']');
*p= 0;
p++;
}
else
p= aurora->instance[i].host;
if (p && (p= strchr(p, ':')))
{
*p= '\0';
p++;
aurora->instance[i].port= atoi(p);
}
}
return 0;
}
/* }}} */
/* {{{ int aurora_get_instance_type
*
* RETURNS:
*
* AURORA_PRIMARY
* AURORA_REPLICA
* -1 on error
*/
int aurora_get_instance_type(MYSQL *mysql)
{
int rc;
char *query= "select variable_value from information_schema.global_variables where variable_name='INNODB_READ_ONLY' AND variable_value='OFF'";
if (!mysql_query(mysql, query))
{
MYSQL_RES *res= mysql_store_result(mysql);
rc= mysql_num_rows(res) ? AURORA_PRIMARY : AURORA_REPLICA;
mysql_free_result(res);
return rc;
}
return -1;
}
/* }}} */
/* {{{ my_bool aurora_get_primary_id
*
* try to find primary instance from slave by retrieving
* primary_id information_schema.replica_host_status information
*
* If the function succeeds, primary_id will be copied into
* aurora->primary_id
*
* Returns:
* 1 on success
* 0 if an error occured or primary_id couldn't be
* found
*/
my_bool aurora_get_primary_id(MYSQL *mysql, AURORA *aurora)
{
my_bool rc= 0;
if (!mysql_query(mysql, "select server_id from information_schema.replica_host_status "
"where session_id = 'MASTER_SESSION_ID'"))
{
MYSQL_RES *res;
MYSQL_ROW row;
if ((res= mysql_store_result(mysql)))
{
if ((row= mysql_fetch_row(res)))
{
if (row[0])
{
strcpy(aurora->primary_id, row[0]);
rc= 1;
}
}
mysql_free_result(res);
}
}
return rc;
}
/* }}} */
/* {{{ unsigned int aurora_get_valid_instances
*
* returns the number of instances which are
* not blacklisted or don't have a type assigned.
*/
static unsigned int aurora_get_valid_instances(AURORA *aurora, AURORA_INSTANCE **instances)
{
int i, valid_instances= 0;
memset(instances, 0, sizeof(AURORA_INSTANCE *) * AURORA_MAX_INSTANCES);
for (i=0; i < aurora->num_instances; i++)
{
if (aurora->instance[i].type != AURORA_UNAVAILABLE)
{
if (aurora->instance[i].type == AURORA_PRIMARY && aurora->active[AURORA_PRIMARY])
continue;
instances[valid_instances]= &aurora->instance[i];
valid_instances++;
}
}
return valid_instances;
}
/* }}} */
/* {{{ void aurora_refresh_blacklist() */
void aurora_refresh_blacklist(AURORA *aurora)
{
int i;
for (i=0; i < aurora->num_instances; i++)
{
if (aurora->instance[i].blacklisted &&
!(AURORA_IS_BLACKLISTED(aurora, i)))
{
aurora->instance[i].blacklisted= 0;
aurora->instance[i].type= AURORA_UNKNOWN;
}
}
}
/* }}} */
/* {{{ MYSQL *aurora_connect_instance() */
MYSQL *aurora_connect_instance(AURORA *aurora, AURORA_INSTANCE *instance, MYSQL *mysql)
{
if (!mysql->methods->db_connect(mysql,
instance->host,
aurora->username,
aurora->password,
aurora->database,
instance->port ? instance->port : aurora->port,
NULL,
aurora->client_flag))
{
/* connection not available */
instance->blacklisted= time(NULL);
instance->type= AURORA_UNAVAILABLE;
return NULL;
}
/* check if we are slave or master */
switch (aurora_get_instance_type(mysql))
{
case AURORA_PRIMARY:
instance->type= AURORA_PRIMARY;
return mysql;
break;
case AURORA_REPLICA:
instance->type= AURORA_REPLICA;
break;
default:
instance->type= AURORA_UNAVAILABLE;
instance->blacklisted= time(NULL);
return NULL;
}
if (!aurora->primary_id[0])
aurora_get_primary_id(mysql, aurora);
return mysql;
}
/* }}} */
/* {{{ void aurora_copy_mysql() */
void aurora_copy_mysql(MYSQL *from, MYSQL *to)
{
LIST *li_stmt= to->stmts;
for (;li_stmt;li_stmt= li_stmt->next)
{
MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data;
if (stmt->state != MYSQL_STMT_INITTED)
{
stmt->state= MYSQL_STMT_INITTED;
SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
}
}
from->free_me= to->free_me;
from->reconnect= to->reconnect;
from->net.conn_hdlr= to->net.conn_hdlr;
from->stmts= to->stmts;
to->stmts= NULL;
memset(&to->options, 0, sizeof(to->options));
to->free_me= 0;
to->net.conn_hdlr= 0;
mysql_close(to);
*to= *from;
to->net.pvio= from->net.pvio;
to->net.pvio->mysql= to;
from->net.pvio= NULL;
}
/* }}} */
/* {{{ my_bool aurora_find_replica() */
my_bool aurora_find_replica(AURORA *aurora)
{
int valid_instances;
my_bool replica_found= 0;
AURORA_INSTANCE *instance[AURORA_MAX_INSTANCES];
MYSQL mysql;
// struct st_dynamic_array *init_command= aurora->mysql[AURORA_PRIMARY]->options.init_command;
if (aurora->num_instances < 2)
return 0;
mysql_init(&mysql);
mysql.options= aurora->mysql[AURORA_PRIMARY]->options;
/* don't execute init_command on slave */
mysql.net.conn_hdlr= aurora->mysql[AURORA_PRIMARY]->net.conn_hdlr;
valid_instances= aurora_get_valid_instances(aurora, instance);
while (valid_instances && !replica_found)
{
int random_pick= rand() % valid_instances;
if ((aurora_connect_instance(aurora, instance[random_pick], &mysql)))
{
switch (instance[random_pick]->type) {
case AURORA_REPLICA:
if (!aurora->mysql[AURORA_REPLICA])
{
aurora->mysql[AURORA_REPLICA]= mysql_init(NULL);
}
aurora_copy_mysql(&mysql, aurora->mysql[AURORA_REPLICA]);
aurora->active[AURORA_REPLICA]= 1;
return 1;
break;
case AURORA_PRIMARY:
aurora_copy_mysql(&mysql, aurora->mysql[AURORA_PRIMARY]);
aurora->pvio[AURORA_PRIMARY]= aurora->mysql[AURORA_PRIMARY]->net.pvio;
aurora->active[AURORA_PRIMARY]= 1;
continue;
break;
default:
mysql_close(&mysql);
return 0;
break;
}
}
valid_instances= aurora_get_valid_instances(aurora, instance);
}
return 0;
}
/* }}} */
/* {{{ AURORA_INSTANCE aurora_get_primary_id_instance() */
AURORA_INSTANCE *aurora_get_primary_id_instance(AURORA *aurora)
{
int i;
if (!aurora->primary_id[0])
return 0;
for (i=0; i < aurora->num_instances; i++)
{
if (!strncmp(aurora->instance[i].host, aurora->primary_id, strlen(aurora->primary_id)))
return &aurora->instance[i];
}
return NULL;
}
/* }}} */
/* {{{ my_bool aurora_find_primary() */
my_bool aurora_find_primary(AURORA *aurora)
{
int i;
AURORA_INSTANCE *instance= NULL;
MYSQL mysql;
my_bool check_primary= 1;
if (!aurora->num_instances)
return 0;
mysql_init(&mysql);
mysql.options= aurora->mysql[AURORA_PRIMARY]->options;
mysql.net.conn_hdlr= aurora->mysql[AURORA_PRIMARY]->net.conn_hdlr;
for (i=0; i < aurora->num_instances; i++)
{
if (check_primary && aurora->primary_id[0])
{
if ((instance= aurora_get_primary_id_instance(aurora)) &&
aurora_connect_instance(aurora, instance, &mysql) &&
instance->type == AURORA_PRIMARY)
{
aurora_copy_mysql(&mysql, aurora->mysql[AURORA_PRIMARY]);
aurora->active[AURORA_PRIMARY]= 1;
return 1;
}
/* primary id connect failed, don't try again */
aurora->primary_id[0]= 0;
check_primary= 0;
}
if (aurora->instance[i].type != AURORA_UNAVAILABLE)
{
if (aurora_connect_instance(aurora, &aurora->instance[i], &mysql)
&& aurora->instance[i].type == AURORA_PRIMARY)
{
aurora_copy_mysql(&mysql, aurora->mysql[AURORA_PRIMARY]);
aurora->active[AURORA_PRIMARY]= 1;
return 1;
}
}
}
return 0;
}
/* }}} */
/* {{{ void aurora_close_replica() */
void aurora_close_replica(MYSQL *mysql, AURORA *aurora)
{
if (aurora->mysql[AURORA_REPLICA])
{
aurora->mysql[AURORA_REPLICA]->net.pvio->mysql= aurora->mysql[AURORA_REPLICA];
aurora->mysql[AURORA_REPLICA]->net.conn_hdlr= 0;
mysql_close(aurora->mysql[AURORA_REPLICA]);
aurora->pvio[AURORA_REPLICA]= 0;
aurora->mysql[AURORA_REPLICA]= NULL;
}
}
/* }}} */
/* {{{ MYSQL *aurora_connect */
MYSQL *aurora_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd,
const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag)
{
AURORA *aurora= NULL;
MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr;
my_bool is_reconnect= 0;
if ((aurora= (AURORA *)hdlr->data))
{
aurora_refresh_blacklist(aurora);
if (aurora->mysql[aurora->last_instance_type]->net.pvio)
{
SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0);
return NULL;
}
is_reconnect= 1;
}
else
{
if (!(aurora= (AURORA *)my_malloc(sizeof(AURORA), MYF(MY_ZEROFILL))))
{
mysql->methods->set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
return NULL;
}
mysql->net.conn_hdlr->data= (void *)aurora;
aurora->mysql[AURORA_PRIMARY]= mysql;
if (aurora_parse_url(host, aurora))
{
goto error;
}
if (user)
aurora->username= strdup(user);
if (passwd)
aurora->password= strdup(passwd);
if (db)
aurora->database= strdup(db);
aurora->port= port;
aurora->client_flag= client_flag;
aurora->pvio[AURORA_PRIMARY]= aurora->pvio[AURORA_REPLICA]= NULL;
hdlr->data= aurora;
}
/* In case of reconnect, close broken connection first */
if (is_reconnect)
{
DISABLE_AURORA(mysql);
switch (aurora->last_instance_type) {
case AURORA_REPLICA:
aurora_close_replica(mysql, aurora);
aurora->pvio[AURORA_REPLICA]= NULL;
break;
case AURORA_PRIMARY:
/* pvio will be closed in mysql_reconnect() */
aurora->pvio[AURORA_PRIMARY]= NULL;
aurora->primary_id[0]= 0;
break;
}
aurora->active[aurora->last_instance_type]= 0;
}
if (!aurora->active[AURORA_REPLICA])
{
if (aurora_find_replica(aurora))
{
aurora->pvio[AURORA_REPLICA]= aurora->mysql[AURORA_REPLICA]->net.pvio;
aurora->mysql[AURORA_REPLICA]->net.conn_hdlr= mysql->net.conn_hdlr;
}
else
aurora->pvio[AURORA_REPLICA]= NULL;
}
if (!aurora->active[AURORA_PRIMARY])
{
if (aurora_find_primary(aurora))
{
aurora->active[AURORA_PRIMARY]= 1;
aurora->pvio[AURORA_PRIMARY]= aurora->mysql[AURORA_PRIMARY]->net.pvio;
}
}
aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
ENABLE_AURORA(mysql);
return mysql;
error:
aurora_close_memory(aurora);
return NULL;
}
/* }}} */
/* {{{ my_bool aurora_reconnect */
my_bool aurora_reconnect(MYSQL *mysql)
{
AURORA *aurora;
MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr;
my_bool rc= 1;
aurora= (AURORA *)hdlr->data;
DISABLE_AURORA(mysql);
switch (aurora->last_instance_type)
{
case AURORA_REPLICA:
if (!(rc= mysql->methods->reconnect(aurora->mysql[aurora->last_instance_type])))
aurora_switch_connection(mysql, aurora, AURORA_REPLICA);
break;
case AURORA_PRIMARY:
if (!(rc= mysql->methods->reconnect(aurora->mysql[aurora->last_instance_type])))
aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
break;
default:
/* todo: error message */
break;
}
ENABLE_AURORA(mysql);
return rc;
}
/* }}} */
/* {{{ void aurora_close */
void aurora_close(MYSQL *mysql)
{
MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr;
AURORA *aurora= (AURORA *)hdlr->data;
aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
/* if the connection is not active yet, just return */
if (!aurora->active[1])
return;
if (aurora->mysql[AURORA_REPLICA])
{
/* we got options from primary, so don't free it twice */
memset(&aurora->mysql[AURORA_REPLICA]->options, 0, sizeof(mysql->options));
/* connection handler wull be freed in mysql_close() */
aurora->mysql[AURORA_REPLICA]->net.conn_hdlr= 0;
mysql_close(aurora->mysql[AURORA_REPLICA]);
}
if (aurora->mysql[AURORA_PRIMARY])
{
/* connection handler wull be freed in mysql_close() */
aurora->mysql[AURORA_PRIMARY]->net.conn_hdlr= 0;
aurora->mysql[AURORA_PRIMARY]->net.pvio= aurora->pvio[AURORA_PRIMARY];
mysql_close(aurora->mysql[AURORA_PRIMARY]);
}
/*
if (aurora->mysql[AURORA_PRIMARY])
{
aurora->mysql[AURORA_PRIMARY]->net.pvio= aurora->pvio[AURORA_PRIMARY];
aurora->mysql[AURORA_PRIMARY]->net.conn_hdlr= 0;
mysql_close(aurora->mysql[AURORA_PRIMARY]);
}
*/
/* free masrwe information */
aurora_close_memory(aurora);
}
/* }}} */
/* {{{ my_bool is_replica_command */
my_bool is_replica_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;
}
/* }}} */
/* {{{ my_bool is_replica_stmt */
my_bool is_replica_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 aurora_command */
int aurora_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
size_t length, my_bool skipp_check, void *opt_arg)
{
AURORA *aurora= (AURORA *)mysql->net.conn_hdlr->data;
/* if we don't have slave or slave became unavailable root traffic to master */
if (!aurora->mysql[AURORA_REPLICA] || !OPT_HAS_EXT_VAL(mysql, read_only))
{
if (command != COM_INIT_DB)
{
aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
return 0;
}
}
switch(command) {
case COM_INIT_DB:
/* we need to change default database on primary and replica */
if (aurora->mysql[AURORA_REPLICA] && aurora->last_instance_type != AURORA_REPLICA)
{
aurora_switch_connection(mysql, aurora, AURORA_REPLICA);
DISABLE_AURORA(mysql);
mysql_select_db(aurora->mysql[AURORA_REPLICA], arg);
ENABLE_AURORA(mysql);
aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
}
break;
case COM_QUERY:
case COM_STMT_PREPARE:
if (aurora->mysql[AURORA_REPLICA] && aurora->last_instance_type != AURORA_REPLICA)
aurora_switch_connection(mysql, aurora, AURORA_REPLICA);
break;
case COM_STMT_EXECUTE:
case COM_STMT_FETCH:
if (aurora->pvio[AURORA_REPLICA]->mysql->stmts && is_replica_stmt(aurora->pvio[AURORA_REPLICA]->mysql, arg))
{
if (aurora->last_instance_type != AURORA_REPLICA)
aurora_switch_connection(mysql, aurora, AURORA_REPLICA);
}
else
{
if (aurora->last_instance_type != AURORA_PRIMARY)
aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
}
default:
aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
break;
}
return 0;
}
/* }}} */
/* {{{ int aurora_set_options() */
int aurora_set_options(MYSQL *mysql, enum mysql_option option, void *arg)
{
switch(option) {
default:
return -1;
}
}
/* }}} */

View File

@@ -70,7 +70,8 @@ MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ =
repl_connect,
repl_close,
repl_set_options,
repl_command
repl_command,
NULL
};
typedef struct st_conn_repl {
@@ -81,10 +82,20 @@ typedef struct st_conn_repl {
char *url;
char *host[2];
int port[2];
unsigned int current_type;
} REPL_DATA;
#define SET_SLAVE(mysql, data) mysql->net.pvio= data->pvio[MARIADB_SLAVE]
#define SET_MASTER(mysql, data) mysql->net.pvio= data->pvio[MARIADB_MASTER]
#define SET_SLAVE(mysql, data)\
{\
mysql->net.pvio= data->pvio[MARIADB_SLAVE]; \
data->current_type= MARIADB_SLAVE;\
}
#define SET_MASTER(mysql, data)\
{\
mysql->net.pvio= data->pvio[MARIADB_MASTER];\
data->current_type= MARIADB_MASTER;\
}
/* parse url
@@ -108,9 +119,7 @@ my_bool repl_parse_url(const char *url, REPL_DATA *data)
memset(data->host, 0, 2 * sizeof(char *));
memset(data->port, 0, 2 * sizeof(int));
if (data->url)
my_free(data->url);
if (!data->url)
data->url= my_strdup(url, MYF(0));
data->host[MARIADB_MASTER]= p= data->url;
@@ -150,10 +159,11 @@ my_bool repl_parse_url(const char *url, REPL_DATA *data)
{
/* 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],']')))
if (data->host[i][0]== '[' && strchr(data->host[i], ':') && (p= strchr(data->host[i],']')))
{
/* ignore first square bracket */
data->host[i]++;
memmove(data->host[i], data->host[i]+1, strlen(data->host[i]) - 1);
p= strchr(data->host[i],']');
*p= 0;
p++;
}
@@ -176,12 +186,18 @@ MYSQL *repl_connect(MYSQL *mysql, const char *host, const char *user, const char
REPL_DATA *data= NULL;
MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr;
if ((data= (REPL_DATA *)hdlr->data))
{
ma_pvio_close(data->pvio[MARIADB_MASTER]);
data->pvio[MARIADB_MASTER]= 0;
repl_close(mysql);
}
if (!(data= calloc(1, sizeof(REPL_DATA))))
{
mysql->methods->set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
return NULL;
}
memset(data->pvio, 0, 2 * sizeof(MARIADB_PVIO *));
if (repl_parse_url(host, data))
@@ -194,6 +210,7 @@ MYSQL *repl_connect(MYSQL *mysql, const char *host, const char *user, const char
data->pvio[MARIADB_MASTER]= mysql->net.pvio;
hdlr->data= data;
SET_MASTER(mysql, data);
/* to allow immediate access without connection delay, we will start
* connecting to slave(s) in background */
@@ -245,7 +262,6 @@ void repl_close(MYSQL *mysql)
my_free(data->url);
my_free(data);
mysql->net.conn_hdlr->data= NULL;
mysql_close(mysql);
}
static my_bool is_slave_command(const char *buffer, size_t buffer_len)
@@ -295,20 +311,20 @@ int repl_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
case COM_QUERY:
case COM_STMT_PREPARE:
if (is_slave_command(arg, length))
SET_SLAVE(mysql, data);
SET_SLAVE(mysql, data)
else
SET_MASTER(mysql,data);
SET_MASTER(mysql,data)
break;
case COM_STMT_EXECUTE:
case COM_STMT_FETCH:
if (data->pvio[MARIADB_SLAVE]->mysql->stmts && is_slave_stmt(data->pvio[MARIADB_SLAVE]->mysql, arg))
SET_SLAVE(mysql, data);
SET_SLAVE(mysql, data)
else
SET_MASTER(mysql,data);
SET_MASTER(mysql,data)
break;
default:
SET_MASTER(mysql,data);
SET_MASTER(mysql,data)
break;
}
return 0;

View File

@@ -251,7 +251,7 @@ my_bool pvio_npipe_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo)
0, /* no sharing */
NULL, /* default security attributes */
OPEN_EXISTING,
0, /* default attributes */
FILE_FLAG_OVERLAPPED,
NULL)) != INVALID_HANDLE_VALUE)
break;

View File

@@ -21,7 +21,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/unittest/mytap)
ADD_DEFINITIONS(-DLIBMARIADB)
SET(API_TESTS "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs"
SET(API_TESTS "aurora" "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs"
"sp" "result" "connection" "misc" "ps_new" "sqlite3" "thread" "dyncol")
# Get finger print from server certificate
@@ -61,7 +61,7 @@ ENDIF()
FOREACH(API_TEST ${API_TESTS})
ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c)
TARGET_LINK_LIBRARIES(${API_TEST} mytap mariadbclient )
TARGET_LINK_LIBRARIES(${API_TEST} mytap libmariadb)
ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST})
SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 120)
ENDFOREACH(API_TEST)

View File

@@ -0,0 +1,67 @@
/*
*/
#include "my_test.h"
static int aurora1(MYSQL *mysql)
{
int rc;
my_bool read_only= 1;
char *primary, *replica;
MYSQL_RES *res;
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
check_mysql_rc(rc, mysql);
rc= mysql_query(mysql, "CREATE TABLE t1 (a int, b varchar(20))");
check_mysql_rc(rc, mysql);
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1, 'foo'), (2, 'bar')");
check_mysql_rc(rc, mysql);
primary= mysql->host;
diag("primary: %s", primary);
mysql_options(mysql, MARIADB_OPT_CONNECTION_READ_ONLY, &read_only);
/* ensure, that this is a replica, so INSERT should fail */
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (3, 'error')");
if (rc)
diag("Expected error: %s", mysql_error(mysql));
rc= mysql_query(mysql, "SELECT a, b FROM t1");
check_mysql_rc(rc, mysql);
res= mysql_store_result(mysql);
diag("Num_rows: %d", mysql_num_rows(res));
mysql_free_result(res);
replica= mysql->host;
diag("replica: %s", replica);
diag("db: %s", mysql->db);
return OK;
}
struct my_tests_st my_tests[] = {
{"aurora1", aurora1, TEST_CONNECTION_NEW, 0, NULL, NULL},
{NULL, NULL, 0, 0, NULL, NULL}
};
int main(int argc, char **argv)
{
mysql_library_init(0,0,NULL);
if (argc > 1)
get_options(argc, argv);
get_envvars();
run_tests(my_tests);
mysql_server_end();
return(exit_status());
}

View File

@@ -55,13 +55,17 @@ static int test_conc75(MYSQL *my)
{
ulong thread_id= mysql_thread_id(mysql);
/* force reconnect */
mysql->reconnect= 1;
diag("killing connection");
mysql_kill(my, thread_id);
sleep(1);
sleep(2);
mysql_ping(mysql);
rc= mysql_query(mysql, "load data local infile './nonexistingfile.csv' into table a (`a`)");
FAIL_IF(!test(mysql->options.client_flag | CLIENT_LOCAL_FILES), "client_flags not correct");
diag("thread1: %d %d", thread_id, mysql_thread_id(mysql));
FAIL_IF(thread_id == mysql_thread_id(mysql), "new thread id expected");
diag("cs: %s", mysql->charset->csname);
FAIL_IF(strcmp(mysql->charset->csname, "utf8"), "wrong character set");
//diag("cs: %s", mysql->charset->csname);
//FAIL_IF(strcmp(mysql->charset->csname, "utf8"), "wrong character set");
}
mysql_close(mysql);
return OK;
@@ -76,7 +80,12 @@ static int test_conc74(MYSQL *my)
mysql= mysql_init(NULL);
mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS);
if (!mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS))
{
diag("Error: %s", mysql_error(mysql));
mysql_close(mysql);
return FAIL;
}
rc= mysql_query(mysql, "DROP TABLE IF EXISTS a");
check_mysql_rc(rc, mysql);
@@ -128,7 +137,11 @@ static int test_conc70(MYSQL *my)
int rc;
MYSQL_RES *res;
MYSQL_ROW row;
MYSQL *mysql= mysql_init(NULL);
MYSQL *mysql;
SKIP_CONNECTION_HANDLER;
mysql= mysql_init(NULL);
rc= mysql_query(my, "SET @a:=@@max_allowed_packet");
check_mysql_rc(rc, my);
@@ -148,6 +161,14 @@ static int test_conc70(MYSQL *my)
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (REPEAT('A', 1024 * 1024 * 20))");
check_mysql_rc(rc, mysql);
if (mysql_warning_count(mysql))
{
diag("server doesn't accept package size");
return SKIP;
}
sleep(20);
rc= mysql_query(mysql, "SELECT a FROM t1");
check_mysql_rc(rc, mysql);
@@ -175,7 +196,11 @@ static int test_conc68(MYSQL *my)
int rc;
MYSQL_RES *res;
MYSQL_ROW row;
MYSQL *mysql= mysql_init(NULL);
MYSQL *mysql;
SKIP_CONNECTION_HANDLER;
mysql= mysql_init(NULL);
rc= mysql_query(my, "SET @a:=@@max_allowed_packet");
check_mysql_rc(rc, my);
@@ -193,6 +218,11 @@ static int test_conc68(MYSQL *my)
rc= mysql_query(mysql, "INSERT INTO t1 VALUES (REPEAT('A', 1024 * 1024 * 20))");
check_mysql_rc(rc, mysql);
if (mysql_warning_count(mysql))
{
diag("server doesn't accept package size");
return SKIP;
}
rc= mysql_query(mysql, "SELECT a FROM t1");
check_mysql_rc(rc, mysql);
@@ -504,6 +534,7 @@ static int test_mysql_insert_id(MYSQL *mysql)
according to the manual, this might be 20 or 300, but it looks like
auto_increment column takes priority over last_insert_id().
*/
diag("res: %d", res);
FAIL_UNLESS(res == 20, "");
/* If first autogenerated number fails and 2nd works: */
rc= mysql_query(mysql, "drop table t2");
@@ -618,12 +649,9 @@ static int bug_conc1(MYSQL *mysql)
{
mysql_real_connect(mysql, hostname, username, password, schema,
port, socketname, 0);
diag("errno: %d", mysql_errno(mysql));
FAIL_IF(mysql_errno(mysql) != CR_ALREADY_CONNECTED,
"Expected errno=CR_ALREADY_CONNECTED");
FAIL_IF(strcmp(mysql_error(mysql), ER(CR_ALREADY_CONNECTED)) != 0,
"Wrong error message");
FAIL_IF(strcmp(ER(CR_ALREADY_CONNECTED), "Can't connect twice. Already connected") != 0,
"wrong error message");
return OK;
}
@@ -668,11 +696,14 @@ static int test_reconnect_maxpackage(MYSQL *my)
{
int rc;
ulong max_packet= 0;
MYSQL *mysql= mysql_init(NULL);
MYSQL *mysql;
MYSQL_RES *res;
MYSQL_ROW row;
char *query;
SKIP_CONNECTION_HANDLER;
mysql= mysql_init(NULL);
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
port, socketname,
CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS), mysql_error(mysql));
@@ -770,6 +801,8 @@ int main(int argc, char **argv)
get_envvars();
diag("user: %s", username);
run_tests(my_tests);
return(exit_status());

View File

@@ -679,7 +679,7 @@ static int test_utf16_utf32_noboms(MYSQL *mysql)
for (i= 0; i < sizeof(csname)/sizeof(char*); ++i)
{
csinfo[i]= mysql_find_charset_name(csname[i]);
csinfo[i]= mariadb_get_charset_by_name(csname[i]);
if (csinfo[i] == NULL)
{

View File

@@ -32,6 +32,7 @@ static int test_conc66(MYSQL *my)
MYSQL *mysql= mysql_init(NULL);
int rc;
FILE *fp;
char query[1024];
if (!(fp= fopen("./my.cnf", "w")))
return FAIL;
@@ -47,7 +48,8 @@ static int test_conc66(MYSQL *my)
rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./my.cnf");
check_mysql_rc(rc, mysql);
rc= mysql_query(my, "GRANT ALL ON test.* TO 'conc66'@'localhost' IDENTIFIED BY 'test\";#test'");
sprintf(query, "GRANT ALL ON %s.* TO 'conc66'@'%s' IDENTIFIED BY 'test\";#test'", schema, hostname);
rc= mysql_query(my, query);
check_mysql_rc(rc, my);
rc= mysql_query(my, "FLUSH PRIVILEGES");
check_mysql_rc(rc, my);
@@ -57,7 +59,8 @@ static int test_conc66(MYSQL *my)
diag("Error: %s", mysql_error(mysql));
return FAIL;
}
rc= mysql_query(my, "DROP USER conc66@localhost");
sprintf(query, "DROP user conc66@%s", hostname);
rc= mysql_query(my, query);
check_mysql_rc(rc, my);
mysql_close(mysql);
@@ -561,6 +564,8 @@ static int test_reconnect(MYSQL *mysql)
mysql_kill(mysql, mysql_thread_id(mysql1));
sleep(4);
mysql_ping(mysql1);
rc= mysql_query(mysql1, "SELECT 1 FROM DUAL LIMIT 0");
check_mysql_rc(rc, mysql1);
diag("Thread_id after kill: %lu", mysql_thread_id(mysql1));
@@ -649,6 +654,8 @@ static int test_conc118(MYSQL *mysql)
rc= mysql_kill(mysql, mysql_thread_id(mysql));
sleep(2);
mysql_ping(mysql);
rc= mysql_query(mysql, "SET @a:=1");
check_mysql_rc(rc, mysql);
@@ -657,12 +664,9 @@ static int test_conc118(MYSQL *mysql)
rc= mysql_kill(mysql, mysql_thread_id(mysql));
sleep(2);
mysql->host= "foo";
rc= mysql_query(mysql, "SET @a:=1");
FAIL_IF(!rc, "error expected");
mysql->host= hostname;
rc= mysql_query(mysql, "SET @a:=1");
check_mysql_rc(rc, mysql);

View File

@@ -82,6 +82,13 @@ if (!(expr))\
return FAIL;\
}
#define SKIP_CONNECTION_HANDLER \
if (hostname && strstr(hostname, "://"))\
{\
diag("Test skipped (connection handler)");\
return SKIP;\
}
/* connection options */
#define TEST_CONNECTION_DEFAULT 1 /* default connection */
#define TEST_CONNECTION_NONE 2 /* tests creates own connection */
@@ -104,7 +111,7 @@ struct my_tests_st
char *skipmsg;
};
static char *schema = "test_c";
static char *schema = 0;
static char *hostname = 0;
static char *password = 0;
static unsigned int port = 0;
@@ -368,18 +375,19 @@ int check_variable(MYSQL *mysql, char *variable, char *value)
MYSQL *test_connect(struct my_tests_st *test) {
MYSQL *mysql;
char query[255];
int i= 1;
int i= 0;
int timeout= 10;
int truncation_report= 1;
if (!(mysql = mysql_init(NULL))) {
diag("%s", "mysql_init failed - exiting");
return(NULL);
}
mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, &i);
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&i);
mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, &truncation_report);
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
/* option handling */
if (test && test->options) {
int i=0;
while (test->options[i].option)
{
@@ -403,6 +411,8 @@ MYSQL *test_connect(struct my_tests_st *test) {
/* change database or create if it doesn't exist */
if (mysql_select_db(mysql, schema)) {
diag("Error number: %d", mysql_errno(mysql));
if(mysql_errno(mysql) == 1049) {
sprintf(query, "CREATE DATABASE %s", schema);
if (mysql_query(mysql, query)) {
@@ -425,11 +435,6 @@ static int reset_connection(MYSQL *mysql) {
rc= mysql_change_user(mysql, username, password, schema);
check_mysql_rc(rc, mysql);
if (mysql_get_server_version(mysql) < 50400)
rc= mysql_query(mysql, "SET table_type='MyISAM'");
else
rc= mysql_query(mysql, "SET storage_engine='MyISAM'");
check_mysql_rc(rc, mysql);
rc= mysql_query(mysql, "SET sql_mode=''");
check_mysql_rc(rc, mysql);
@@ -452,6 +457,8 @@ void get_envvars() {
password= envvar;
if (!schema && (envvar= getenv("MYSQL_TEST_DB")))
schema= envvar;
if (!schema)
schema= "testc";
if (!port && (envvar= getenv("MYSQL_TEST_PORT")))
port= atoi(envvar);
if (!socketname && (envvar= getenv("MYSQL_TEST_SOCKET")))
@@ -470,7 +477,8 @@ void run_tests(struct my_tests_st *test) {
if ((mysql_default= test_connect(NULL)))
{
diag("Testing against MySQL Server %s", mysql_get_server_info(mysql_default));
diag("Host %s", mysql_get_host_info(mysql_default));
diag("Host: %s", mysql_get_host_info(mysql_default));
diag("Client library: %s", mysql_get_client_info());
}
else
{

View File

@@ -64,7 +64,10 @@ static int test_conc83(MYSQL *my)
/* 1. Status is inited, so prepare should work */
rc= mysql_kill(mysql, mysql_thread_id(mysql));
sleep(2);
sleep(5);
rc= mysql_ping(mysql);
check_mysql_rc(rc, mysql);
rc= mysql_stmt_prepare(stmt, query, strlen(query));
check_stmt_rc(rc, stmt);

View File

@@ -528,9 +528,8 @@ static int test_bug12744(MYSQL *mysql)
rc= mysql_options(mysql, MYSQL_OPT_RECONNECT, "1");
check_mysql_rc(rc, mysql);
rc= mysql_kill(mysql, mysql_thread_id(mysql));
check_mysql_rc(rc, mysql);
sleep(2);
sleep(4);
rc= mysql_ping(mysql);
check_mysql_rc(rc, mysql);