Merge branch 'master' into blr
This commit is contained in:
parent
29d247319d
commit
9777e83a5a
BIN
Documentation/Max Scale 0.6 Release Notes.pdf
Normal file
BIN
Documentation/Max Scale 0.6 Release Notes.pdf
Normal file
Binary file not shown.
Binary file not shown.
2
README
2
README
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
@ -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.
@ -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
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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)
|
@ -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 *);
|
||||
|
@ -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 *);
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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++;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
@ -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
|
||||
|
||||
|
@ -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;
|
@ -0,0 +1,2 @@
|
||||
use test;
|
||||
select @@server_id;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
@ -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;
|
@ -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
|
@ -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;
|
@ -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;
|
@ -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)
|
||||
|
8
test.inc
8
test.inc
@ -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 :=
|
||||
|
@ -57,7 +57,8 @@ MAXSCALE_HOME
|
||||
log_manager,
|
||||
query_classifier,
|
||||
server,
|
||||
utils
|
||||
utils,
|
||||
modules
|
||||
|
|
||||
|- utils cleantests, buildtests, runtests, testall
|
||||
| |
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user