Merge branch 'master' into blr

This commit is contained in:
Mark Riddoch
2014-05-19 13:46:47 +01:00
parent 29d247319d
commit 9777e83a5a
50 changed files with 829 additions and 553 deletions

Binary file not shown.

2
README
View File

@ -35,7 +35,7 @@ Bugs can be reported in the SkySQL bugs database
Edit the file build_gateway.inc in your MaxScale directory and set Edit the file build_gateway.inc in your MaxScale directory and set
the ROOT_PATH to the directory in which you have installed the the ROOT_PATH to the directory in which you have installed the
MaxScale source code. Set the MYSQL_ROOT/MYSQL_HEADERS variables MaxScale source code. Set the INC_PATH/MYSQL_ROOT/MYSQL_HEADERS variables
to the location in which you have installed the developer package to the location in which you have installed the developer package
for MariaDB or checked out the source code of MariaDB and the for MariaDB or checked out the source code of MariaDB and the
location of your MariaDB include files. location of your MariaDB include files.

View File

@ -1 +1 @@
0.5.0 0.6.0

View File

@ -2,67 +2,52 @@
# This file includes all dynamically changing build-related # This file includes all dynamically changing build-related
# variables. # variables.
# #
# Current values are to satisfy MaxScale build process.
#
# Modify to match with your needs. Do not commit any private # Modify to match with your needs. Do not commit any private
# changes to this file! # changes to this file!
# #
#
# #
# Use debug flags # Set debug flags
# Values : <empty> | Y
# #
DEBUG := DEBUG :=
# #
# Set build env # Set build env
# Values : <empty> | Y
# #
UNIX := Y UNIX := Y
# #
# Set MaxScale branch directory # Set MaxScale branch directory
# Example := $(HOME)/src/MaxScale
# #
ROOT_PATH := ROOT_PATH := $(HOME)/src/bazaar/tmp/maxscale
#
# Main directory for headers
# Example := $(HOME)/usr/include
#
INC_PATH :=
INC_PATH := $(HOME)/usr/include
# #
# Set prefix to MySQL Resources # Set prefix to MySQL Resources
# Example := $(INC_PATH)/mysql
# #
MYSQL_ROOT := MYSQL_ROOT := $(INC_PATH)/mysql
# #
# Set prefix of the path to development headers # Set prefix of the path to development headers
# Example := -I$(INC_PATH) -I$(MYSQL_ROOT)/ -I$(MYSQL_ROOT)/private/ -I$(MYSQL_ROOT)/extra/
# #
MYSQL_HEADERS := MYSQL_HEADERS := -I$(INC_PATH) -I$(MYSQL_ROOT)/ -I$(MYSQL_ROOT)/private/ -I$(MYSQL_ROOT)/extra/
# #
# Set DYNLIB=Y if you want to link MaxScale with dynamic embedded lib # Set DYNLIB=Y if you want to link MaxScale with dynamic embedded lib
# Values : <empty> | Y
# #
DYNLIB := DYNLIB :=
# #
# Set path to Embedded MySQL Server # Set path to Embedded MySQL Server
# Example
# EMBEDDED_LIB := $(HOME)/usr/lib64
# ifdef DYNLIB
# EMBEDDED_LIB := $(HOME)/usr/lib64/dynlib
# endif
# #
EMBEDDED_LIB := EMBEDDED_LIB := $(HOME)/usr/lib64
ifdef DYNLIB ifdef DYNLIB
EMBEDDED_LIB := EMBEDDED_LIB := $(HOME)/usr/lib64/dynlib
endif endif
# #
# Set path to MySQL errors file # Set path to MySQL errors file
# Example := $(HOME)/usr/share/mysql
# #
ERRMSG := ERRMSG := $(HOME)/usr/share/mysql

View File

@ -253,6 +253,7 @@ static int logmanager_write_log(
va_list valist); va_list valist);
static blockbuf_t* blockbuf_init(logfile_id_t id); static blockbuf_t* blockbuf_init(logfile_id_t id);
static void blockbuf_node_done(void* bb_data);
static char* blockbuf_get_writepos( static char* blockbuf_get_writepos(
#if 0 #if 0
int** refcount, int** refcount,
@ -997,7 +998,12 @@ static char* blockbuf_get_writepos(
return pos; return pos;
} }
static void blockbuf_node_done(
void* bb_data)
{
blockbuf_t* bb = (blockbuf_t *)bb_data;
simple_mutex_done(&bb->bb_mutex);
}
static blockbuf_t* blockbuf_init( static blockbuf_t* blockbuf_init(
@ -2059,7 +2065,7 @@ static bool logfile_init(
if (mlist_init(&logfile->lf_blockbuf_list, if (mlist_init(&logfile->lf_blockbuf_list,
NULL, NULL,
strdup("logfile block buffer list"), strdup("logfile block buffer list"),
NULL, blockbuf_node_done,
MAXNBLOCKBUFS) == NULL) MAXNBLOCKBUFS) == NULL)
{ {
ss_dfprintf(stderr, ss_dfprintf(stderr,

View File

@ -5,6 +5,7 @@
include ../../build_gateway.inc include ../../build_gateway.inc
include ../../makefile.inc include ../../makefile.inc
include ../../test.inc
CC = gcc CC = gcc
CPP = g++ CPP = g++
@ -43,7 +44,7 @@ buildtests:
runtests: runtests:
@echo "" >> $(TESTLOG) @echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG) @echo $(shell date) >> $(TESTLOG)
@echo "Test Log Manager" >> $(TESTLOG) @echo "Test Log Manager" >> $(TESTLOG)
@ -62,3 +63,5 @@ runtests:
@-$(LAUNCH_DEBUGGER) $(TESTAPP) "-t 16" 2>>$(TESTLOG) @-$(LAUNCH_DEBUGGER) $(TESTAPP) "-t 16" 2>>$(TESTLOG)
@echo "Log Manager PASSED" >> $(TESTLOG) @echo "Log Manager PASSED" >> $(TESTLOG)
@echo "" >> $(TESTLOG) @echo "" >> $(TESTLOG)
# @cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -25,10 +25,10 @@ MaxScale
%build %build
ln -s /lib64/libaio.so.1 /lib64/libaio.so ln -s /lib64/libaio.so.1 /lib64/libaio.so
make ROOT_PATH=`pwd` HOME="" clean make ROOT_PATH=`pwd` HOME="" $DEBUG_FLAG1 $DEBUG_FLAG2 clean
make ROOT_PATH=`pwd` HOME="" depend make ROOT_PATH=`pwd` HOME="" $DEBUG_FLAG1 $DEBUG_FLAG2 depend
make ROOT_PATH=`pwd` HOME="" make ROOT_PATH=`pwd` HOME="" $DEBUG_FLAG1 $DEBUG_FLAG2
make DEST=`pwd`/binaries ROOT_PATH=`pwd` HOME="" ERRMSG="/usr/share/mysql/english" install make DEST=`pwd`/binaries ROOT_PATH=`pwd` HOME="" ERRMSG="/usr/share/mysql/english" $DEBUG_FLAG1 $DEBUG_FLAG2 install
%post %post
ln -s /lib64/libaio.so.1 /lib64/libaio.so ln -s /lib64/libaio.so.1 /lib64/libaio.so

View File

@ -50,11 +50,13 @@
#include <sql_parse.h> #include <sql_parse.h>
#include <errmsg.h> #include <errmsg.h>
#include <client_settings.h> #include <client_settings.h>
#include <set_var.h>
#include <strfunc.h>
#include <item_func.h> #include <item_func.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <stdarg.h> #include <stdarg.h>
extern int lm_enabled_logfiles_bitmask; extern int lm_enabled_logfiles_bitmask;
@ -73,9 +75,13 @@ static bool create_parse_tree(
static skygw_query_type_t resolve_query_type( static skygw_query_type_t resolve_query_type(
THD* thd); THD* thd);
static bool skygw_stmt_causes_implicit_commit( static bool skygw_stmt_causes_implicit_commit(
LEX* lex, LEX* lex,
uint mask); int* autocommit_stmt);
static int is_autocommit_stmt(
LEX* lex);
/** /**
* @node (write brief function description here) * @node (write brief function description here)
@ -346,9 +352,9 @@ return_here:
* restrictive, for example, QUERY_TYPE_READ is smaller than QUERY_TYPE_WRITE. * restrictive, for example, QUERY_TYPE_READ is smaller than QUERY_TYPE_WRITE.
* *
*/ */
static u_int8_t set_query_type( static u_int16_t set_query_type(
u_int8_t* qtype, u_int16_t* qtype,
u_int8_t new_type) u_int16_t new_type)
{ {
*qtype = MAX(*qtype, new_type); *qtype = MAX(*qtype, new_type);
return *qtype; return *qtype;
@ -374,7 +380,8 @@ static skygw_query_type_t resolve_query_type(
THD* thd) THD* thd)
{ {
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN; skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
u_int8_t type = QUERY_TYPE_UNKNOWN; u_int16_t type = QUERY_TYPE_UNKNOWN;
int set_autocommit_stmt = -1; /*< -1 no, 0 disable, 1 enable */
LEX* lex; LEX* lex;
Item* item; Item* item;
/** /**
@ -397,7 +404,9 @@ static skygw_query_type_t resolve_query_type(
goto return_qtype; goto return_qtype;
} }
if (skygw_stmt_causes_implicit_commit(lex, CF_AUTO_COMMIT_TRANS)) if (skygw_stmt_causes_implicit_commit(
lex,
&set_autocommit_stmt))
{ {
if (LOG_IS_ENABLED(LOGFILE_TRACE)) if (LOG_IS_ENABLED(LOGFILE_TRACE))
{ {
@ -418,8 +427,26 @@ static skygw_query_type_t resolve_query_type(
"next command."); "next command.");
} }
} }
if (set_autocommit_stmt == 1)
{
type |= QUERY_TYPE_ENABLE_AUTOCOMMIT;
}
type |= QUERY_TYPE_COMMIT; type |= QUERY_TYPE_COMMIT;
} }
if (set_autocommit_stmt == 0)
{
if (LOG_IS_ENABLED(LOGFILE_TRACE))
{
skygw_log_write(
LOGFILE_TRACE,
"Disable autocommit : implicit START TRANSACTION"
" before executing the next command.");
}
type |= QUERY_TYPE_DISABLE_AUTOCOMMIT;
type |= QUERY_TYPE_BEGIN_TRX;
}
/** /**
* REVOKE ALL, ASSIGN_TO_KEYCACHE, * REVOKE ALL, ASSIGN_TO_KEYCACHE,
* PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER * PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER
@ -648,11 +675,18 @@ return_qtype:
return qtype; return qtype;
} }
static bool skygw_stmt_causes_implicit_commit(LEX* lex, uint mask) /**
* Checks if statement causes implicit COMMIT.
* autocommit_stmt gets values 1, 0 or -1 if stmt is enable, disable or
* something else than autocommit.
*/
static bool skygw_stmt_causes_implicit_commit(
LEX* lex,
int* autocommit_stmt)
{ {
bool succp; bool succp;
if (!(sql_command_flags[lex->sql_command] & mask)) if (!(sql_command_flags[lex->sql_command] & CF_AUTO_COMMIT_TRANS))
{ {
succp = false; succp = false;
goto return_succp; goto return_succp;
@ -668,13 +702,86 @@ static bool skygw_stmt_causes_implicit_commit(LEX* lex, uint mask)
succp = !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE); succp = !(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);
break; break;
case SQLCOM_SET_OPTION: case SQLCOM_SET_OPTION:
succp = lex->autocommit ? true : false; if ((*autocommit_stmt = is_autocommit_stmt(lex)) == 1)
{
succp = true;
}
else
{
succp =false;
}
break; break;
default: default:
succp = true; succp = true;
break; break;
} }
return_succp: return_succp:
return succp; return succp;
} }
/**
* Finds out if stmt is SET autocommit
* and if the new value matches with the enable_cmd argument.
*
* Returns 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;
}

View File

@ -29,15 +29,17 @@ EXTERN_C_BLOCK_BEGIN
* is modified * is modified
*/ */
typedef enum { typedef enum {
QUERY_TYPE_UNKNOWN = 0, /*< Couln't find out or parse error */ QUERY_TYPE_UNKNOWN = 0x000, /*< Initial value, can't be tested bitwisely */
QUERY_TYPE_LOCAL_READ = (1<<0), /*< Read non-database data, execute in MaxScale */ QUERY_TYPE_LOCAL_READ = 0x001, /*< Read non-database data, execute in MaxScale */
QUERY_TYPE_READ = (1<<1), /*< No updates */ QUERY_TYPE_READ = 0x002, /*< No updates */
QUERY_TYPE_WRITE = (1<<2), /*< Master data will be modified */ QUERY_TYPE_WRITE = 0x004, /*< Master data will be modified */
QUERY_TYPE_SESSION_WRITE = (1<<3), /*< Session data will be modified */ QUERY_TYPE_SESSION_WRITE = 0x008, /*< Session data will be modified */
QUERY_TYPE_GLOBAL_WRITE = (1<<4), /*< Global system variable modification */ QUERY_TYPE_GLOBAL_WRITE = 0x010, /*< Global system variable modification */
QUERY_TYPE_BEGIN_TRX = (1<<5), /*< BEGIN or START TRANSACTION */ QUERY_TYPE_BEGIN_TRX = 0x020, /*< BEGIN or START TRANSACTION */
QUERY_TYPE_ROLLBACK = (1<<6), /*< ROLLBACK */ QUERY_TYPE_ENABLE_AUTOCOMMIT = 0x040,/*< SET autocommit=1 */
QUERY_TYPE_COMMIT = (1<<7), /*< COMMIT */ QUERY_TYPE_DISABLE_AUTOCOMMIT = 0x080,/*< SET autocommit=0 */
QUERY_TYPE_ROLLBACK = 0x100, /*< ROLLBACK */
QUERY_TYPE_COMMIT = 0x200 /*< COMMIT */
} skygw_query_type_t; } skygw_query_type_t;
#define QUERY_IS_TYPE(mask,type) ((mask & type) == type) #define QUERY_IS_TYPE(mask,type) ((mask & type) == type)

View File

@ -5,6 +5,7 @@
include ../../build_gateway.inc include ../../build_gateway.inc
include ../../makefile.inc include ../../makefile.inc
include ../../test.inc
CC = gcc CC = gcc
CPP = g++ CPP = g++
@ -48,7 +49,7 @@ buildtests:
$(LDLIBS) $(LDMYSQL) $(LDLIBS) $(LDMYSQL)
runtests: runtests:
@echo "" >> $(TESTLOG) @echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG) @echo $(shell date) >> $(TESTLOG)
@echo "Test Query Classifier" >> $(TESTLOG) @echo "Test Query Classifier" >> $(TESTLOG)
@ -59,3 +60,4 @@ ifeq ($?, 0)
else else
@echo "Query Classifier FAILED" >> $(TESTLOG) @echo "Query Classifier FAILED" >> $(TESTLOG)
endif endif
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -37,6 +37,8 @@ passwd=maxpwd
# user=<User to fetch password inforamtion with> # user=<User to fetch password inforamtion with>
# passwd=<Password of the user, plain text currently> # passwd=<Password of the user, plain text currently>
# enable_root_user=<0 or 1, default is 0> # enable_root_user=<0 or 1, default is 0>
# version_string=<specific string for server handshake,
# default is the MariaDB embedded library version>
# #
# Valid router modules currently are: # Valid router modules currently are:
# readwritesplit, readconnroute and debugcli # readwritesplit, readconnroute and debugcli

Binary file not shown.

View File

@ -29,6 +29,7 @@
* 06/02/14 Massimiliano Pinto Added support for enable/disable root user in services * 06/02/14 Massimiliano Pinto Added support for enable/disable root user in services
* 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list * 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list
* 11/03/14 Massimiliano Pinto Added Unix socket support * 11/03/14 Massimiliano Pinto Added Unix socket support
* 11/05/14 Massimiliano Pinto Added version_string support to service
* *
* @endverbatim * @endverbatim
*/ */
@ -43,6 +44,7 @@
#include <monitor.h> #include <monitor.h>
#include <skygw_utils.h> #include <skygw_utils.h>
#include <log_manager.h> #include <log_manager.h>
#include <mysql.h>
extern int lm_enabled_logfiles_bitmask; extern int lm_enabled_logfiles_bitmask;
@ -56,6 +58,7 @@ static void check_config_objects(CONFIG_CONTEXT *context);
static char *config_file = NULL; static char *config_file = NULL;
static GATEWAY_CONF gateway; static GATEWAY_CONF gateway;
char *version_string = NULL;
/** /**
* Config item handler for the ini file reader * Config item handler for the ini file reader
@ -116,6 +119,21 @@ config_load(char *file)
CONFIG_CONTEXT config; CONFIG_CONTEXT config;
int rval; int rval;
MYSQL *conn;
conn = mysql_init(NULL);
if (conn) {
if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0)) {
char *ptr;
version_string = (char *)mysql_get_server_info(conn);
ptr = strstr(version_string, "-embedded");
if (ptr) {
*ptr = '\0';
}
}
mysql_close(conn);
}
global_defaults(); global_defaults();
config.object = ""; config.object = "";
@ -146,6 +164,11 @@ int rval;
if (!config_file) if (!config_file)
return 0; return 0;
if (gateway.version_string)
free(gateway.version_string);
global_defaults(); global_defaults();
config.object = ""; config.object = "";
@ -203,6 +226,28 @@ int error_count = 0;
char *enable_root_user = char *enable_root_user =
config_get_value(obj->parameters, "enable_root_user"); config_get_value(obj->parameters, "enable_root_user");
char *version_string = config_get_value(obj->parameters, "version_string");
if (version_string) {
((SERVICE *)(obj->element))->version_string = strdup(version_string);
} else {
if (gateway.version_string)
((SERVICE *)(obj->element))->version_string = strdup(gateway.version_string);
}
if (obj->element == NULL) /*< if module load failed */
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Reading configuration "
"for router service '%s' failed. "
"Router %s is not loaded.",
obj->object,
obj->object)));
obj = obj->next;
continue; /*< process next obj */
}
if (enable_root_user) if (enable_root_user)
serviceEnableRootUser(obj->element, atoi(enable_root_user)); serviceEnableRootUser(obj->element, atoi(enable_root_user));
@ -580,6 +625,10 @@ static void
global_defaults() global_defaults()
{ {
gateway.n_threads = 1; gateway.n_threads = 1;
if (version_string != NULL)
gateway.version_string = strdup(version_string);
else
gateway.version_string = NULL;
} }
/** /**
@ -622,6 +671,7 @@ SERVER *server;
char *user; char *user;
char *auth; char *auth;
char *enable_root_user; char *enable_root_user;
char *version_string;
enable_root_user = config_get_value(obj->parameters, "enable_root_user"); enable_root_user = config_get_value(obj->parameters, "enable_root_user");
@ -629,6 +679,15 @@ SERVER *server;
"user"); "user");
auth = config_get_value(obj->parameters, auth = config_get_value(obj->parameters,
"passwd"); "passwd");
version_string = config_get_value(obj->parameters, "version_string");
if (version_string) {
if (service->version_string)
free(service->version_string);
service->version_string = strdup(version_string);
}
if (user && auth) { if (user && auth) {
service_update(service, router, service_update(service, router,
user, user,
@ -861,6 +920,7 @@ static char *service_params[] =
"user", "user",
"passwd", "passwd",
"enable_root_user", "enable_root_user",
"version_string",
NULL NULL
}; };

View File

@ -308,6 +308,13 @@ dcb_final_free(DCB *dcb)
free(dcb->data); free(dcb->data);
if (dcb->remote) if (dcb->remote)
free(dcb->remote); free(dcb->remote);
/* Consume dcb->delayq buffer */
if (dcb->delayq) {
GWBUF *queue = dcb->delayq;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
}
bitmask_free(&dcb->memdata.bitmask); bitmask_free(&dcb->memdata.bitmask);
simple_mutex_done(&dcb->dcb_read_lock); simple_mutex_done(&dcb->dcb_read_lock);
simple_mutex_done(&dcb->dcb_write_lock); simple_mutex_done(&dcb->dcb_write_lock);

View File

@ -53,6 +53,17 @@ static void register_module(const char *module,
void *modobj); void *modobj);
static void unregister_module(const char *module); static void unregister_module(const char *module);
char* get_maxscale_home(void)
{
char* home = getenv("MAXSCALE_HOME");
if (home == NULL)
{
home = "/usr/local/skysql/MaxScale";
}
return home;
}
/** /**
* Load the dynamic library related to a gateway module. The routine * Load the dynamic library related to a gateway module. The routine
* will look for library files in the current directory, * will look for library files in the current directory,
@ -82,9 +93,9 @@ MODULES *mod;
sprintf(fname, "./lib%s.so", module); sprintf(fname, "./lib%s.so", module);
if (access(fname, F_OK) == -1) if (access(fname, F_OK) == -1)
{ {
if ((home = getenv("MAXSCALE_HOME")) == NULL) home = get_maxscale_home ();
home = "/usr/local/skysql/MaxScale";
sprintf(fname, "%s/modules/lib%s.so", home, module); sprintf(fname, "%s/modules/lib%s.so", home, module);
if (access(fname, F_OK) == -1) if (access(fname, F_OK) == -1)
{ {
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
@ -100,7 +111,7 @@ MODULES *mod;
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"Error : Unable to load library for module: " "Error : Unable to load library for module: "
"%s, %s.", "%s\n\t\t\t %s.",
module, module,
dlerror()))); dlerror())));
return NULL; return NULL;
@ -111,7 +122,7 @@ MODULES *mod;
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"Error : Version interface not supported by " "Error : Version interface not supported by "
"module: %s, %s.", "module: %s\n\t\t\t %s.",
module, module,
dlerror()))); dlerror())));
dlclose(dlhandle); dlclose(dlhandle);
@ -134,7 +145,7 @@ MODULES *mod;
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"Error : Expected entry point interface missing " "Error : Expected entry point interface missing "
"from module: %s, %s.", "from module: %s\n\t\t\t %s.",
module, module,
dlerror()))); dlerror())));
dlclose(dlhandle); dlclose(dlhandle);

View File

@ -28,12 +28,15 @@
* 06/02/14 Massimiliano Pinto Added: serviceEnableRootUser routine * 06/02/14 Massimiliano Pinto Added: serviceEnableRootUser routine
* 25/02/14 Massimiliano Pinto Added: service refresh limit feature * 25/02/14 Massimiliano Pinto Added: service refresh limit feature
* 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services) * 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services)
* 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL
* *
* @endverbatim * @endverbatim
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <errno.h>
#include <session.h> #include <session.h>
#include <service.h> #include <service.h>
#include <server.h> #include <server.h>
@ -70,11 +73,26 @@ SERVICE *service;
return NULL; return NULL;
if ((service->router = load_module(router, MODULE_ROUTER)) == NULL) if ((service->router = load_module(router, MODULE_ROUTER)) == NULL)
{ {
char* home = get_maxscale_home();
char* ldpath = getenv("LD_LIBRARY_PATH");
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Unable to load %s module \"%s\".\n\t\t\t"
" Ensure that lib%s.so exists in one of the "
"following directories :\n\t\t\t "
"- %s/modules\n\t\t\t - %s",
MODULE_ROUTER,
router,
router,
home,
ldpath)));
free(service); free(service);
return NULL; return NULL;
} }
service->name = strdup(servname); service->name = strdup(servname);
service->routerModule = strdup(router); service->routerModule = strdup(router);
service->version_string = NULL;
memset(&service->stats, 0, sizeof(SERVICE_STATS)); memset(&service->stats, 0, sizeof(SERVICE_STATS));
service->ports = NULL; service->ports = NULL;
service->stats.started = time(0); service->stats.started = time(0);
@ -625,7 +643,7 @@ SERVICE *ptr;
/** /**
* Print all services to a DCB * Print all services to a DCB
* *
* Designed to be called within a debugger session in order * Designed to be called within a CLI command in order
* to display all active services within the gateway * to display all active services within the gateway
*/ */
void void
@ -637,15 +655,30 @@ SERVICE *ptr;
ptr = allServices; ptr = allServices;
while (ptr) while (ptr)
{ {
SERVER *server = ptr->databases; dprintService(dcb, ptr);
dcb_printf(dcb, "Service %p\n", ptr); ptr = ptr->next;
dcb_printf(dcb, "\tService: %s\n", ptr->name); }
dcb_printf(dcb, "\tRouter: %s (%p)\n", ptr->routerModule, spinlock_release(&service_spin);
ptr->router); }
if (ptr->router)
ptr->router->diagnostics(ptr->router_instance, dcb); /**
* Print details of a single service.
*
* @param dcb DCB to print data to
* @param service The service to print
*/
dprintService(DCB *dcb, SERVICE *service)
{
SERVER *server = service->databases;
dcb_printf(dcb, "Service %p\n", service);
dcb_printf(dcb, "\tService: %s\n", service->name);
dcb_printf(dcb, "\tRouter: %s (%p)\n", service->routerModule,
service->router);
if (service->router)
service->router->diagnostics(service->router_instance, dcb);
dcb_printf(dcb, "\tStarted: %s", dcb_printf(dcb, "\tStarted: %s",
asctime(localtime(&ptr->stats.started))); asctime(localtime(&service->stats.started)));
dcb_printf(dcb, "\tBackend databases\n"); dcb_printf(dcb, "\tBackend databases\n");
while (server) while (server)
{ {
@ -653,12 +686,9 @@ SERVICE *ptr;
server->protocol); server->protocol);
server = server->nextdb; server = server->nextdb;
} }
dcb_printf(dcb, "\tUsers data: %p\n", ptr->users); dcb_printf(dcb, "\tUsers data: %p\n", service->users);
dcb_printf(dcb, "\tTotal connections: %d\n", ptr->stats.n_sessions); dcb_printf(dcb, "\tTotal connections: %d\n", service->stats.n_sessions);
dcb_printf(dcb, "\tCurrently connected: %d\n", ptr->stats.n_current); dcb_printf(dcb, "\tCurrently connected: %d\n", service->stats.n_current);
ptr = ptr->next;
}
spinlock_release(&service_spin);
} }
/** /**

View File

@ -5,6 +5,7 @@
include ../../../build_gateway.inc include ../../../build_gateway.inc
include ../../../makefile.inc include ../../../makefile.inc
include ../../../test.inc
CC=cc CC=cc
TESTLOG := $(shell pwd)/testhash.log TESTLOG := $(shell pwd)/testhash.log
@ -26,7 +27,7 @@ buildtests :
testhash.c ../hashtable.o ../atomic.o ../spinlock.o -o testhash testhash.c ../hashtable.o ../atomic.o ../spinlock.o -o testhash
runtests: runtests:
@echo "" >> $(TESTLOG) @echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG) @echo $(shell date) >> $(TESTLOG)
@echo "Test MaxScale core" >> $(TESTLOG) @echo "Test MaxScale core" >> $(TESTLOG)
@ -38,3 +39,4 @@ else
@echo "MaxScale core FAILED" >> $(TESTLOG) @echo "MaxScale core FAILED" >> $(TESTLOG)
endif endif
@echo "" >> $(TESTLOG) @echo "" >> $(TESTLOG)
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -26,6 +26,7 @@
* *
* Date Who Description * Date Who Description
* 21/06/13 Mark Riddoch Initial implementation * 21/06/13 Mark Riddoch Initial implementation
* 07/05/14 Massimiliano Pinto Added version_string to global configuration
* *
* @endverbatim * @endverbatim
*/ */
@ -55,6 +56,7 @@ typedef struct config_context {
*/ */
typedef struct { typedef struct {
int n_threads; /**< Number of polling threads */ int n_threads; /**< Number of polling threads */
char *version_string; /**< The version string of embedded database library */
} GATEWAY_CONF; } GATEWAY_CONF;
extern int config_load(char *); extern int config_load(char *);

View File

@ -1,126 +0,0 @@
/*
* This file is distributed as part of the SkySQL Gateway. It is free
* software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright SkySQL Ab 2013
*
*/
/*
* MYSQL mysql protocol header file
* Revision History
*
* Date Who Description
* 10/06/13 Massimiliano Pinto Initial implementation
*
*/
#include <dcb.h>
/* Protocol packing macros. */
#define gw_mysql_set_byte2(__buffer, __int) do { \
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); } while (0)
#define gw_mysql_set_byte3(__buffer, __int) do { \
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \
(__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); } while (0)
#define gw_mysql_set_byte4(__buffer, __int) do { \
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \
(__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); \
(__buffer)[3]= (uint8_t)(((__int) >> 24) & 0xFF); } while (0)
/* Protocol unpacking macros. */
#define gw_mysql_get_byte2(__buffer) \
(uint16_t)((__buffer)[0] | \
((__buffer)[1] << 8))
#define gw_mysql_get_byte3(__buffer) \
(uint32_t)((__buffer)[0] | \
((__buffer)[1] << 8) | \
((__buffer)[2] << 16))
#define gw_mysql_get_byte4(__buffer) \
(uint32_t)((__buffer)[0] | \
((__buffer)[1] << 8) | \
((__buffer)[2] << 16) | \
((__buffer)[3] << 24))
#define gw_mysql_get_byte8(__buffer) \
((uint64_t)(__buffer)[0] | \
((uint64_t)(__buffer)[1] << 8) | \
((uint64_t)(__buffer)[2] << 16) | \
((uint64_t)(__buffer)[3] << 24) | \
((uint64_t)(__buffer)[4] << 32) | \
((uint64_t)(__buffer)[5] << 40) | \
((uint64_t)(__buffer)[6] << 48) | \
((uint64_t)(__buffer)[7] << 56))
typedef enum
{
GW_MYSQL_CAPABILITIES_NONE= 0,
GW_MYSQL_CAPABILITIES_LONG_PASSWORD= (1 << 0),
GW_MYSQL_CAPABILITIES_FOUND_ROWS= (1 << 1),
GW_MYSQL_CAPABILITIES_LONG_FLAG= (1 << 2),
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB= (1 << 3),
GW_MYSQL_CAPABILITIES_NO_SCHEMA= (1 << 4),
GW_MYSQL_CAPABILITIES_COMPRESS= (1 << 5),
GW_MYSQL_CAPABILITIES_ODBC= (1 << 6),
GW_MYSQL_CAPABILITIES_LOCAL_FILES= (1 << 7),
GW_MYSQL_CAPABILITIES_IGNORE_SPACE= (1 << 8),
GW_MYSQL_CAPABILITIES_PROTOCOL_41= (1 << 9),
GW_MYSQL_CAPABILITIES_INTERACTIVE= (1 << 10),
GW_MYSQL_CAPABILITIES_SSL= (1 << 11),
GW_MYSQL_CAPABILITIES_IGNORE_SIGPIPE= (1 << 12),
GW_MYSQL_CAPABILITIES_TRANSACTIONS= (1 << 13),
GW_MYSQL_CAPABILITIES_RESERVED= (1 << 14),
GW_MYSQL_CAPABILITIES_SECURE_CONNECTION= (1 << 15),
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS= (1 << 16),
GW_MYSQL_CAPABILITIES_MULTI_RESULTS= (1 << 17),
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS= (1 << 18),
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH= (1 << 19),
GW_MYSQL_CAPABILITIES_SSL_VERIFY_SERVER_CERT= (1 << 30),
GW_MYSQL_CAPABILITIES_REMEMBER_OPTIONS= (1 << 31),
GW_MYSQL_CAPABILITIES_CLIENT= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD |
GW_MYSQL_CAPABILITIES_FOUND_ROWS |
GW_MYSQL_CAPABILITIES_LONG_FLAG |
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB |
GW_MYSQL_CAPABILITIES_LOCAL_FILES |
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH |
GW_MYSQL_CAPABILITIES_TRANSACTIONS |
GW_MYSQL_CAPABILITIES_PROTOCOL_41 |
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS |
GW_MYSQL_CAPABILITIES_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_SECURE_CONNECTION),
GW_MYSQL_CAPABILITIES_CLIENT_COMPRESS= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD |
GW_MYSQL_CAPABILITIES_FOUND_ROWS |
GW_MYSQL_CAPABILITIES_LONG_FLAG |
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB |
GW_MYSQL_CAPABILITIES_LOCAL_FILES |
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH |
GW_MYSQL_CAPABILITIES_TRANSACTIONS |
GW_MYSQL_CAPABILITIES_PROTOCOL_41 |
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS |
GW_MYSQL_CAPABILITIES_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_COMPRESS
),
} gw_mysql_capabilities_t;
#define SMALL_CHUNK 1024
#define MAX_CHUNK SMALL_CHUNK * 8 * 4
#define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10)
extern int mysql_send_ok(DCB *, int, int, const char *);
extern int MySQLSendHandshake(DCB *);

View File

@ -55,4 +55,6 @@ extern void *load_module(const char *module, const char *type);
extern void unload_module(const char *module); extern void unload_module(const char *module);
extern void printModules(); extern void printModules();
extern void dprintAllModules(DCB *); extern void dprintAllModules(DCB *);
char* get_maxscale_home(void);
#endif #endif

View File

@ -38,6 +38,7 @@
* 23/06/13 Mark Riddoch Added service user and users * 23/06/13 Mark Riddoch Added service user and users
* 06/02/14 Massimiliano Pinto Added service flag for root user access * 06/02/14 Massimiliano Pinto Added service flag for root user access
* 25/02/14 Massimiliano Pinto Added service refresh limit feature * 25/02/14 Massimiliano Pinto Added service refresh limit feature
* 07/05/14 Massimiliano Pinto Added version_string field to service struct
* *
* @endverbatim * @endverbatim
*/ */
@ -108,6 +109,7 @@ typedef struct service {
*router; /**< The router we are using */ *router; /**< The router we are using */
void *router_instance; void *router_instance;
/**< The router instance for this service */ /**< The router instance for this service */
char *version_string; /** version string for this service listeners */
struct server *databases; /**< The set of servers in the backend */ struct server *databases; /**< The set of servers in the backend */
SERVICE_USER credentials; /**< The cedentials of the service user */ SERVICE_USER credentials; /**< The cedentials of the service user */
SPINLOCK spin; /**< The service spinlock */ SPINLOCK spin; /**< The service spinlock */

View File

@ -122,6 +122,8 @@ struct router_client_session {
/*< cursor is pointer and status variable to current session command */ /*< cursor is pointer and status variable to current session command */
sescmd_cursor_t rses_cursor[BE_COUNT]; sescmd_cursor_t rses_cursor[BE_COUNT];
int rses_capabilities; /*< input type, for example */ int rses_capabilities; /*< input type, for example */
bool rses_autocommit_enabled;
bool rses_transaction_active;
struct router_client_session* next; struct router_client_session* next;
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
skygw_chk_t rses_chk_tail; skygw_chk_t rses_chk_tail;
@ -155,4 +157,8 @@ typedef struct router_instance {
struct router_instance* next; /*< Next router on the list */ struct router_instance* next; /*< Next router on the list */
} ROUTER_INSTANCE; } ROUTER_INSTANCE;
#define BACKEND_TYPE(b) (SERVER_IS_MASTER((b)->backend_server) ? BE_MASTER : \
(SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : \
(SERVER_IS_JOINED((b)->backend_server) ? BE_JOINED : BE_UNDEFINED)));
#endif /*< _RWSPLITROUTER_H */ #endif /*< _RWSPLITROUTER_H */

View File

@ -33,6 +33,7 @@
* If current user is authenticated the new users' table will replace the old one * If current user is authenticated the new users' table will replace the old one
* 28/02/2014 Massimiliano Pinto Added: client IPv4 in dcb->ipv4 and inet_ntop for string representation * 28/02/2014 Massimiliano Pinto Added: client IPv4 in dcb->ipv4 and inet_ntop for string representation
* 11/03/2014 Massimiliano Pinto Added: Unix socket support * 11/03/2014 Massimiliano Pinto Added: Unix socket support
* 07/05/2014 Massimiliano Pinto Added: specific version string in server handshake
* *
*/ */
@ -225,10 +226,21 @@ MySQLSendHandshake(DCB* dcb)
uint8_t mysql_filler_ten[10]; uint8_t mysql_filler_ten[10];
uint8_t mysql_last_byte = 0x00; uint8_t mysql_last_byte = 0x00;
char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1]=""; char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1]="";
char *version_string;
int len_version_string=0;
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol); MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
GWBUF *buf; GWBUF *buf;
/* get the version string from service property if available*/
if (dcb->service->version_string != NULL) {
version_string = dcb->service->version_string;
len_version_string = strlen(version_string);
} else {
version_string = GW_MYSQL_VERSION;
len_version_string = strlen(GW_MYSQL_VERSION);
}
gw_generate_random_str(server_scramble, GW_MYSQL_SCRAMBLE_SIZE); gw_generate_random_str(server_scramble, GW_MYSQL_SCRAMBLE_SIZE);
// copy back to the caller // copy back to the caller
@ -245,7 +257,7 @@ MySQLSendHandshake(DCB* dcb)
memcpy(mysql_plugin_data, server_scramble + 8, 12); memcpy(mysql_plugin_data, server_scramble + 8, 12);
mysql_payload_size = sizeof(mysql_protocol_version) + (strlen(GW_MYSQL_VERSION) + 1) + sizeof(mysql_thread_id) + 8 + sizeof(mysql_filler) + sizeof(mysql_server_capabilities_one) + sizeof(mysql_server_language) + sizeof(mysql_server_status) + sizeof(mysql_server_capabilities_two) + sizeof(mysql_scramble_len) + sizeof(mysql_filler_ten) + 12 + sizeof(mysql_last_byte) + strlen("mysql_native_password") + sizeof(mysql_last_byte); mysql_payload_size = sizeof(mysql_protocol_version) + (len_version_string + 1) + sizeof(mysql_thread_id) + 8 + sizeof(mysql_filler) + sizeof(mysql_server_capabilities_one) + sizeof(mysql_server_language) + sizeof(mysql_server_status) + sizeof(mysql_server_capabilities_two) + sizeof(mysql_scramble_len) + sizeof(mysql_filler_ten) + 12 + sizeof(mysql_last_byte) + strlen("mysql_native_password") + sizeof(mysql_last_byte);
// allocate memory for packet header + payload // allocate memory for packet header + payload
if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL) if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL)
@ -271,8 +283,9 @@ MySQLSendHandshake(DCB* dcb)
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_protocol_version); mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_protocol_version);
// write server version plus 0 filler // write server version plus 0 filler
strcpy((char *)mysql_handshake_payload, GW_MYSQL_VERSION); strcpy((char *)mysql_handshake_payload, version_string);
mysql_handshake_payload = mysql_handshake_payload + strlen(GW_MYSQL_VERSION); mysql_handshake_payload = mysql_handshake_payload + len_version_string;
*mysql_handshake_payload = 0x00; *mysql_handshake_payload = 0x00;
mysql_handshake_payload++; mysql_handshake_payload++;

View File

@ -88,4 +88,20 @@ install: $(MODULES)
(cd readwritesplit; make DEST=$(DEST) install) (cd readwritesplit; make DEST=$(DEST) install)
(cd binlog; make DEST=$(DEST) install) (cd binlog; make DEST=$(DEST) install)
cleantests:
$(MAKE) -C readwritesplit/test cleantests
$(MAKE) -C test cleantests
buildtests:
$(MAKE) -C readwritesplit/test DEBUG=Y buildtests
$(MAKE) -C test DEBUG=Y buildtests
runtests:
$(MAKE) -C test runtests
$(MAKE) -C readwritesplit runtests
testall:
$(MAKE) -C test testall
$(MAKE) -C readwritesplit testall
include depend.mk include depend.mk

View File

@ -62,4 +62,17 @@ depend:
install: $(MODULES) install: $(MODULES)
install -D $(MODULES) $(DEST)/MaxScale/modules install -D $(MODULES) $(DEST)/MaxScale/modules
cleantests:
$(MAKE) -C test cleantest
testall:
$(MAKE) -C test testall
buildtests:
$(MAKE) -C test buildtests
runtests:
$(MAKE) -C runtests
include depend.mk include depend.mk

View File

@ -157,6 +157,13 @@ static void tracelog_routed_query(
DCB* dcb, DCB* dcb,
GWBUF* buf); GWBUF* buf);
static bool route_session_write(
ROUTER_CLIENT_SES* router_client_ses,
GWBUF* querybuf,
ROUTER_INSTANCE* inst,
unsigned char packet_type,
skygw_query_type_t qtype);
static SPINLOCK instlock; static SPINLOCK instlock;
static ROUTER_INSTANCE* instances; static ROUTER_INSTANCE* instances;
@ -244,7 +251,6 @@ static ROUTER* createInstance(
"module but none are supported. The options will be " "module but none are supported. The options will be "
"ignored."))); "ignored.")));
} }
/** /**
* Create an array of the backend servers in the router structure to * Create an array of the backend servers in the router structure to
* maintain a count of the number of connections to each * maintain a count of the number of connections to each
@ -432,6 +438,8 @@ static void* newSession(
return (void *)client_rses; return (void *)client_rses;
} }
/** /**
* Close a session with the router, this is the mechanism * Close a session with the router, this is the mechanism
* by which a router may cleanup data structure etc. * by which a router may cleanup data structure etc.
@ -574,11 +582,13 @@ static int routeQuery(
bool rses_is_closed; bool rses_is_closed;
rses_property_t* prop; rses_property_t* prop;
size_t len; size_t len;
static bool transaction_active; /** if false everything goes to master and session commands to slave too */
static bool autocommit_enabled = true;
/** if true everything goes to master and session commands to slave too */
static bool transaction_active = false;
CHK_CLIENT_RSES(router_cli_ses); CHK_CLIENT_RSES(router_cli_ses);
/** Dirty read for quick check if router is closed. */ /** Dirty read for quick check if router is closed. */
if (router_cli_ses->rses_closed) if (router_cli_ses->rses_closed)
{ {
@ -669,207 +679,102 @@ static int routeQuery(
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, LOGIF(LT, (skygw_log_write(LOGFILE_TRACE,
"Packet type\t%s", "Packet type\t%s",
STRPACKETTYPE(packet_type)))); STRPACKETTYPE(packet_type))));
/**
if (QUERY_IS_TYPE(qtype,QUERY_TYPE_COMMIT) && * If autocommit is disabled or transaction is explicitly started
transaction_active) * transaction becomes active and master gets all statements until
* transaction is committed and autocommit is enabled again.
*/
if (router_cli_ses->rses_autocommit_enabled &&
QUERY_IS_TYPE(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT))
{ {
transaction_active = false; router_cli_ses->rses_autocommit_enabled = false;
if (!router_cli_ses->rses_transaction_active)
{
router_cli_ses->rses_transaction_active = true;
} }
}
else if (!router_cli_ses->rses_transaction_active &&
switch (qtype) { QUERY_IS_TYPE(qtype, QUERY_TYPE_BEGIN_TRX))
case QUERY_TYPE_WRITE: {
LOGIF(LT, (skygw_log_write( router_cli_ses->rses_transaction_active = true;
LOGFILE_TRACE, }
"%lu [routeQuery:rwsplit] Query type\t%s, " /**
"routing to Master.", * Explicit COMMIT and ROLLBACK, implicit COMMIT.
pthread_self(), */
STRQTYPE(qtype)))); if (router_cli_ses->rses_autocommit_enabled &&
router_cli_ses->rses_transaction_active &&
LOGIF(LT, tracelog_routed_query(router_cli_ses, (QUERY_IS_TYPE(qtype,QUERY_TYPE_COMMIT) ||
"routeQuery", QUERY_IS_TYPE(qtype,QUERY_TYPE_ROLLBACK)))
master_dcb, {
gwbuf_clone(querybuf))); router_cli_ses->rses_transaction_active = false;
}
ret = master_dcb->func.write(master_dcb, querybuf); else if (!router_cli_ses->rses_autocommit_enabled &&
atomic_add(&inst->stats.n_master, 1); QUERY_IS_TYPE(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT))
{
goto return_ret; router_cli_ses->rses_autocommit_enabled = true;
break; router_cli_ses->rses_transaction_active = false;
}
case QUERY_TYPE_READ: /**
LOGIF(LT, (skygw_log_write_flush( * Session update is always routed in the same way.
LOGFILE_TRACE, */
"%lu [routeQuery:rwsplit] Query type\t%s, " if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE))
"routing to %s.", {
pthread_self(), bool succp = route_session_write(
STRQTYPE(qtype),
(transaction_active ? "Master" : "Slave"))));
LOGIF(LT, tracelog_routed_query(
router_cli_ses, router_cli_ses,
"routeQuery", querybuf,
(transaction_active ? master_dcb : slave_dcb), inst,
gwbuf_clone(querybuf))); packet_type,
qtype);
if (transaction_active) if (succp)
{ {
ret = master_dcb->func.write(master_dcb, querybuf); ret = 1;
} }
else ss_dassert(succp);
ss_dassert(ret == 1);
goto return_ret;
}
else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) &&
!router_cli_ses->rses_transaction_active)
{ {
bool succp;
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Read-only query, routing to Slave.")));
ss_dassert(QUERY_IS_TYPE(qtype, QUERY_TYPE_READ));
ret = slave_dcb->func.write(slave_dcb, querybuf); ret = slave_dcb->func.write(slave_dcb, querybuf);
}
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] Routed.",
pthread_self())));
atomic_add(&inst->stats.n_slave, 1); atomic_add(&inst->stats.n_slave, 1);
goto return_ret; goto return_ret;
break;
case QUERY_TYPE_SESSION_WRITE:
/**
* Execute in backends used by current router session.
* Save session variable commands to router session property
* struct. Thus, they can be replayed in backends which are
* started and joined later.
*
* Suppress redundant OK packets sent by backends.
*
* DOES THIS ALL APPLY TO COM_QUIT AS WELL??
*
* The first OK packet is replied to the client.
*
*/
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] DCB M:%p s:%p, "
"Query type\t%s, "
"packet type %s, routing to all servers.",
pthread_self(),
master_dcb,
slave_dcb,
STRQTYPE(qtype),
STRPACKETTYPE(packet_type))));
/**
* COM_QUIT is one-way message. Server doesn't respond to that.
* Therefore reply processing is unnecessary and session
* command property is not needed. It is just routed to both
* backends.
*/
if (packet_type == COM_QUIT)
{
int rc;
int rc2;
rc = master_dcb->func.write(master_dcb, gwbuf_clone(querybuf));
rc2 = slave_dcb->func.write(slave_dcb, querybuf);
if (rc == 1 && rc == rc2)
{
ret = 1;
}
goto return_ret;
}
prop = rses_property_init(RSES_PROP_TYPE_SESCMD);
/**
* Additional reference is created to querybuf to
* prevent it from being released before properties
* are cleaned up as a part of router sessionclean-up.
*/
mysql_sescmd_init(prop, querybuf, packet_type, router_cli_ses);
/** Lock router session */
if (!rses_begin_locked_router_action(router_cli_ses))
{
rses_property_done(prop);
goto return_ret;
}
/** Add sescmd property to router client session */
rses_property_add(router_cli_ses, prop);
/** Execute session command in master */
if (execute_sescmd_in_backend(router_cli_ses, BE_MASTER))
{
ret = 1;
} }
else else
{ {
/** Log error */ bool succp = true;
}
/** Execute session command in slave */ if (LOG_IS_ENABLED(LOGFILE_TRACE))
if (execute_sescmd_in_backend(router_cli_ses, BE_SLAVE))
{ {
ret = 1; if (router_cli_ses->rses_transaction_active) /*< all to master */
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Transaction is active, routing to Master.")));
} }
else else
{ {
/** Log error */
}
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
atomic_add(&inst->stats.n_all, 1);
goto return_ret;
break;
case QUERY_TYPE_BEGIN_TRX:
transaction_active = true;
LOGIF(LT, tracelog_routed_query(router_cli_ses,
"routeQuery",
master_dcb,
gwbuf_clone(querybuf)));
ret = master_dcb->func.write(master_dcb, querybuf);
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] Routed.",
pthread_self())));
atomic_add(&inst->stats.n_master, 1);
goto return_ret;
break;
case QUERY_TYPE_COMMIT:
case QUERY_TYPE_ROLLBACK:
transaction_active = false;
LOGIF(LT, tracelog_routed_query(router_cli_ses,
"routeQuery",
master_dcb,
gwbuf_clone(querybuf)));
ret = master_dcb->func.write(master_dcb, querybuf);
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] Routed.",
pthread_self())));
atomic_add(&inst->stats.n_master, 1);
goto return_ret;
break;
default:
LOGIF(LT, (skygw_log_write( LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE, LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] Query type\t%s, " "Begin transaction, write or unspecified type, "
"routing to Master by default.", "routing to Master.")));
pthread_self(), }
STRQTYPE(qtype)))); }
/**
* Is this really ok?
* What is not known is routed to master.
*/
LOGIF(LT, tracelog_routed_query(router_cli_ses,
"routeQuery",
master_dcb,
gwbuf_clone(querybuf)));
ret = master_dcb->func.write(master_dcb, querybuf); ret = master_dcb->func.write(master_dcb, querybuf);
atomic_add(&inst->stats.n_master, 1); atomic_add(&inst->stats.n_master, 1);
goto return_ret; goto return_ret;
break; }
} /*< switch by query type */
return_ret: return_ret:
if (plainsqlbuf != NULL) if (plainsqlbuf != NULL)
@ -1037,7 +942,6 @@ static void clientReply(
/** Unlock */ /** Unlock */
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
/** /**
* 1. Check if backend received reply to sescmd. * 1. Check if backend received reply to sescmd.
* 2. Check sescmd's state whether OK_PACKET has been * 2. Check sescmd's state whether OK_PACKET has been
@ -1086,7 +990,6 @@ static void clientReply(
writebuf = sescmd_cursor_process_replies(client_dcb, writebuf = sescmd_cursor_process_replies(client_dcb,
writebuf, writebuf,
scur); scur);
} }
/** Unlock router session */ /** Unlock router session */
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
@ -1690,6 +1593,7 @@ return_succp:
return succp; return succp;
} }
/** /**
* Moves cursor to next property and copied address of its sescmd to cursor. * Moves cursor to next property and copied address of its sescmd to cursor.
* Current propery must be non-null. * Current propery must be non-null.
@ -1767,7 +1671,6 @@ static rses_property_t* mysql_sescmd_get_property(
return scmd->my_sescmd_prop; return scmd->my_sescmd_prop;
} }
static void tracelog_routed_query( static void tracelog_routed_query(
ROUTER_CLIENT_SES* rses, ROUTER_CLIENT_SES* rses,
char* funcname, char* funcname,
@ -1824,11 +1727,13 @@ static void tracelog_routed_query(
-1)), -1)),
STRBETYPE(be_type), STRBETYPE(be_type),
dcb))); dcb)));
free(querystr);
} }
} }
gwbuf_free(buf); gwbuf_free(buf);
} }
/** /**
* Return rc, rc < 0 if router session is closed. rc == 0 if there are no * Return rc, rc < 0 if router session is closed. rc == 0 if there are no
* capabilities specified, rc > 0 when there are capabilities. * capabilities specified, rc > 0 when there are capabilities.
@ -1852,3 +1757,102 @@ static uint8_t getCapabilities (
return_rc: return_rc:
return rc; return rc;
} }
/**
* Execute in backends used by current router session.
* Save session variable commands to router session property
* struct. Thus, they can be replayed in backends which are
* started and joined later.
*
* Suppress redundant OK packets sent by backends.
*
* The first OK packet is replied to the client.
* Return true if succeed, false is returned if router session was closed or
* if execute_sescmd_in_backend failed.
*/
static bool route_session_write(
ROUTER_CLIENT_SES* router_cli_ses,
GWBUF* querybuf,
ROUTER_INSTANCE* inst,
unsigned char packet_type,
skygw_query_type_t qtype)
{
bool succp;
DCB* master_dcb;
DCB* slave_dcb;
rses_property_t* prop;
master_dcb = router_cli_ses->rses_dcb[BE_MASTER];
slave_dcb = router_cli_ses->rses_dcb[BE_SLAVE];
CHK_DCB(master_dcb);
CHK_DCB(slave_dcb);
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Session write, query type\t%s, packet type %s, "
"routing to all servers.",
STRQTYPE(qtype),
STRPACKETTYPE(packet_type))));
/**
* COM_QUIT is one-way message. Server doesn't respond to that.
* Therefore reply processing is unnecessary and session
* command property is not needed. It is just routed to both
* backends.
*/
if (packet_type == COM_QUIT)
{
int rc;
int rc2;
rc = master_dcb->func.write(master_dcb, gwbuf_clone(querybuf));
rc2 = slave_dcb->func.write(slave_dcb, querybuf);
if (rc == 1 && rc == rc2)
{
succp = true;
}
goto return_succp;
}
prop = rses_property_init(RSES_PROP_TYPE_SESCMD);
/**
* Additional reference is created to querybuf to
* prevent it from being released before properties
* are cleaned up as a part of router sessionclean-up.
*/
mysql_sescmd_init(prop, querybuf, packet_type, router_cli_ses);
/** Lock router session */
if (!rses_begin_locked_router_action(router_cli_ses))
{
rses_property_done(prop);
succp = false;
goto return_succp;
}
/** Add sescmd property to router client session */
rses_property_add(router_cli_ses, prop);
/** Execute session command in master */
succp = execute_sescmd_in_backend(router_cli_ses, BE_MASTER);
if (!succp)
{
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
goto return_succp;
}
/** Execute session command in slave */
succp = execute_sescmd_in_backend(router_cli_ses, BE_SLAVE);
if (!succp)
{
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
goto return_succp;
}
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
atomic_add(&inst->stats.n_all, 1);
return_succp:
return succp;
}

View File

@ -8,7 +8,7 @@ include $(ROOT_PATH)/makefile.inc
include $(ROOT_PATH)/test.inc include $(ROOT_PATH)/test.inc
CC=cc CC=cc
TESTLOG := $(shell pwd)/testrouters.log TESTLOG := $(shell pwd)/testrwsplit.log
RET := -1 RET := -1
cleantests: cleantests:
@ -25,17 +25,11 @@ buildtests:
runtests: runtests:
@echo "" >> $(TESTLOG) @echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG) @echo $(shell date) >> $(TESTLOG)
@echo "Test MaxScale R/W Split" >> $(TESTLOG) @echo "Test Read/Write split router" >> $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
./rwsplit.sh $(TESTLOG) $(THOST) $(TPORT_RW) $(TMASTER_ID) $(TUSER) $(TPWD) ./rwsplit.sh $(TESTLOG) $(THOST) $(TPORT_RW) $(TMASTER_ID) $(TUSER) $(TPWD)
@echo "" >> $(TESTLOG) @echo "" >> $(TESTLOG)
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
pesce:
@echo "fine"

View File

@ -30,9 +30,46 @@ else
echo "$TINPUT PASSED">>$TLOG ; echo "$TINPUT PASSED">>$TLOG ;
fi fi
TINPUT=test_transaction_routing2b.sql
TRETVAL=0
a=`$RUNCMD < ./$TINPUT`
if [ "$a" != "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
TINPUT=test_transaction_routing3.sql TINPUT=test_transaction_routing3.sql
TRETVAL=2 TRETVAL=2
a=`$RUNCMD < ./$TINPUT` a=`$RUNCMD < ./$TINPUT`
if [ "$a" == "$TMASTER_ID" ]; then
echo "$TINPUT FAILED, return value $a when one of the slave IDs was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
TINPUT=test_transaction_routing3b.sql
TRETVAL=2
a=`$RUNCMD < ./$TINPUT`
if [ "$a" == "$TMASTER_ID" ]; then
echo "$TINPUT FAILED, return value $a when one of the slave IDs was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
# test implicit transaction, that is, not started explicitly, autocommit=0
TINPUT=test_transaction_routing4.sql
TRETVAL=0
a=`$RUNCMD < ./$TINPUT`
if [ "$a" != "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
TINPUT=test_transaction_routing4b.sql
TRETVAL=0
a=`$RUNCMD < ./$TINPUT`
if [ "$a" != "$TRETVAL" ]; then if [ "$a" != "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG; echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else else
@ -113,3 +150,44 @@ if [ "$a" == "$TRETVAL" ]; then
else else
echo "$TINPUT PASSED">>$TLOG ; echo "$TINPUT PASSED">>$TLOG ;
fi fi
TINPUT=test_autocommit_disabled1.sql
TRETVAL=1
a=`$RUNCMD < ./$TINPUT`
if [ "$a" != "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
TINPUT=test_autocommit_disabled1b.sql
TRETVAL=1
a=`$RUNCMD < ./$TINPUT`
if [ "$a" != "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
# Disable autocommit in the first session and then test in new session that
# it is again enabled.
TINPUT=test_autocommit_disabled2.sql
TRETVAL=1
a=`$RUNCMD < ./$TINPUT`
if [ "$a" != "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi
TINPUT=set_autocommit_disabled.sql
`$RUNCMD < ./$TINPUT`
TINPUT=test_after_autocommit_disabled.sql
TRETVAL=$TMASTER_ID
a=`$RUNCMD < ./$TINPUT`
if [ "$a" == "$TRETVAL" ]; then
echo "$TINPUT FAILED, return value $a when it was not accetable">>$TLOG;
else
echo "$TINPUT PASSED">>$TLOG ;
fi

View File

@ -0,0 +1,7 @@
use test;
drop table if exists t1;
create table t1 (id integer);
set autocommit=0; -- open transaction
begin;
insert into t1 values(1); -- write to master
commit;

View File

@ -0,0 +1,2 @@
use test;
select @@server_id;

View File

@ -0,0 +1,7 @@
use test;
drop table if exists t1;
create table t1 (id integer);
set autocommit=0; -- open transaction
insert into t1 values(1); -- write to master
select count(*) from t1; -- read from master
drop table t1;

View File

@ -0,0 +1,7 @@
use test;
drop table if exists t1;
create table t1 (id integer);
set autocommit=OFF; -- open transaction
insert into t1 values(1); -- write to master
select count(*) from t1; -- read from master
drop table t1;

View File

@ -0,0 +1,9 @@
use test;
drop table if exists t1;
create table t1 (id integer);
set autocommit=0; -- open transaction
begin;
insert into t1 values(1); -- write to master
commit;
select count(*) from t1; -- read from master since autocommit is disabled
drop table t1;

View File

@ -1,5 +1,5 @@
DROP DATABASE If EXISTS FOO; DROP DATABASE If EXISTS FOO;
SET autocommit=0; SET autocommit=1;
BEGIN; BEGIN;
CREATE DATABASE FOO; -- implicit commit CREATE DATABASE FOO; -- implicit commit
SELECT (@@server_id) INTO @a; SELECT (@@server_id) INTO @a;

View File

@ -1,7 +1,7 @@
USE test; USE test;
DROP TABLE IF EXISTS T1; DROP TABLE IF EXISTS T1;
DROP EVENT IF EXISTS myevent; DROP EVENT IF EXISTS myevent;
SET autocommit=0; SET autocommit=1;
BEGIN; BEGIN;
CREATE TABLE T1 (id integer); CREATE TABLE T1 (id integer);
CREATE EVENT myevent CREATE EVENT myevent

View File

@ -1,6 +1,6 @@
USE test; USE test;
DROP TABLE IF EXISTS T1; DROP TABLE IF EXISTS T1;
SET autocommit=0; SET autocommit=1;
BEGIN; BEGIN;
CREATE TABLE T1 (id integer); -- implicit commit CREATE TABLE T1 (id integer); -- implicit commit
SELECT (@@server_id) INTO @a; SELECT (@@server_id) INTO @a;

View File

@ -1,6 +1,6 @@
USE test; USE test;
DROP PROCEDURE IF EXISTS simpleproc; DROP PROCEDURE IF EXISTS simpleproc;
SET autocommit=0; SET autocommit=1;
BEGIN; BEGIN;
DELIMITER // DELIMITER //
CREATE PROCEDURE simpleproc (OUT param1 INT) CREATE PROCEDURE simpleproc (OUT param1 INT)

View File

@ -1,6 +1,6 @@
USE test; USE test;
DROP FUNCTION IF EXISTS hello; DROP FUNCTION IF EXISTS hello;
SET autocommit=0; SET autocommit=1;
BEGIN; BEGIN;
CREATE FUNCTION hello (s CHAR(20)) CREATE FUNCTION hello (s CHAR(20))
RETURNS CHAR(50) DETERMINISTIC RETURNS CHAR(50) DETERMINISTIC

View File

@ -1,7 +1,7 @@
USE test; USE test;
DROP TABLE IF EXISTS T1; DROP TABLE IF EXISTS T1;
CREATE TABLE T1 (id integer); -- implicit commit CREATE TABLE T1 (id integer); -- implicit commit
SET autocommit=0; SET autocommit=1;
BEGIN; BEGIN;
CREATE INDEX foo_t1 on T1 (id); -- implicit commit CREATE INDEX foo_t1 on T1 (id); -- implicit commit
SELECT (@@server_id) INTO @a; SELECT (@@server_id) INTO @a;

View File

@ -1,19 +0,0 @@
USE test;
SET autocommit = 0;
SET @a= -1;
SET @b= -2;
START TRANSACTION;
CREATE TABLE IF NOT EXISTS myCity (a int, b char(20));
INSERT INTO myCity VALUES (1, 'Milan');
INSERT INTO myCity VALUES (2, 'London');
COMMIT;
START TRANSACTION;
DELETE FROM myCity;
SET @a = (SELECT COUNT(*) FROM myCity);
ROLLBACK;
START TRANSACTION;
SET @b = (SELECT COUNT(*) FROM myCity);
START TRANSACTION;
DROP TABLE myCity;
SELECT (@a+@b) AS res;
COMMIT;

View File

@ -1,10 +1,11 @@
USE test; USE test;
SET autocommit = 0; SET autocommit = Off;
START TRANSACTION; START TRANSACTION;
CREATE TABLE IF NOT EXISTS myCity (a int, b char(20)); CREATE TABLE IF NOT EXISTS myCity (a int, b char(20));
INSERT INTO myCity VALUES (1, 'Milan'); INSERT INTO myCity VALUES (1, 'Milan');
INSERT INTO myCity VALUES (2, 'London'); INSERT INTO myCity VALUES (2, 'London');
COMMIT; COMMIT;
START TRANSACTION;
DELETE FROM myCity; DELETE FROM myCity;
SELECT COUNT(*) FROM myCity; -- read transaction's modifications from slave SELECT COUNT(*) FROM myCity; -- read transaction's modifications from master
COMMIT; COMMIT;

View File

@ -0,0 +1,7 @@
-- Read from slave after implicit COMMIT
USE test;
START TRANSACTION;
CREATE TABLE IF NOT EXISTS T2 (id integer);
INSERT INTO T2 VALUES (@@server_id);
SET AUTOCOMMIT=oN;
SELECT id from T2; -- read transaction's modifications from slave

View File

@ -0,0 +1,9 @@
USE test;
SET autocommit = 0;
CREATE TABLE IF NOT EXISTS myCity (a int, b char(20));
INSERT INTO myCity VALUES (1, 'Milan');
INSERT INTO myCity VALUES (2, 'London');
COMMIT;
DELETE FROM myCity; -- implicit transaction started
SELECT COUNT(*) FROM myCity; -- read transaction's modifications from master
COMMIT;

View File

@ -0,0 +1,9 @@
USE test;
SET autocommit = oFf;
CREATE TABLE IF NOT EXISTS myCity (a int, b char(20));
INSERT INTO myCity VALUES (1, 'Milan');
INSERT INTO myCity VALUES (2, 'London');
COMMIT;
DELETE FROM myCity; -- implicit transaction started
SELECT COUNT(*) FROM myCity; -- read transaction's modifications from master
COMMIT;

View File

@ -5,6 +5,7 @@
include ../../build_gateway.inc include ../../build_gateway.inc
include ../../makefile.inc include ../../makefile.inc
include ../../test.inc
TEST_ROOT := $(ROOT_PATH)/test TEST_ROOT := $(ROOT_PATH)/test
PARENT_DIR := $(ROOT_PATH)/server PARENT_DIR := $(ROOT_PATH)/server
@ -40,7 +41,14 @@ testall:
@echo $(shell date) >> $(TESTLOG) @echo $(shell date) >> $(TESTLOG)
@echo "Test Server Core" >> $(TESTLOG) @echo "Test Server Core" >> $(TESTLOG)
$(MAKE) -C $(ROOT_PATH)/server/core testall $(MAKE) -C $(ROOT_PATH)/server/core testall
@echo "Query Classifier PASSED" >> $(TESTLOG) @echo "Core PASSED" >> $(TESTLOG)
@echo "" >> $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG)
@echo "Test Modules" >> $(TESTLOG)
$(MAKE) -C $(ROOT_PATH)/server/modules/routing testall
@killall maxscale
@echo "Routing modules PASSED" >> $(TESTLOG)
buildtests: buildtests:
@ -51,7 +59,7 @@ else
endif endif
runtests: runtests:
@echo "" >> $(TESTLOG) @echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG) @echo $(shell date) >> $(TESTLOG)
@echo "Test MaxScale server" >> $(TESTLOG) @echo "Test MaxScale server" >> $(TESTLOG)
@ -63,5 +71,4 @@ runtests:
@sleep 5 @sleep 5
@echo "MaxScale server PASSED" >> $(TESTLOG) @echo "MaxScale server PASSED" >> $(TESTLOG)
@echo "" >> $(TESTLOG) @echo "" >> $(TESTLOG)
@killall maxscale @cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -33,3 +33,9 @@ TPWD :=
# TMASTER_ID := 2 # TMASTER_ID := 2
# #
TMASTER_ID := TMASTER_ID :=
#
# Global test log where all log is gathered
# TEST_MAXSCALE_LOG := $(ROOT_PATH)/test/test_maxscale.log
#
TEST_MAXSCALE_LOG :=

View File

@ -57,7 +57,8 @@ MAXSCALE_HOME
log_manager, log_manager,
query_classifier, query_classifier,
server, server,
utils utils,
modules
| |
|- utils cleantests, buildtests, runtests, testall |- utils cleantests, buildtests, runtests, testall
| | | |

View File

@ -5,11 +5,12 @@
include ../build_gateway.inc include ../build_gateway.inc
include ../makefile.inc include ../makefile.inc
include ../test.inc
export MAXSCALE_HOME=$(shell pwd)/MaxScale export MAXSCALE_HOME=$(shell pwd)/MaxScale
CC=cc CC=cc
TESTLOG := $(shell pwd)/testmaxscale.log TESTLOG := $(TEST_MAXSCALE_LOG)
testall: testall:
$(MAKE) cleantests $(MAKE) cleantests
@ -55,9 +56,10 @@ buildtests:
$(MAKE) -C $(ROOT_PATH)/utils buildtests $(MAKE) -C $(ROOT_PATH)/utils buildtests
runtests: runtests:
@echo "" >> $(TESTLOG) @echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG) @echo $(shell date) >> $(TESTLOG)
@echo "Test MaxScale root" >> $(TESTLOG) @echo "Test MaxScale root" >> $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG) @echo "-------------------------------" >> $(TESTLOG)
@echo "Nothing to run here so far" >> $(TESTLOG) @echo "Nothing to run here so far" >> $(TESTLOG)