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
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
for MariaDB or checked out the source code of MariaDB and the
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
# variables.
#
# Current values are to satisfy MaxScale build process.
#
# Modify to match with your needs. Do not commit any private
# changes to this file!
#
#
#
# Use debug flags
# Values : <empty> | Y
# Set debug flags
#
DEBUG :=
#
# Set build env
# Values : <empty> | Y
#
UNIX := Y
#
# Set MaxScale branch directory
# Example := $(HOME)/src/MaxScale
#
ROOT_PATH :=
#
# Main directory for headers
# Example := $(HOME)/usr/include
#
INC_PATH :=
ROOT_PATH := $(HOME)/src/bazaar/tmp/maxscale
INC_PATH := $(HOME)/usr/include
#
# Set prefix to MySQL Resources
# Example := $(INC_PATH)/mysql
#
MYSQL_ROOT :=
MYSQL_ROOT := $(INC_PATH)/mysql
#
# 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
# Values : <empty> | Y
#
DYNLIB :=
#
# 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
EMBEDDED_LIB :=
EMBEDDED_LIB := $(HOME)/usr/lib64/dynlib
endif
#
# 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);
static blockbuf_t* blockbuf_init(logfile_id_t id);
static void blockbuf_node_done(void* bb_data);
static char* blockbuf_get_writepos(
#if 0
int** refcount,
@ -996,8 +997,13 @@ static char* blockbuf_get_writepos(
simple_mutex_unlock(&bb->bb_mutex);
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(
@ -2059,7 +2065,7 @@ static bool logfile_init(
if (mlist_init(&logfile->lf_blockbuf_list,
NULL,
strdup("logfile block buffer list"),
NULL,
blockbuf_node_done,
MAXNBLOCKBUFS) == NULL)
{
ss_dfprintf(stderr,

View File

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

View File

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

View File

@ -50,11 +50,13 @@
#include <sql_parse.h>
#include <errmsg.h>
#include <client_settings.h>
#include <set_var.h>
#include <strfunc.h>
#include <item_func.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
extern int lm_enabled_logfiles_bitmask;
@ -73,9 +75,13 @@ static bool create_parse_tree(
static skygw_query_type_t resolve_query_type(
THD* thd);
static bool skygw_stmt_causes_implicit_commit(
LEX* lex,
uint mask);
LEX* lex,
int* autocommit_stmt);
static int is_autocommit_stmt(
LEX* lex);
/**
* @node (write brief function description here)
@ -346,9 +352,9 @@ return_here:
* restrictive, for example, QUERY_TYPE_READ is smaller than QUERY_TYPE_WRITE.
*
*/
static u_int8_t set_query_type(
u_int8_t* qtype,
u_int8_t new_type)
static u_int16_t set_query_type(
u_int16_t* qtype,
u_int16_t new_type)
{
*qtype = MAX(*qtype, new_type);
return *qtype;
@ -374,7 +380,8 @@ static skygw_query_type_t resolve_query_type(
THD* thd)
{
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;
Item* item;
/**
@ -397,7 +404,9 @@ static skygw_query_type_t resolve_query_type(
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))
{
@ -418,7 +427,25 @@ static skygw_query_type_t resolve_query_type(
"next command.");
}
}
if (set_autocommit_stmt == 1)
{
type |= QUERY_TYPE_ENABLE_AUTOCOMMIT;
}
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,
@ -648,11 +675,18 @@ 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;
if (!(sql_command_flags[lex->sql_command] & mask))
if (!(sql_command_flags[lex->sql_command] & CF_AUTO_COMMIT_TRANS))
{
succp = false;
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);
break;
case SQLCOM_SET_OPTION:
succp = lex->autocommit ? true : false;
if ((*autocommit_stmt = is_autocommit_stmt(lex)) == 1)
{
succp = true;
}
else
{
succp =false;
}
break;
default:
succp = true;
break;
}
return_succp:
return succp;
}
/**
* Finds out if stmt is SET autocommit
* and if the new value matches with the enable_cmd argument.
*
* 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
*/
typedef enum {
QUERY_TYPE_UNKNOWN = 0, /*< Couln't find out or parse error */
QUERY_TYPE_LOCAL_READ = (1<<0), /*< Read non-database data, execute in MaxScale */
QUERY_TYPE_READ = (1<<1), /*< No updates */
QUERY_TYPE_WRITE = (1<<2), /*< Master data will be modified */
QUERY_TYPE_SESSION_WRITE = (1<<3), /*< Session data will be modified */
QUERY_TYPE_GLOBAL_WRITE = (1<<4), /*< Global system variable modification */
QUERY_TYPE_BEGIN_TRX = (1<<5), /*< BEGIN or START TRANSACTION */
QUERY_TYPE_ROLLBACK = (1<<6), /*< ROLLBACK */
QUERY_TYPE_COMMIT = (1<<7), /*< COMMIT */
QUERY_TYPE_UNKNOWN = 0x000, /*< Initial value, can't be tested bitwisely */
QUERY_TYPE_LOCAL_READ = 0x001, /*< Read non-database data, execute in MaxScale */
QUERY_TYPE_READ = 0x002, /*< No updates */
QUERY_TYPE_WRITE = 0x004, /*< Master data will be modified */
QUERY_TYPE_SESSION_WRITE = 0x008, /*< Session data will be modified */
QUERY_TYPE_GLOBAL_WRITE = 0x010, /*< Global system variable modification */
QUERY_TYPE_BEGIN_TRX = 0x020, /*< BEGIN or START TRANSACTION */
QUERY_TYPE_ENABLE_AUTOCOMMIT = 0x040,/*< SET autocommit=1 */
QUERY_TYPE_DISABLE_AUTOCOMMIT = 0x080,/*< SET autocommit=0 */
QUERY_TYPE_ROLLBACK = 0x100, /*< ROLLBACK */
QUERY_TYPE_COMMIT = 0x200 /*< COMMIT */
} skygw_query_type_t;
#define QUERY_IS_TYPE(mask,type) ((mask & type) == type)

View File

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

View File

@ -37,6 +37,8 @@ passwd=maxpwd
# user=<User to fetch password inforamtion with>
# passwd=<Password of the user, plain text currently>
# 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:
# 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
* 14/02/14 Massimiliano Pinto Added enable_root_user in the service_params list
* 11/03/14 Massimiliano Pinto Added Unix socket support
* 11/05/14 Massimiliano Pinto Added version_string support to service
*
* @endverbatim
*/
@ -43,6 +44,7 @@
#include <monitor.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <mysql.h>
extern int lm_enabled_logfiles_bitmask;
@ -56,6 +58,7 @@ static void check_config_objects(CONFIG_CONTEXT *context);
static char *config_file = NULL;
static GATEWAY_CONF gateway;
char *version_string = NULL;
/**
* Config item handler for the ini file reader
@ -116,6 +119,21 @@ config_load(char *file)
CONFIG_CONTEXT config;
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();
config.object = "";
@ -146,6 +164,11 @@ int rval;
if (!config_file)
return 0;
if (gateway.version_string)
free(gateway.version_string);
global_defaults();
config.object = "";
@ -202,7 +225,29 @@ int error_count = 0;
config_get_value(obj->parameters, "passwd");
char *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)
serviceEnableRootUser(obj->element, atoi(enable_root_user));
@ -580,6 +625,10 @@ static void
global_defaults()
{
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 *auth;
char *enable_root_user;
char *version_string;
enable_root_user = config_get_value(obj->parameters, "enable_root_user");
@ -629,6 +679,15 @@ SERVER *server;
"user");
auth = config_get_value(obj->parameters,
"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) {
service_update(service, router,
user,
@ -861,6 +920,7 @@ static char *service_params[] =
"user",
"passwd",
"enable_root_user",
"version_string",
NULL
};

View File

@ -308,6 +308,13 @@ dcb_final_free(DCB *dcb)
free(dcb->data);
if (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);
simple_mutex_done(&dcb->dcb_read_lock);
simple_mutex_done(&dcb->dcb_write_lock);

View File

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

View File

@ -28,12 +28,15 @@
* 06/02/14 Massimiliano Pinto Added: serviceEnableRootUser routine
* 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)
* 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <session.h>
#include <service.h>
#include <server.h>
@ -70,11 +73,26 @@ SERVICE *service;
return 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);
return NULL;
}
service->name = strdup(servname);
service->routerModule = strdup(router);
service->version_string = NULL;
memset(&service->stats, 0, sizeof(SERVICE_STATS));
service->ports = NULL;
service->stats.started = time(0);
@ -159,7 +177,7 @@ GWPROTOCOL *funcs;
if (port->listener->func.listen(port->listener, config_bind)) {
port->listener->session = session_alloc(service, port->listener);
if (port->listener->session != NULL) {
port->listener->session->state = SESSION_STATE_LISTENER;
listeners += 1;
@ -625,7 +643,7 @@ SERVICE *ptr;
/**
* 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
*/
void
@ -637,30 +655,42 @@ SERVICE *ptr;
ptr = allServices;
while (ptr)
{
SERVER *server = ptr->databases;
dcb_printf(dcb, "Service %p\n", ptr);
dcb_printf(dcb, "\tService: %s\n", ptr->name);
dcb_printf(dcb, "\tRouter: %s (%p)\n", ptr->routerModule,
ptr->router);
if (ptr->router)
ptr->router->diagnostics(ptr->router_instance, dcb);
dcb_printf(dcb, "\tStarted: %s",
asctime(localtime(&ptr->stats.started)));
dcb_printf(dcb, "\tBackend databases\n");
while (server)
{
dcb_printf(dcb, "\t\t%s:%d Protocol: %s\n", server->name, server->port,
server->protocol);
server = server->nextdb;
}
dcb_printf(dcb, "\tUsers data: %p\n", ptr->users);
dcb_printf(dcb, "\tTotal connections: %d\n", ptr->stats.n_sessions);
dcb_printf(dcb, "\tCurrently connected: %d\n", ptr->stats.n_current);
dprintService(dcb, ptr);
ptr = ptr->next;
}
spinlock_release(&service_spin);
}
/**
* 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",
asctime(localtime(&service->stats.started)));
dcb_printf(dcb, "\tBackend databases\n");
while (server)
{
dcb_printf(dcb, "\t\t%s:%d Protocol: %s\n", server->name, server->port,
server->protocol);
server = server->nextdb;
}
dcb_printf(dcb, "\tUsers data: %p\n", service->users);
dcb_printf(dcb, "\tTotal connections: %d\n", service->stats.n_sessions);
dcb_printf(dcb, "\tCurrently connected: %d\n", service->stats.n_current);
}
/**
* Update the definition of a service
*

View File

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

View File

@ -24,8 +24,9 @@
* @verbatim
* Revision History
*
* Date Who Description
* 21/06/13 Mark Riddoch Initial implementation
* Date Who Description
* 21/06/13 Mark Riddoch Initial implementation
* 07/05/14 Massimiliano Pinto Added version_string to global configuration
*
* @endverbatim
*/
@ -54,7 +55,8 @@ typedef struct config_context {
* The gateway global configuration data
*/
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;
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 printModules();
extern void dprintAllModules(DCB *);
char* get_maxscale_home(void);
#endif

View File

@ -38,6 +38,7 @@
* 23/06/13 Mark Riddoch Added service user and users
* 06/02/14 Massimiliano Pinto Added service flag for root user access
* 25/02/14 Massimiliano Pinto Added service refresh limit feature
* 07/05/14 Massimiliano Pinto Added version_string field to service struct
*
* @endverbatim
*/
@ -108,6 +109,7 @@ typedef struct service {
*router; /**< The router we are using */
void *router_instance;
/**< 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 */
SERVICE_USER credentials; /**< The cedentials of the service user */
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 */
sescmd_cursor_t rses_cursor[BE_COUNT];
int rses_capabilities; /*< input type, for example */
bool rses_autocommit_enabled;
bool rses_transaction_active;
struct router_client_session* next;
#if defined(SS_DEBUG)
skygw_chk_t rses_chk_tail;
@ -155,4 +157,8 @@ typedef struct router_instance {
struct router_instance* next; /*< Next router on the list */
} 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 */

View File

@ -33,6 +33,7 @@
* 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
* 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_last_byte = 0x00;
char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1]="";
char *version_string;
int len_version_string=0;
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
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);
// copy back to the caller
@ -245,7 +257,7 @@ MySQLSendHandshake(DCB* dcb)
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
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);
// write server version plus 0 filler
strcpy((char *)mysql_handshake_payload, GW_MYSQL_VERSION);
mysql_handshake_payload = mysql_handshake_payload + strlen(GW_MYSQL_VERSION);
strcpy((char *)mysql_handshake_payload, version_string);
mysql_handshake_payload = mysql_handshake_payload + len_version_string;
*mysql_handshake_payload = 0x00;
mysql_handshake_payload++;

View File

@ -88,4 +88,20 @@ install: $(MODULES)
(cd readwritesplit; 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

View File

@ -20,9 +20,9 @@
include ../../../../build_gateway.inc
LOGPATH := $(ROOT_PATH)/log_manager
UTILSPATH := $(ROOT_PATH)/utils
QCLASSPATH := $(ROOT_PATH)/query_classifier
LOGPATH := $(ROOT_PATH)/log_manager
UTILSPATH := $(ROOT_PATH)/utils
QCLASSPATH := $(ROOT_PATH)/query_classifier
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include \
@ -61,5 +61,18 @@ depend:
install: $(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

View File

@ -74,7 +74,7 @@ static uint8_t getCapabilities (ROUTER* inst, void* router_session);
static bool search_backend_servers(
BACKEND** p_master,
BACKEND** p_slave,
ROUTER_INSTANCE* router);
ROUTER_INSTANCE* router);
static ROUTER_OBJECT MyObject = {
createInstance,
@ -157,6 +157,13 @@ static void tracelog_routed_query(
DCB* dcb,
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 ROUTER_INSTANCE* instances;
@ -211,10 +218,10 @@ static ROUTER* createInstance(
SERVICE* service,
char** options)
{
ROUTER_INSTANCE* router;
SERVER* server;
ROUTER_INSTANCE* router;
SERVER* server;
int n;
int i;
int i;
if ((router = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) {
return NULL;
@ -244,7 +251,6 @@ static ROUTER* createInstance(
"module but none are supported. The options will be "
"ignored.")));
}
/**
* Create an array of the backend servers in the router structure to
* maintain a count of the number of connections to each
@ -266,7 +272,7 @@ static ROUTER* createInstance(
router->servers[n]->backend_conn_count = 0;
n += 1;
server = server->nextdb;
}
}
router->servers[n] = NULL;
/**
@ -300,7 +306,7 @@ static ROUTER* createInstance(
}
}
}
/**
/**
* We have completed the creation of the router data, so now
* insert this router into the linked list of routers
* that have been created with this module.
@ -329,12 +335,12 @@ static void* newSession(
{
BACKEND* local_backend[BE_COUNT];
ROUTER_CLIENT_SES* client_rses;
ROUTER_INSTANCE* router = (ROUTER_INSTANCE *)router_inst;
ROUTER_INSTANCE* router = (ROUTER_INSTANCE *)router_inst;
bool succp;
client_rses =
(ROUTER_CLIENT_SES *)calloc(1, sizeof(ROUTER_CLIENT_SES));
if (client_rses == NULL)
{
ss_dassert(false);
@ -345,7 +351,7 @@ static void* newSession(
#if defined(SS_DEBUG)
client_rses->rses_chk_top = CHK_NUM_ROUTER_SES;
client_rses->rses_chk_tail = CHK_NUM_ROUTER_SES;
#endif
#endif
/** store pointers to sescmd list to both cursors */
client_rses->rses_cursor[BE_MASTER].scmd_cur_rses = client_rses;
client_rses->rses_cursor[BE_MASTER].scmd_cur_active = false;
@ -360,8 +366,8 @@ static void* newSession(
&client_rses->rses_properties[RSES_PROP_TYPE_SESCMD];
client_rses->rses_cursor[BE_SLAVE].scmd_cur_cmd = NULL;
client_rses->rses_cursor[BE_SLAVE].scmd_cur_be_type = BE_SLAVE;
/**
/**
* Find a backend server to connect to. This is the extent of the
* load balancing algorithm we need to implement for this simple
* connection router.
@ -369,7 +375,7 @@ static void* newSession(
succp = search_backend_servers(&local_backend[BE_MASTER],
&local_backend[BE_SLAVE],
router);
/** Both Master and Slave must be found */
if (!succp) {
free(client_rses);
@ -385,23 +391,23 @@ static void* newSession(
if (client_rses->rses_dcb[BE_SLAVE] == NULL) {
ss_dassert(session->refcount == 1);
free(client_rses);
free(client_rses);
return NULL;
}
/**
}
/**
* Open the master connection.
*/
*/
client_rses->rses_dcb[BE_MASTER] = dcb_connect(
local_backend[BE_MASTER]->backend_server,
session,
local_backend[BE_MASTER]->backend_server->protocol);
if (client_rses->rses_dcb[BE_MASTER] == NULL)
{
{
/** Close slave connection first. */
client_rses->rses_dcb[BE_SLAVE]->func.close(client_rses->rses_dcb[BE_SLAVE]);
free(client_rses);
return NULL;
}
}
/**
* We now have a master and a slave server with the least connections.
* Bump the connection counts for these servers.
@ -411,7 +417,7 @@ static void* newSession(
client_rses->rses_backend[BE_SLAVE] = local_backend[BE_SLAVE];
client_rses->rses_backend[BE_MASTER] = local_backend[BE_MASTER];
router->stats.n_sessions += 1;
router->stats.n_sessions += 1;
client_rses->rses_capabilities = RCAP_TYPE_STMT_INPUT;
/**
@ -423,15 +429,17 @@ static void* newSession(
* Add this session to end of the list of active sessions in router.
*/
spinlock_acquire(&router->lock);
client_rses->next = router->connections;
client_rses->next = router->connections;
router->connections = client_rses;
spinlock_release(&router->lock);
CHK_CLIENT_RSES(client_rses);
CHK_CLIENT_RSES(client_rses);
return (void *)client_rses;
}
/**
* Close a session with the router, this is the mechanism
* by which a router may cleanup data structure etc.
@ -446,7 +454,7 @@ static void closeSession(
ROUTER_CLIENT_SES* router_cli_ses;
DCB* slave_dcb;
DCB* master_dcb;
router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
CHK_CLIENT_RSES(router_cli_ses);
/**
@ -457,7 +465,7 @@ static void closeSession(
/** decrease server current connection counters */
atomic_add(&router_cli_ses->rses_backend[BE_SLAVE]->backend_server->stats.n_current, -1);
atomic_add(&router_cli_ses->rses_backend[BE_MASTER]->backend_server->stats.n_current, -1);
slave_dcb = router_cli_ses->rses_dcb[BE_SLAVE];
router_cli_ses->rses_dcb[BE_SLAVE] = NULL;
master_dcb = router_cli_ses->rses_dcb[BE_MASTER];
@ -466,14 +474,14 @@ static void closeSession(
router_cli_ses->rses_closed = true;
/** Unlock */
rses_end_locked_router_action(router_cli_ses);
/**
/**
* Close the backend server connections
*/
if (slave_dcb != NULL) {
CHK_DCB(slave_dcb);
slave_dcb->func.close(slave_dcb);
}
}
if (master_dcb != NULL) {
master_dcb->func.close(master_dcb);
@ -491,8 +499,8 @@ static void freeSession(
int i;
router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session;
router = (ROUTER_INSTANCE *)router_instance;
router = (ROUTER_INSTANCE *)router_instance;
atomic_add(&router_cli_ses->rses_backend[BE_SLAVE]->backend_conn_count, -1);
atomic_add(&router_cli_ses->rses_backend[BE_MASTER]->backend_conn_count, -1);
@ -560,24 +568,26 @@ static int routeQuery(
void* router_session,
GWBUF* querybuf)
{
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
GWBUF* plainsqlbuf = NULL;
char* querystr = NULL;
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
GWBUF* plainsqlbuf = NULL;
char* querystr = NULL;
char* startpos;
unsigned char packet_type;
uint8_t* packet;
int ret = 0;
DCB* master_dcb = NULL;
DCB* slave_dcb = NULL;
DCB* master_dcb = NULL;
DCB* slave_dcb = NULL;
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
bool rses_is_closed;
rses_property_t* prop;
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);
/** Dirty read for quick check if router is closed. */
if (router_cli_ses->rses_closed)
@ -609,16 +619,16 @@ static int routeQuery(
"Error: Failed to route %s:%s:\"%s\" to "
"backend server. %s.",
STRPACKETTYPE(packet_type),
STRQTYPE(qtype),
(querystr == NULL ? "(empty)" : querystr),
(rses_is_closed ? "Router was closed" :
"Router has no backend servers where to "
"route to"))));
STRQTYPE(qtype),
(querystr == NULL ? "(empty)" : querystr),
(rses_is_closed ? "Router was closed" :
"Router has no backend servers where to "
"route to"))));
goto return_ret;
}
inst->stats.n_queries++;
startpos = (char *)&packet[5];
switch(packet_type) {
case COM_QUIT: /**< 1 QUIT will close all sessions */
case COM_INIT_DB: /**< 2 DDL must go to the master */
@ -647,7 +657,7 @@ static int routeQuery(
querystr = master_dcb->func.getquerystr(
(void *) gwbuf_clone(querybuf),
&querystr_is_copy);
*/
*/
qtype = skygw_query_classifier_get_type(querystr, 0);
break;
@ -669,207 +679,102 @@ static int routeQuery(
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE,
"Packet type\t%s",
STRPACKETTYPE(packet_type))));
if (QUERY_IS_TYPE(qtype,QUERY_TYPE_COMMIT) &&
transaction_active)
/**
* If autocommit is disabled or transaction is explicitly started
* 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 &&
QUERY_IS_TYPE(qtype, QUERY_TYPE_BEGIN_TRX))
{
router_cli_ses->rses_transaction_active = true;
}
switch (qtype) {
case QUERY_TYPE_WRITE:
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] Query type\t%s, "
"routing to Master.",
pthread_self(),
STRQTYPE(qtype))));
LOGIF(LT, tracelog_routed_query(router_cli_ses,
"routeQuery",
master_dcb,
gwbuf_clone(querybuf)));
ret = master_dcb->func.write(master_dcb, querybuf);
atomic_add(&inst->stats.n_master, 1);
goto return_ret;
break;
case QUERY_TYPE_READ:
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] Query type\t%s, "
"routing to %s.",
pthread_self(),
STRQTYPE(qtype),
(transaction_active ? "Master" : "Slave"))));
LOGIF(LT, tracelog_routed_query(
/**
* Explicit COMMIT and ROLLBACK, implicit COMMIT.
*/
if (router_cli_ses->rses_autocommit_enabled &&
router_cli_ses->rses_transaction_active &&
(QUERY_IS_TYPE(qtype,QUERY_TYPE_COMMIT) ||
QUERY_IS_TYPE(qtype,QUERY_TYPE_ROLLBACK)))
{
router_cli_ses->rses_transaction_active = false;
}
else if (!router_cli_ses->rses_autocommit_enabled &&
QUERY_IS_TYPE(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT))
{
router_cli_ses->rses_autocommit_enabled = true;
router_cli_ses->rses_transaction_active = false;
}
/**
* Session update is always routed in the same way.
*/
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE))
{
bool succp = route_session_write(
router_cli_ses,
"routeQuery",
(transaction_active ? master_dcb : slave_dcb),
gwbuf_clone(querybuf)));
if (transaction_active)
{
ret = master_dcb->func.write(master_dcb, querybuf);
}
else
{
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);
goto return_ret;
break;
querybuf,
inst,
packet_type,
qtype);
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
{
/** Log error */
}
/** Execute session command in slave */
if (execute_sescmd_in_backend(router_cli_ses, BE_SLAVE))
if (succp)
{
ret = 1;
}
else
{
/** Log error */
}
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
atomic_add(&inst->stats.n_all, 1);
ss_dassert(succp);
ss_dassert(ret == 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;
}
else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) &&
!router_cli_ses->rses_transaction_active)
{
bool succp;
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(
LOGFILE_TRACE,
"%lu [routeQuery:rwsplit] Query type\t%s, "
"routing to Master by default.",
pthread_self(),
STRQTYPE(qtype))));
LOGFILE_TRACE,
"Read-only query, routing to Slave.")));
ss_dassert(QUERY_IS_TYPE(qtype, QUERY_TYPE_READ));
/**
* 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 = slave_dcb->func.write(slave_dcb, querybuf);
atomic_add(&inst->stats.n_slave, 1);
ret = master_dcb->func.write(master_dcb, querybuf);
atomic_add(&inst->stats.n_master, 1);
goto return_ret;
break;
} /*< switch by query type */
}
else
{
bool succp = true;
if (LOG_IS_ENABLED(LOGFILE_TRACE))
{
if (router_cli_ses->rses_transaction_active) /*< all to master */
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Transaction is active, routing to Master.")));
}
else
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Begin transaction, write or unspecified type, "
"routing to Master.")));
}
}
ret = master_dcb->func.write(master_dcb, querybuf);
atomic_add(&inst->stats.n_master, 1);
goto return_ret;
}
return_ret:
if (plainsqlbuf != NULL)
@ -1036,8 +941,7 @@ static void clientReply(
client_dcb = backend_dcb->session->client;
/** 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.
* 2. Check sescmd's state whether OK_PACKET has been
@ -1057,14 +961,14 @@ static void clientReply(
}
if (backend_dcb == master_dcb)
{
{
be_type = BE_MASTER;
}
}
else if (backend_dcb == slave_dcb)
{
{
be_type = BE_SLAVE;
}
LOGIF(LT, tracelog_routed_query(router_cli_ses,
}
LOGIF(LT, tracelog_routed_query(router_cli_ses,
"reply_by_statement",
backend_dcb,
gwbuf_clone(writebuf)));
@ -1085,8 +989,7 @@ static void clientReply(
{
writebuf = sescmd_cursor_process_replies(client_dcb,
writebuf,
scur);
scur);
}
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
@ -1123,7 +1026,7 @@ lock_failed:
*
* @param inst - in, use
* Pointer to router instance
*
*
* @return true, if all what what requested found, false if the request
* was not satisfied or was partially satisfied.
*
@ -1134,12 +1037,12 @@ lock_failed:
static bool search_backend_servers(
BACKEND** p_master,
BACKEND** p_slave,
ROUTER_INSTANCE* router)
{
ROUTER_INSTANCE* router)
{
BACKEND* local_backend[BE_COUNT] = {NULL,NULL};
int i;
int i;
bool succp = true;
/*
* Loop over all the servers and find any that have fewer connections
* than current candidate server.
@ -1154,32 +1057,32 @@ static bool search_backend_servers(
* very low load.
*
* If master is searched for, the first master found is chosen.
*/
*/
for (i = 0; router->servers[i] != NULL; i++) {
BACKEND* be = router->servers[i];
if (be != NULL) {
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"%lu [search_backend_servers] Examine server "
"%s:%d with %d connections. Status is %d, "
"router->bitvalue is %d",
pthread_self(),
"%s:%d with %d connections. Status is %d, "
"router->bitvalue is %d",
pthread_self(),
be->backend_server->name,
be->backend_server->port,
be->backend_conn_count,
be->backend_server->status,
router->bitmask)));
router->bitmask)));
}
if (be != NULL &&
SERVER_IS_RUNNING(be->backend_server) &&
(be->backend_server->status & router->bitmask) ==
router->bitvalue)
{
{
if (SERVER_IS_SLAVE(be->backend_server) &&
p_slave != NULL)
{
{
/**
* If no candidate set, set first running
* server as an initial candidate server.
@ -1187,7 +1090,7 @@ static bool search_backend_servers(
if (local_backend[BE_SLAVE] == NULL)
{
local_backend[BE_SLAVE] = be;
}
}
else if (be->backend_conn_count <
local_backend[BE_SLAVE]->backend_conn_count)
{
@ -1203,74 +1106,74 @@ static bool search_backend_servers(
be->backend_server->stats.n_connections <
local_backend[BE_SLAVE]->backend_server->stats.n_connections)
{
/**
/**
* This running server has the same
* number of connections currently
* as the candidate but has had
* fewer connections over time
* than candidate, set this server
* to candidate.
*/
*/
local_backend[BE_SLAVE] = be;
}
}
}
}
else if (p_master != NULL &&
local_backend[BE_MASTER] == NULL &&
SERVER_IS_MASTER(be->backend_server))
{
{
local_backend[BE_MASTER] = be;
}
}
else if (p_master != NULL &&
local_backend[BE_JOINED] == NULL &&
SERVER_IS_JOINED(be->backend_server))
{
{
local_backend[BE_JOINED] = be;
}
}
}
}
}
}
if (router->bitvalue != 0 &&
p_master != NULL &&
local_backend[BE_JOINED] == NULL)
{
{
succp = false;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
LOGFILE_ERROR,
"Error : Couldn't find a Joined Galera node from %d "
"candidates.",
"candidates.",
i)));
goto return_succp;
}
if (p_slave != NULL && local_backend[BE_SLAVE] == NULL) {
succp = false;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Couldn't find suitable Slave from %d "
"candidates.",
"candidates.",
i)));
}
if (p_master != NULL && local_backend[BE_MASTER] == NULL) {
succp = false;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Couldn't find suitable Master from %d "
"candidates.",
"candidates.",
i)));
}
}
if (local_backend[BE_SLAVE] != NULL) {
*p_slave = local_backend[BE_SLAVE];
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"%lu [readwritesplit:search_backend_servers] Selected "
"Slave %s:%d from %d candidates.",
pthread_self(),
local_backend[BE_SLAVE]->backend_server->name,
local_backend[BE_SLAVE]->backend_server->port,
i)));
}
}
if (local_backend[BE_MASTER] != NULL) {
*p_master = local_backend[BE_MASTER];
LOGIF(LT, (skygw_log_write(
@ -1630,7 +1533,7 @@ static bool execute_sescmd_in_backend(
sescmd_cursor_t* scur;
dcb = rses->rses_dcb[be_type];
CHK_DCB(dcb);
CHK_CLIENT_RSES(rses);
ss_dassert(SPINLOCK_IS_LOCKED(&rses->rses_lock));
@ -1665,7 +1568,7 @@ static bool execute_sescmd_in_backend(
dcb->session,
sescmd_cursor_clone_querybuf(scur));
break;
case COM_QUIT:
case COM_QUERY:
case COM_INIT_DB:
@ -1690,6 +1593,7 @@ return_succp:
return succp;
}
/**
* Moves cursor to next property and copied address of its sescmd to cursor.
* Current propery must be non-null.
@ -1767,7 +1671,6 @@ static rses_property_t* mysql_sescmd_get_property(
return scmd->my_sescmd_prop;
}
static void tracelog_routed_query(
ROUTER_CLIENT_SES* rses,
char* funcname,
@ -1781,7 +1684,7 @@ static void tracelog_routed_query(
char* querystr;
char* startpos = (char *)&packet[5];
backend_type_t be_type;
if (rses->rses_dcb[BE_MASTER] == dcb)
{
be_type = BE_MASTER;
@ -1824,11 +1727,13 @@ static void tracelog_routed_query(
-1)),
STRBETYPE(be_type),
dcb)));
free(querystr);
}
}
gwbuf_free(buf);
}
/**
* Return rc, rc < 0 if router session is closed. rc == 0 if there are no
* capabilities specified, rc > 0 when there are capabilities.
@ -1851,4 +1756,103 @@ static uint8_t getCapabilities (
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
CC=cc
TESTLOG := $(shell pwd)/testrouters.log
TESTLOG := $(shell pwd)/testrwsplit.log
RET := -1
cleantests:
@ -25,17 +25,11 @@ buildtests:
runtests:
@echo "" >> $(TESTLOG)
@echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG)
@echo "Test MaxScale R/W Split" >> $(TESTLOG)
@echo "Test Read/Write split router" >> $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG)
./rwsplit.sh $(TESTLOG) $(THOST) $(TPORT_RW) $(TMASTER_ID) $(TUSER) $(TPWD)
@echo "" >> $(TESTLOG)
pesce:
@echo "fine"
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -30,9 +30,46 @@ else
echo "$TINPUT PASSED">>$TLOG ;
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
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
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
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
else
@ -113,3 +150,44 @@ if [ "$a" == "$TRETVAL" ]; then
else
echo "$TINPUT PASSED">>$TLOG ;
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;
SET autocommit=0;
SET autocommit=1;
BEGIN;
CREATE DATABASE FOO; -- implicit commit
SELECT (@@server_id) INTO @a;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
USE test;
DROP TABLE IF EXISTS T1;
CREATE TABLE T1 (id integer); -- implicit commit
SET autocommit=0;
SET autocommit=1;
BEGIN;
CREATE INDEX foo_t1 on T1 (id); -- implicit commit
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;
SET autocommit = 0;
SET autocommit = Off;
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;
COMMIT;
START TRANSACTION;
DELETE FROM myCity;
SELECT COUNT(*) FROM myCity; -- read transaction's modifications from slave
SELECT COUNT(*) FROM myCity; -- read transaction's modifications from master
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 ../../makefile.inc
include ../../test.inc
TEST_ROOT := $(ROOT_PATH)/test
PARENT_DIR := $(ROOT_PATH)/server
@ -39,8 +40,15 @@ testall:
@echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG)
@echo "Test Server Core" >> $(TESTLOG)
$(MAKE) -C $(ROOT_PATH)/server/core testall
@echo "Query Classifier PASSED" >> $(TESTLOG)
$(MAKE) -C $(ROOT_PATH)/server/core testall
@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:
@ -51,7 +59,7 @@ else
endif
runtests:
@echo "" >> $(TESTLOG)
@echo "" > $(TESTLOG)
@echo "-------------------------------" >> $(TESTLOG)
@echo $(shell date) >> $(TESTLOG)
@echo "Test MaxScale server" >> $(TESTLOG)
@ -63,5 +71,4 @@ runtests:
@sleep 5
@echo "MaxScale server PASSED" >> $(TESTLOG)
@echo "" >> $(TESTLOG)
@killall maxscale
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)

View File

@ -32,4 +32,10 @@ TPWD :=
# master's server_id, for example:
# 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,
query_classifier,
server,
utils
utils,
modules
|
|- utils cleantests, buildtests, runtests, testall
| |

View File

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