Merge branch 'Z3' of https://github.com/skysql/MaxScale into Z3
Conflicts: gcov.diff server/core/buffer.c server/include/buffer.h server/modules/routing/readwritesplit/readwritesplit.c
This commit is contained in:
Binary file not shown.
BIN
Documentation/MaxScale Configuration And Usage Scenarios-Z2.pdf
Normal file
BIN
Documentation/MaxScale Configuration And Usage Scenarios-Z2.pdf
Normal file
Binary file not shown.
Binary file not shown.
BIN
Documentation/MaxScale Debug And Diagnostic Support-Z2.pdf
Normal file
BIN
Documentation/MaxScale Debug And Diagnostic Support-Z2.pdf
Normal file
Binary file not shown.
BIN
Documentation/MaxScale MySQL Cluster setup-Z2.pdf
Normal file
BIN
Documentation/MaxScale MySQL Cluster setup-Z2.pdf
Normal file
Binary file not shown.
BIN
Documentation/internal/hint_syntax.pdf
Normal file
BIN
Documentation/internal/hint_syntax.pdf
Normal file
Binary file not shown.
138
gcov.diff
138
gcov.diff
@ -1,138 +0,0 @@
|
|||||||
diff --git a/makefile.inc b/makefile.inc
|
|
||||||
index f2d93bf..c7dbffa 100644
|
|
||||||
--- a/makefile.inc
|
|
||||||
+++ b/makefile.inc
|
|
||||||
@@ -24,8 +24,8 @@ endif
|
|
||||||
|
|
||||||
# -O2 -g -pipe -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector --param=ssp-buffer-size=4 -fPIC
|
|
||||||
|
|
||||||
-CFLAGS := $(CFLAGS) -Wall
|
|
||||||
-LDLIBS := $(LDLIBS) -pthread
|
|
||||||
+CFLAGS := $(CFLAGS) -Wall -fprofile-arcs -ftest-coverage
|
|
||||||
+LDLIBS := $(LDLIBS) -pthread -lgcov
|
|
||||||
LDMYSQL := -lmysqld
|
|
||||||
CPP_LDLIBS := -lstdc++
|
|
||||||
|
|
||||||
diff --git a/server/core/Makefile b/server/core/Makefile
|
|
||||||
index 9bf650c..9df75a7 100644
|
|
||||||
--- a/server/core/Makefile
|
|
||||||
+++ b/server/core/Makefile
|
|
||||||
@@ -75,7 +75,7 @@ POBJS=maxpasswd.o secrets.o utils.o
|
|
||||||
LIBS=-L$(EMBEDDED_LIB) \
|
|
||||||
-lmysqld \
|
|
||||||
-lz -lm -lcrypt -lcrypto -ldl -laio -lrt -pthread -llog_manager \
|
|
||||||
- -L../inih/extra -linih -lssl -lstdc++
|
|
||||||
+ -L../inih/extra -linih -lssl -lstdc++ -lgcov
|
|
||||||
|
|
||||||
all: maxscale maxkeys maxpasswd
|
|
||||||
|
|
||||||
diff --git a/server/modules/filter/Makefile b/server/modules/filter/Makefile
|
|
||||||
index 931c35a..d5dcca9 100644
|
|
||||||
--- a/server/modules/filter/Makefile
|
|
||||||
+++ b/server/modules/filter/Makefile
|
|
||||||
@@ -25,7 +25,7 @@ UTILSPATH := $(ROOT_PATH)/utils
|
|
||||||
|
|
||||||
CC=cc
|
|
||||||
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
|
||||||
- -I$(UTILSPATH) -Wall -g
|
|
||||||
+ -I$(UTILSPATH) -Wall -g -fprofile-arcs -ftest-coverage
|
|
||||||
|
|
||||||
include ../../../makefile.inc
|
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ TEESRCS=tee.c
|
|
||||||
TEEOBJ=$(TEESRCS:.c=.o)
|
|
||||||
SRCS=$(TESTSRCS) $(QLASRCS) $(REGEXSRCS) $(TOPNSRCS) $(TEESRCS)
|
|
||||||
OBJ=$(SRCS:.c=.o)
|
|
||||||
-LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
|
||||||
+LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager -lgcov
|
|
||||||
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so libtopfilter.so libtee.so
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/server/modules/monitor/Makefile b/server/modules/monitor/Makefile
|
|
||||||
index 7fdbc58..bca01de 100644
|
|
||||||
--- a/server/modules/monitor/Makefile
|
|
||||||
+++ b/server/modules/monitor/Makefile
|
|
||||||
@@ -28,7 +28,7 @@ CFLAGS=-c -fPIC -I. -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
|
||||||
|
|
||||||
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
|
||||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) \
|
|
||||||
- -Wl,-rpath,$(EMBEDDED_LIB)
|
|
||||||
+ -Wl,-rpath,$(EMBEDDED_LIB) -fprofile-arcs -ftest-coverage
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ GALERAOBJ=$(GALERASRCS:.c=.o)
|
|
||||||
SRCS=$(MYSQLSRCS)
|
|
||||||
OBJ=$(SRCS:.c=.o)
|
|
||||||
LIBS=$(UTILSPATH)/skygw_utils.o -llog_manager \
|
|
||||||
- -L$(EMBEDDED_LIB) -lmysqld
|
|
||||||
+ -L$(EMBEDDED_LIB) -lmysqld -lgcov
|
|
||||||
MODULES=libmysqlmon.so libgaleramon.so
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/server/modules/protocol/Makefile b/server/modules/protocol/Makefile
|
|
||||||
index 54a8f8c..c8913ab 100644
|
|
||||||
--- a/server/modules/protocol/Makefile
|
|
||||||
+++ b/server/modules/protocol/Makefile
|
|
||||||
@@ -31,7 +31,7 @@ UTILSPATH := $(ROOT_PATH)/utils
|
|
||||||
|
|
||||||
CC=cc
|
|
||||||
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
|
||||||
- -I$(UTILSPATH) -Wall -g
|
|
||||||
+ -I$(UTILSPATH) -Wall -g -fprofile-arcs -ftest-coverage
|
|
||||||
|
|
||||||
include ../../../makefile.inc
|
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ MAXSCALEDOBJ=$(MAXSCALEDSRCS:.c=.o)
|
|
||||||
SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS) $(TELNETDSRCS) $(HTTPDSRCS) \
|
|
||||||
$(MAXSCALEDSRCS)
|
|
||||||
OBJ=$(SRCS:.c=.o)
|
|
||||||
-LIBS=$(UTILSPATH)/skygw_utils.o
|
|
||||||
+LIBS=$(UTILSPATH)/skygw_utils.o -lgcov
|
|
||||||
MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so \
|
|
||||||
libmaxscaled.so
|
|
||||||
|
|
||||||
diff --git a/server/modules/routing/Makefile b/server/modules/routing/Makefile
|
|
||||||
index 4feac68..afd1da7 100644
|
|
||||||
--- a/server/modules/routing/Makefile
|
|
||||||
+++ b/server/modules/routing/Makefile
|
|
||||||
@@ -29,7 +29,7 @@ UTILSPATH := $(ROOT_PATH)/utils
|
|
||||||
|
|
||||||
CC=cc
|
|
||||||
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
|
||||||
- -I$(UTILSPATH) -Wall -g
|
|
||||||
+ -I$(UTILSPATH) -Wall -g -fprofile-arcs -ftest-coverage
|
|
||||||
|
|
||||||
include ../../../makefile.inc
|
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ CLISRCS=cli.c debugcmd.c
|
|
||||||
CLIOBJ=$(CLISRCS:.c=.o)
|
|
||||||
SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) cli.c
|
|
||||||
OBJ=$(SRCS:.c=.o)
|
|
||||||
-LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
|
||||||
+LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager -lgcov
|
|
||||||
MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/server/modules/routing/readwritesplit/Makefile b/server/modules/routing/readwritesplit/Makefile
|
|
||||||
index c60f2ff..a3a643e 100644
|
|
||||||
--- a/server/modules/routing/readwritesplit/Makefile
|
|
||||||
+++ b/server/modules/routing/readwritesplit/Makefile
|
|
||||||
@@ -27,7 +27,7 @@ QCLASSPATH := $(ROOT_PATH)/query_classifier
|
|
||||||
CC=cc
|
|
||||||
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include \
|
|
||||||
-I$(LOGPATH) -I$(UTILSPATH) -I$(QCLASSPATH) \
|
|
||||||
- $(MYSQL_HEADERS) -Wall -g
|
|
||||||
+ $(MYSQL_HEADERS) -Wall -g -fprofile-arcs -ftest-coverage
|
|
||||||
|
|
||||||
include ../../../../makefile.inc
|
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ LDFLAGS=-shared -L$(LOGPATH) -L$(QCLASSPATH) -L$(EMBEDDED_LIB) \
|
|
||||||
|
|
||||||
SRCS=readwritesplit.c
|
|
||||||
OBJ=$(SRCS:.c=.o)
|
|
||||||
-LIBS=-lssl -pthread -llog_manager -lquery_classifier -lmysqld
|
|
||||||
+LIBS=-lssl -pthread -llog_manager -lquery_classifier -lmysqld -lgcov
|
|
||||||
MODULES=libreadwritesplit.so
|
|
||||||
|
|
||||||
all: $(MODULES)
|
|
@ -32,8 +32,9 @@
|
|||||||
# are behind SS_DEBUG macros.
|
# are behind SS_DEBUG macros.
|
||||||
# 29/06/13 Vilho Raatikka Reverted Query classifier changes because
|
# 29/06/13 Vilho Raatikka Reverted Query classifier changes because
|
||||||
# gateway needs mysql client lib, not qc.
|
# gateway needs mysql client lib, not qc.
|
||||||
# 24/07/13 Mark Ridoch Addition of encryption routines
|
# 24/07/13 Mark Riddoch Addition of encryption routines
|
||||||
# 30/05/14 Mark Ridoch Filter API added
|
# 30/05/14 Mark Riddoch Filter API added
|
||||||
|
# 25/07/14 Mark Riddoch Addition of hints
|
||||||
|
|
||||||
include ../../build_gateway.inc
|
include ../../build_gateway.inc
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \
|
|||||||
SRCS= atomic.c buffer.c spinlock.c gateway.c \
|
SRCS= atomic.c buffer.c spinlock.c gateway.c \
|
||||||
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c \
|
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c \
|
||||||
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \
|
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \
|
||||||
monitor.c adminusers.c secrets.c filter.c modutil.c
|
monitor.c adminusers.c secrets.c filter.c modutil.c hint.c
|
||||||
|
|
||||||
HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
|
HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
|
||||||
../include/gw.h ../modules/include/mysql_client_server_protocol.h \
|
../include/gw.h ../modules/include/mysql_client_server_protocol.h \
|
||||||
@ -65,7 +66,7 @@ HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
|
|||||||
../include/modules.h ../include/poll.h ../include/config.h \
|
../include/modules.h ../include/poll.h ../include/config.h \
|
||||||
../include/users.h ../include/hashtable.h ../include/gwbitmask.h \
|
../include/users.h ../include/hashtable.h ../include/gwbitmask.h \
|
||||||
../include/adminusers.h ../include/version.h ../include/maxscale.h \
|
../include/adminusers.h ../include/version.h ../include/maxscale.h \
|
||||||
../include/filter.h modutil.h
|
../include/filter.h modutil.h hint.h
|
||||||
|
|
||||||
OBJ=$(SRCS:.c=.o)
|
OBJ=$(SRCS:.c=.o)
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
* 11/07/13 Mark Riddoch Add reference count mechanism
|
* 11/07/13 Mark Riddoch Add reference count mechanism
|
||||||
* 16/07/2013 Massimiliano Pinto Added command type to gwbuf struct
|
* 16/07/2013 Massimiliano Pinto Added command type to gwbuf struct
|
||||||
* 24/06/2014 Mark Riddoch Addition of gwbuf_trim
|
* 24/06/2014 Mark Riddoch Addition of gwbuf_trim
|
||||||
|
* 15/07/2014 Mark Riddoch Addition of properties
|
||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
@ -88,6 +89,8 @@ SHARED_BUF *sbuf;
|
|||||||
sbuf->refcount = 1;
|
sbuf->refcount = 1;
|
||||||
rval->sbuf = sbuf;
|
rval->sbuf = sbuf;
|
||||||
rval->next = NULL;
|
rval->next = NULL;
|
||||||
|
rval->hint = NULL;
|
||||||
|
rval->properties = NULL;
|
||||||
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
|
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
|
||||||
rval->gwbuf_info = GWBUF_INFO_NONE;
|
rval->gwbuf_info = GWBUF_INFO_NONE;
|
||||||
rval->gwbuf_bufobj = NULL;
|
rval->gwbuf_bufobj = NULL;
|
||||||
@ -103,6 +106,8 @@ SHARED_BUF *sbuf;
|
|||||||
void
|
void
|
||||||
gwbuf_free(GWBUF *buf)
|
gwbuf_free(GWBUF *buf)
|
||||||
{
|
{
|
||||||
|
BUF_PROPERTY *prop;
|
||||||
|
|
||||||
buffer_object_t* bo;
|
buffer_object_t* bo;
|
||||||
|
|
||||||
CHK_GWBUF(buf);
|
CHK_GWBUF(buf);
|
||||||
@ -116,6 +121,22 @@ gwbuf_free(GWBUF *buf)
|
|||||||
{
|
{
|
||||||
bo = gwbuf_remove_buffer_object(buf, bo);
|
bo = gwbuf_remove_buffer_object(buf, bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
while (buf->properties)
|
||||||
|
{
|
||||||
|
prop = buf->properties;
|
||||||
|
buf->properties = prop->next;
|
||||||
|
free(prop->name);
|
||||||
|
free(prop->value);
|
||||||
|
free(prop);
|
||||||
|
}
|
||||||
|
/** Release the hint */
|
||||||
|
while (buf->hint)
|
||||||
|
{
|
||||||
|
HINT* h = buf->hint;
|
||||||
|
buf->hint = buf->hint->next;
|
||||||
|
hint_free(h);
|
||||||
}
|
}
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
@ -145,6 +166,8 @@ GWBUF *rval;
|
|||||||
rval->start = buf->start;
|
rval->start = buf->start;
|
||||||
rval->end = buf->end;
|
rval->end = buf->end;
|
||||||
rval->gwbuf_type = buf->gwbuf_type;
|
rval->gwbuf_type = buf->gwbuf_type;
|
||||||
|
rval->properties = NULL;
|
||||||
|
rval->hint = NULL;
|
||||||
rval->gwbuf_info = buf->gwbuf_info;
|
rval->gwbuf_info = buf->gwbuf_info;
|
||||||
rval->gwbuf_bufobj = buf->gwbuf_bufobj;
|
rval->gwbuf_bufobj = buf->gwbuf_bufobj;
|
||||||
rval->next = NULL;
|
rval->next = NULL;
|
||||||
@ -173,6 +196,8 @@ GWBUF *gwbuf_clone_portion(
|
|||||||
clonebuf->start = (void *)((char*)buf->start)+start_offset;
|
clonebuf->start = (void *)((char*)buf->start)+start_offset;
|
||||||
clonebuf->end = (void *)((char *)clonebuf->start)+length;
|
clonebuf->end = (void *)((char *)clonebuf->start)+length;
|
||||||
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */
|
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */
|
||||||
|
clonebuf->properties = NULL;
|
||||||
|
clonebuf->hint = NULL;
|
||||||
clonebuf->gwbuf_info = buf->gwbuf_info;
|
clonebuf->gwbuf_info = buf->gwbuf_info;
|
||||||
clonebuf->gwbuf_bufobj = buf->gwbuf_bufobj;
|
clonebuf->gwbuf_bufobj = buf->gwbuf_bufobj;
|
||||||
clonebuf->next = NULL;
|
clonebuf->next = NULL;
|
||||||
@ -424,7 +449,6 @@ void* gwbuf_get_buffer_object_data(
|
|||||||
return bo->bo_data;
|
return bo->bo_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return pointer to next buffer object or NULL
|
* @return pointer to next buffer object or NULL
|
||||||
*/
|
*/
|
||||||
@ -441,3 +465,109 @@ static buffer_object_t* gwbuf_remove_buffer_object(
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a property to a buffer.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to add the property to
|
||||||
|
* @param name The property name
|
||||||
|
* @param value The property value
|
||||||
|
* @return Non-zero on success
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
gwbuf_add_property(GWBUF *buf, char *name, char *value)
|
||||||
|
{
|
||||||
|
BUF_PROPERTY *prop;
|
||||||
|
|
||||||
|
if ((prop = malloc(sizeof(BUF_PROPERTY))) == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
prop->name = strdup(name);
|
||||||
|
prop->value = strdup(value);
|
||||||
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
|
prop->next = buf->properties;
|
||||||
|
buf->properties = prop;
|
||||||
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the value of a buffer property
|
||||||
|
* @param buf The buffer itself
|
||||||
|
* @param name The name of the property to return
|
||||||
|
* @return The property value or NULL if the property was not found.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
gwbuf_get_property(GWBUF *buf, char *name)
|
||||||
|
{
|
||||||
|
BUF_PROPERTY *prop;
|
||||||
|
|
||||||
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
|
prop = buf->properties;
|
||||||
|
while (prop && strcmp(prop->name, name) != 0)
|
||||||
|
prop = prop->next;
|
||||||
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
|
if (prop)
|
||||||
|
return prop->value;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a chain of GWBUF structures into a single GWBUF structure
|
||||||
|
*
|
||||||
|
* @param orig The chain to convert
|
||||||
|
* @return The contiguous buffer
|
||||||
|
*/
|
||||||
|
GWBUF *
|
||||||
|
gwbuf_make_contiguous(GWBUF *orig)
|
||||||
|
{
|
||||||
|
GWBUF *newbuf;
|
||||||
|
char *ptr;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (orig->next == NULL)
|
||||||
|
return orig;
|
||||||
|
|
||||||
|
if ((newbuf = gwbuf_alloc(gwbuf_length(orig))) != NULL)
|
||||||
|
{
|
||||||
|
ptr = GWBUF_DATA(newbuf);
|
||||||
|
while (orig)
|
||||||
|
{
|
||||||
|
len = GWBUF_LENGTH(orig);
|
||||||
|
memcpy(ptr, GWBUF_DATA(orig), len);
|
||||||
|
ptr += len;
|
||||||
|
orig = gwbuf_consume(orig, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add hint to a buffer.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to add the hint to
|
||||||
|
* @param hint The hint itself
|
||||||
|
* @return Non-zero on success
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
gwbuf_add_hint(GWBUF *buf, HINT *hint)
|
||||||
|
{
|
||||||
|
HINT *ptr;
|
||||||
|
|
||||||
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
|
if (buf->hint)
|
||||||
|
{
|
||||||
|
ptr = buf->hint;
|
||||||
|
while (ptr->next)
|
||||||
|
ptr = ptr->next;
|
||||||
|
ptr->next = hint;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf->hint = hint;
|
||||||
|
}
|
||||||
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
153
server/core/hint.c
Normal file
153
server/core/hint.c
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* This file is distributed as part of MaxScale. 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 2014
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <hint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file hint.c generic support routines for hints.
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 25/07/14 Mark Riddoch Initial implementation
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate a list of hints
|
||||||
|
*
|
||||||
|
* @param hint The hint list to duplicate
|
||||||
|
* @return A duplicate of the list
|
||||||
|
*/
|
||||||
|
HINT *
|
||||||
|
hint_dup(HINT *hint)
|
||||||
|
{
|
||||||
|
HINT *nlhead = NULL, *nltail = NULL, *ptr1, *ptr2;
|
||||||
|
|
||||||
|
ptr1 = hint;
|
||||||
|
while (ptr1)
|
||||||
|
{
|
||||||
|
if ((ptr2 = (HINT *)malloc(sizeof(HINT))) == NULL)
|
||||||
|
return nlhead;
|
||||||
|
ptr2->type = ptr1->type;
|
||||||
|
if (ptr1->data)
|
||||||
|
ptr2->data = strdup(ptr1->data);
|
||||||
|
else
|
||||||
|
ptr2->data = NULL;
|
||||||
|
if (ptr1->value)
|
||||||
|
ptr2->value = strdup(ptr1->value);
|
||||||
|
else
|
||||||
|
ptr2->value = NULL;
|
||||||
|
ptr2->next = NULL;
|
||||||
|
if (nltail)
|
||||||
|
{
|
||||||
|
nltail->next = ptr2;
|
||||||
|
nltail = ptr2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nlhead = ptr2;
|
||||||
|
nltail = ptr2;
|
||||||
|
}
|
||||||
|
ptr1 = ptr1->next;
|
||||||
|
}
|
||||||
|
return nlhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a ROUTE TO type hint
|
||||||
|
*
|
||||||
|
* @param head The current hint list
|
||||||
|
* @param type The HINT_TYPE
|
||||||
|
* @param data Data may be NULL or the name of a server to route to
|
||||||
|
* @return The result hint list
|
||||||
|
*/
|
||||||
|
HINT *
|
||||||
|
hint_create_route(HINT *head, HINT_TYPE type, char *data)
|
||||||
|
{
|
||||||
|
HINT *hint;
|
||||||
|
|
||||||
|
if ((hint = (HINT *)malloc(sizeof(HINT))) == NULL)
|
||||||
|
return head;
|
||||||
|
hint->next = head;
|
||||||
|
hint->type = type;
|
||||||
|
if (data)
|
||||||
|
hint->data = strdup(data);
|
||||||
|
else
|
||||||
|
hint->data = NULL;
|
||||||
|
hint->value = NULL;
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create name/value parameter hint
|
||||||
|
*
|
||||||
|
* @param head The current hint list
|
||||||
|
* @param pname The parameter name
|
||||||
|
* @param value The parameter value
|
||||||
|
* @return The result hint list
|
||||||
|
*/
|
||||||
|
HINT *
|
||||||
|
hint_create_parameter(HINT *head, char *pname, char *value)
|
||||||
|
{
|
||||||
|
HINT *hint;
|
||||||
|
|
||||||
|
if ((hint = (HINT *)malloc(sizeof(HINT))) == NULL)
|
||||||
|
return head;
|
||||||
|
hint->next = head;
|
||||||
|
hint->type = HINT_PARAMETER;
|
||||||
|
hint->data = pname;
|
||||||
|
hint->value = strdup(value);
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* free_hint - free a hint
|
||||||
|
*
|
||||||
|
* @param hint The hint to free
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hint_free(HINT *hint)
|
||||||
|
{
|
||||||
|
if (hint->data)
|
||||||
|
free(hint->data);
|
||||||
|
if (hint->value)
|
||||||
|
free(hint->value);
|
||||||
|
free(hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hint_exists(
|
||||||
|
HINT** p_hint,
|
||||||
|
HINT_TYPE type)
|
||||||
|
{
|
||||||
|
bool succp = false;
|
||||||
|
|
||||||
|
while (*p_hint != NULL)
|
||||||
|
{
|
||||||
|
if ((*p_hint)->type == type)
|
||||||
|
{
|
||||||
|
succp = true;
|
||||||
|
}
|
||||||
|
p_hint = &(*p_hint)->next;
|
||||||
|
}
|
||||||
|
return succp;
|
||||||
|
}
|
@ -79,7 +79,7 @@ unsigned char *ptr;
|
|||||||
*length += (*ptr++ << 8);
|
*length += (*ptr++ << 8);
|
||||||
ptr += 2; // Skip sequence id and COM_QUERY byte
|
ptr += 2; // Skip sequence id and COM_QUERY byte
|
||||||
*length = *length - 1;
|
*length = *length - 1;
|
||||||
*sql = (char *) ptr;
|
*sql = (char *)ptr;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +416,8 @@ char *status = NULL;
|
|||||||
strcat(status, "Slave, ");
|
strcat(status, "Slave, ");
|
||||||
if (server->status & SERVER_JOINED)
|
if (server->status & SERVER_JOINED)
|
||||||
strcat(status, "Synced, ");
|
strcat(status, "Synced, ");
|
||||||
|
if (server->status & SERVER_NDB)
|
||||||
|
strcat(status, "NDB, ");
|
||||||
if (server->status & SERVER_RUNNING)
|
if (server->status & SERVER_RUNNING)
|
||||||
strcat(status, "Running");
|
strcat(status, "Running");
|
||||||
else
|
else
|
||||||
|
@ -38,15 +38,30 @@
|
|||||||
* 10/06/2013 Mark Riddoch Initial implementation
|
* 10/06/2013 Mark Riddoch Initial implementation
|
||||||
* 11/07/2013 Mark Riddoch Addition of reference count in the gwbuf
|
* 11/07/2013 Mark Riddoch Addition of reference count in the gwbuf
|
||||||
* 16/07/2013 Massimiliano Pinto Added command type for the queue
|
* 16/07/2013 Massimiliano Pinto Added command type for the queue
|
||||||
|
* 10/07/2014 Mark Riddoch Addition of hints
|
||||||
|
* 15/07/2014 Mark Riddoch Added buffer properties
|
||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
|
#include <string.h>
|
||||||
#include <skygw_debug.h>
|
#include <skygw_debug.h>
|
||||||
|
#include <hint.h>
|
||||||
#include <spinlock.h>
|
#include <spinlock.h>
|
||||||
|
|
||||||
|
|
||||||
EXTERN_C_BLOCK_BEGIN
|
EXTERN_C_BLOCK_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer properties - used to store properties related to the buffer
|
||||||
|
* contents. This may be added at any point during the processing of the
|
||||||
|
* data, especially in the protocol stage of the processing.
|
||||||
|
*/
|
||||||
|
typedef struct buf_property {
|
||||||
|
char *name;
|
||||||
|
char *value;
|
||||||
|
struct buf_property *next;
|
||||||
|
} BUF_PROPERTY;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
GWBUF_TYPE_UNDEFINED = 0x00,
|
GWBUF_TYPE_UNDEFINED = 0x00,
|
||||||
@ -55,7 +70,8 @@ typedef enum
|
|||||||
GWBUF_TYPE_SINGLE_STMT = 0x04,
|
GWBUF_TYPE_SINGLE_STMT = 0x04,
|
||||||
GWBUF_TYPE_SESCMD_RESPONSE = 0x08,
|
GWBUF_TYPE_SESCMD_RESPONSE = 0x08,
|
||||||
GWBUF_TYPE_RESPONSE_END = 0x10,
|
GWBUF_TYPE_RESPONSE_END = 0x10,
|
||||||
GWBUF_TYPE_SESCMD = 0x20
|
GWBUF_TYPE_SESCMD = 0x20,
|
||||||
|
GWBUF_TYPE_HTTP = 0x40
|
||||||
} gwbuf_type_t;
|
} gwbuf_type_t;
|
||||||
|
|
||||||
#define GWBUF_IS_TYPE_UNDEFINED(b) (b->gwbuf_type == 0)
|
#define GWBUF_IS_TYPE_UNDEFINED(b) (b->gwbuf_type == 0)
|
||||||
@ -122,6 +138,8 @@ typedef struct gwbuf {
|
|||||||
buffer_object_t *gwbuf_bufobj; /*< List of objects referred to by GWBUF */
|
buffer_object_t *gwbuf_bufobj; /*< List of objects referred to by GWBUF */
|
||||||
gwbuf_info_t gwbuf_info; /*< Info bits */
|
gwbuf_info_t gwbuf_info; /*< Info bits */
|
||||||
gwbuf_type_t gwbuf_type; /*< buffer's data type information */
|
gwbuf_type_t gwbuf_type; /*< buffer's data type information */
|
||||||
|
HINT *hint; /*< Hint data for this buffer */
|
||||||
|
BUF_PROPERTY *properties; /*< Buffer properties */
|
||||||
} GWBUF;
|
} GWBUF;
|
||||||
|
|
||||||
/*<
|
/*<
|
||||||
@ -155,6 +173,10 @@ extern unsigned int gwbuf_length(GWBUF *head);
|
|||||||
extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len);
|
extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len);
|
||||||
extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type);
|
extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type);
|
||||||
extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type);
|
extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type);
|
||||||
|
extern int gwbuf_add_property(GWBUF *buf, char *name, char *value);
|
||||||
|
extern char *gwbuf_get_property(GWBUF *buf, char *name);
|
||||||
|
extern GWBUF *gwbuf_make_contiguous(GWBUF *);
|
||||||
|
extern int gwbuf_add_hint(GWBUF *, HINT *);
|
||||||
|
|
||||||
void gwbuf_add_buffer_object(GWBUF* buf,
|
void gwbuf_add_buffer_object(GWBUF* buf,
|
||||||
bufobj_id_t id,
|
bufobj_id_t id,
|
||||||
|
@ -46,6 +46,8 @@ typedef enum {
|
|||||||
BOOL_TYPE = 0x08
|
BOOL_TYPE = 0x08
|
||||||
} config_param_type_t;
|
} config_param_type_t;
|
||||||
|
|
||||||
|
enum {MAX_RLAG_NOT_AVAILABLE=-1, MAX_RLAG_UNDEFINED=-2};
|
||||||
|
|
||||||
#define PARAM_IS_TYPE(p,t) ((p) & (t))
|
#define PARAM_IS_TYPE(p,t) ((p) & (t))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
69
server/include/hint.h
Normal file
69
server/include/hint.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef _HINT_H
|
||||||
|
#define _HINT_H
|
||||||
|
/*
|
||||||
|
* This file is distributed as part of MaxScale. 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 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file hint.h The generic hint data that may be attached to buffers
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 10/07/14 Mark Riddoch Initial implementation
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <skygw_debug.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The types of hint that are supported by the generic hinting mechanism.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
HINT_ROUTE_TO_MASTER = 1,
|
||||||
|
HINT_ROUTE_TO_SLAVE,
|
||||||
|
HINT_ROUTE_TO_NAMED_SERVER,
|
||||||
|
HINT_ROUTE_TO_UPTODATE_SERVER,
|
||||||
|
HINT_ROUTE_TO_ALL, /*< not implemented yet */
|
||||||
|
HINT_PARAMETER
|
||||||
|
} HINT_TYPE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic hint.
|
||||||
|
*
|
||||||
|
* A hint has a type associated with it and may optionally have hint
|
||||||
|
* specific data.
|
||||||
|
* Multiple hints may be attached to a single buffer.
|
||||||
|
*/
|
||||||
|
typedef struct hint {
|
||||||
|
HINT_TYPE type; /*< The Type of hint */
|
||||||
|
void *data; /*< Type specific data */
|
||||||
|
void *value; /*< Parameter value for hint */
|
||||||
|
unsigned int dsize; /*< Size of the hint data */
|
||||||
|
struct hint *next; /*< Another hint for this buffer */
|
||||||
|
} HINT;
|
||||||
|
|
||||||
|
extern HINT *hint_alloc(HINT_TYPE, void *, unsigned int);
|
||||||
|
extern HINT *hint_create_parameter(HINT *, char *, char *);
|
||||||
|
extern HINT *hint_create_route(HINT *, HINT_TYPE, char *);
|
||||||
|
extern void hint_free(HINT *);
|
||||||
|
extern HINT *hint_dup(HINT *);
|
||||||
|
bool hint_exists(HINT **, HINT_TYPE);
|
||||||
|
#endif
|
@ -38,6 +38,7 @@
|
|||||||
* 03/06/14 Mark Riddoch Addition of maintainance mode
|
* 03/06/14 Mark Riddoch Addition of maintainance mode
|
||||||
* 20/06/14 Massimiliano Pinto Addition of master_id, depth, slaves fields
|
* 20/06/14 Massimiliano Pinto Addition of master_id, depth, slaves fields
|
||||||
* 26/06/14 Mark Riddoch Adidtion of server parameters
|
* 26/06/14 Mark Riddoch Adidtion of server parameters
|
||||||
|
* 30/07/14 Massimiliano Pinto Addition of NDB status for MySQL Cluster
|
||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
@ -99,6 +100,7 @@ typedef struct server {
|
|||||||
#define SERVER_MASTER 0x0002 /**<< The server is a master, i.e. can handle writes */
|
#define SERVER_MASTER 0x0002 /**<< The server is a master, i.e. can handle writes */
|
||||||
#define SERVER_SLAVE 0x0004 /**<< The server is a slave, i.e. can handle reads */
|
#define SERVER_SLAVE 0x0004 /**<< The server is a slave, i.e. can handle reads */
|
||||||
#define SERVER_JOINED 0x0008 /**<< The server is joined in a Galera cluster */
|
#define SERVER_JOINED 0x0008 /**<< The server is joined in a Galera cluster */
|
||||||
|
#define SERVER_NDB 0x0010 /**<< The server is part of a MySQL cluster setup */
|
||||||
#define SERVER_MAINT 0x1000 /**<< Server is in maintenance mode */
|
#define SERVER_MAINT 0x1000 /**<< Server is in maintenance mode */
|
||||||
#define SERVER_SLAVE_OF_EXTERNAL_MASTER 0x0080 /**<< Server is slave of a Master outside the provided replication topology */
|
#define SERVER_SLAVE_OF_EXTERNAL_MASTER 0x0080 /**<< Server is slave of a Master outside the provided replication topology */
|
||||||
|
|
||||||
@ -131,15 +133,21 @@ typedef struct server {
|
|||||||
#define SERVER_IS_JOINED(server) \
|
#define SERVER_IS_JOINED(server) \
|
||||||
(((server)->status & (SERVER_RUNNING|SERVER_JOINED|SERVER_MAINT)) == (SERVER_RUNNING|SERVER_JOINED))
|
(((server)->status & (SERVER_RUNNING|SERVER_JOINED|SERVER_MAINT)) == (SERVER_RUNNING|SERVER_JOINED))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the server a SQL node in MySQL Cluster? The server must be running and with NDB status
|
||||||
|
*/
|
||||||
|
#define SERVER_IS_NDB(server) \
|
||||||
|
(((server)->status & (SERVER_RUNNING|SERVER_NDB|SERVER_MAINT)) == (SERVER_RUNNING|SERVER_NDB))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the server in maintenance mode.
|
* Is the server in maintenance mode.
|
||||||
*/
|
*/
|
||||||
#define SERVER_IN_MAINT(server) ((server)->status & SERVER_MAINT)
|
#define SERVER_IN_MAINT(server) ((server)->status & SERVER_MAINT)
|
||||||
|
|
||||||
/** server is not master, slave or joined */
|
/** server is not master, slave or joined */
|
||||||
#define SERVER_NOT_IN_CLUSTER(s) (((s)->status & (SERVER_MASTER|SERVER_SLAVE|SERVER_JOINED)) == 0)
|
#define SERVER_NOT_IN_CLUSTER(s) (((s)->status & (SERVER_MASTER|SERVER_SLAVE|SERVER_JOINED|SERVER_NDB)) == 0)
|
||||||
|
|
||||||
#define SERVER_IS_IN_CLUSTER(s) (((s)->status & (SERVER_MASTER|SERVER_SLAVE|SERVER_JOINED)) != 0)
|
#define SERVER_IS_IN_CLUSTER(s) (((s)->status & (SERVER_MASTER|SERVER_SLAVE|SERVER_JOINED|SERVER_NDB)) != 0)
|
||||||
|
|
||||||
#define SERVER_IS_RELAY_SERVER(server) \
|
#define SERVER_IS_RELAY_SERVER(server) \
|
||||||
(((server)->status & (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE|SERVER_MAINT)) == (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE))
|
(((server)->status & (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE|SERVER_MAINT)) == (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE))
|
||||||
|
@ -45,7 +45,7 @@ TEEOBJ=$(TEESRCS:.c=.o)
|
|||||||
SRCS=$(TESTSRCS) $(QLASRCS) $(REGEXSRCS) $(TOPNSRCS) $(TEESRCS)
|
SRCS=$(TESTSRCS) $(QLASRCS) $(REGEXSRCS) $(TOPNSRCS) $(TEESRCS)
|
||||||
OBJ=$(SRCS:.c=.o)
|
OBJ=$(SRCS:.c=.o)
|
||||||
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
||||||
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so libtopfilter.so libtee.so
|
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so libtopfilter.so libtee.so libhintfilter.so
|
||||||
|
|
||||||
|
|
||||||
all: $(MODULES)
|
all: $(MODULES)
|
||||||
@ -65,18 +65,25 @@ libtopfilter.so: $(TOPNOBJ)
|
|||||||
libtee.so: $(TEEOBJ)
|
libtee.so: $(TEEOBJ)
|
||||||
$(CC) $(LDFLAGS) $(TEEOBJ) $(LIBS) -o $@
|
$(CC) $(LDFLAGS) $(TEEOBJ) $(LIBS) -o $@
|
||||||
|
|
||||||
|
libhintfilter.so:
|
||||||
|
(cd hint; touch depend.mk ; make; cp $@ ..)
|
||||||
|
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
$(CC) $(CFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(DEL) $(OBJ) $(MODULES)
|
rm -f $(OBJ) $(MODULES)
|
||||||
|
(cd hint; touch depend.mk; make clean)
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
ctags $(SRCS) $(HDRS)
|
ctags $(SRCS) $(HDRS)
|
||||||
|
(cd hint; touch depend.mk; make tags)
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@$(DEL) depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
|
(cd hint; touch depend.mk; make depend)
|
||||||
|
|
||||||
install: $(MODULES)
|
install: $(MODULES)
|
||||||
install -D $(MODULES) $(DEST)/modules
|
install -D $(MODULES) $(DEST)/modules
|
||||||
|
70
server/modules/filter/hint/Makefile
Normal file
70
server/modules/filter/hint/Makefile
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# This file is distributed as part of MaxScale form SkySQL. 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 2014
|
||||||
|
#
|
||||||
|
# Revision History
|
||||||
|
# Date Who Description
|
||||||
|
# 21/07/14 Mark Riddoch Initial module development
|
||||||
|
|
||||||
|
include ../../../../build_gateway.inc
|
||||||
|
|
||||||
|
LOGPATH := $(ROOT_PATH)/log_manager
|
||||||
|
UTILSPATH := $(ROOT_PATH)/utils
|
||||||
|
|
||||||
|
CC=cc
|
||||||
|
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include -I$(LOGPATH) \
|
||||||
|
-I$(UTILSPATH) -Wall -g
|
||||||
|
|
||||||
|
include ../../../../makefile.inc
|
||||||
|
|
||||||
|
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
||||||
|
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
|
||||||
|
|
||||||
|
SRCS= hintfilter.c hintparser.c
|
||||||
|
OBJ=$(SRCS:.c=.o)
|
||||||
|
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
||||||
|
|
||||||
|
libhintfilter.so: $(OBJ)
|
||||||
|
$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJ) libhintfilter.so
|
||||||
|
|
||||||
|
tags:
|
||||||
|
ctags $(SRCS) $(HDRS)
|
||||||
|
|
||||||
|
depend:
|
||||||
|
@rm -f depend.mk
|
||||||
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
|
|
||||||
|
install: $(MODULES)
|
||||||
|
install -D $(MODULES) $(DEST)/modules
|
||||||
|
|
||||||
|
cleantests:
|
||||||
|
$(MAKE) -C test cleantests
|
||||||
|
|
||||||
|
buildtests:
|
||||||
|
$(MAKE) -C test DEBUG=Y buildtests
|
||||||
|
|
||||||
|
runtests:
|
||||||
|
$(MAKE) -C test runtests
|
||||||
|
|
||||||
|
testall:
|
||||||
|
$(MAKE) -C test testall
|
||||||
|
|
||||||
|
include depend.mk
|
272
server/modules/filter/hint/hintfilter.c
Normal file
272
server/modules/filter/hint/hintfilter.c
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* This file is distributed as part of MaxScale by SkySQL. 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 2014
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <filter.h>
|
||||||
|
#include <modinfo.h>
|
||||||
|
#include <modutil.h>
|
||||||
|
#include <mysqlhint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hintfilter.c - a filter to parse the MaxScale hint syntax and attach those
|
||||||
|
* hints to the buffers that carry the requests.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
MODULE_INFO info = {
|
||||||
|
MODULE_API_FILTER,
|
||||||
|
MODULE_ALPHA_RELEASE,
|
||||||
|
FILTER_VERSION,
|
||||||
|
"A hint parsing filter"
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *version_str = "V1.0.0";
|
||||||
|
|
||||||
|
static FILTER *createInstance(char **options, FILTER_PARAMETER **params);
|
||||||
|
static void *newSession(FILTER *instance, SESSION *session);
|
||||||
|
static void closeSession(FILTER *instance, void *session);
|
||||||
|
static void freeSession(FILTER *instance, void *session);
|
||||||
|
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
|
||||||
|
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
|
||||||
|
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
|
||||||
|
|
||||||
|
|
||||||
|
static FILTER_OBJECT MyObject = {
|
||||||
|
createInstance,
|
||||||
|
newSession,
|
||||||
|
closeSession,
|
||||||
|
freeSession,
|
||||||
|
setDownstream,
|
||||||
|
NULL, // No upstream requirement
|
||||||
|
routeQuery,
|
||||||
|
NULL,
|
||||||
|
diagnostic,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the mandatory version entry point
|
||||||
|
*
|
||||||
|
* @return version string of the module
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
version()
|
||||||
|
{
|
||||||
|
return version_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module initialisation routine, called when the module
|
||||||
|
* is first loaded.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ModuleInit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module entry point routine. It is this routine that
|
||||||
|
* must populate the structure that is referred to as the
|
||||||
|
* "module object", this is a structure with the set of
|
||||||
|
* external entry points for this module.
|
||||||
|
*
|
||||||
|
* @return The module object
|
||||||
|
*/
|
||||||
|
FILTER_OBJECT *
|
||||||
|
GetModuleObject()
|
||||||
|
{
|
||||||
|
return &MyObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the filter for a particular service
|
||||||
|
* within MaxScale.
|
||||||
|
*
|
||||||
|
* @param options The options for this filter
|
||||||
|
*
|
||||||
|
* @return The instance data for this new instance
|
||||||
|
*/
|
||||||
|
static FILTER *
|
||||||
|
createInstance(char **options, FILTER_PARAMETER **params)
|
||||||
|
{
|
||||||
|
HINT_INSTANCE *my_instance;
|
||||||
|
|
||||||
|
if ((my_instance = calloc(1, sizeof(HINT_INSTANCE))) != NULL)
|
||||||
|
my_instance->sessions = 0;
|
||||||
|
return (FILTER *)my_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a new session with this instance of the filter.
|
||||||
|
*
|
||||||
|
* @param instance The filter instance data
|
||||||
|
* @param session The session itself
|
||||||
|
* @return Session specific data for this session
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
newSession(FILTER *instance, SESSION *session)
|
||||||
|
{
|
||||||
|
HINT_INSTANCE *my_instance = (HINT_INSTANCE *)instance;
|
||||||
|
HINT_SESSION *my_session;
|
||||||
|
|
||||||
|
if ((my_session = calloc(1, sizeof(HINT_SESSION))) != NULL)
|
||||||
|
{
|
||||||
|
my_session->query_len = 0;
|
||||||
|
my_session->request = NULL;
|
||||||
|
my_session->stack = NULL;
|
||||||
|
my_session->named_hints = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return my_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a session with the filter, this is the mechanism
|
||||||
|
* by which a filter may cleanup data structure etc.
|
||||||
|
*
|
||||||
|
* @param instance The filter instance data
|
||||||
|
* @param session The session being closed
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
closeSession(FILTER *instance, void *session)
|
||||||
|
{
|
||||||
|
HINT_SESSION *my_session = (HINT_SESSION *)session;
|
||||||
|
NAMEDHINTS* named_hints;
|
||||||
|
HINTSTACK* hint_stack;
|
||||||
|
|
||||||
|
if (my_session->request)
|
||||||
|
gwbuf_free(my_session->request);
|
||||||
|
|
||||||
|
|
||||||
|
/** Free named hints */
|
||||||
|
named_hints = my_session->named_hints;
|
||||||
|
|
||||||
|
while ((named_hints = free_named_hint(named_hints)) != NULL)
|
||||||
|
;
|
||||||
|
/** Free stacked hints */
|
||||||
|
hint_stack = my_session->stack;
|
||||||
|
|
||||||
|
while ((hint_stack = free_hint_stack(hint_stack)) != NULL)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the memory associated with this filter session.
|
||||||
|
*
|
||||||
|
* @param instance The filter instance data
|
||||||
|
* @param session The session being closed
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
freeSession(FILTER *instance, void *session)
|
||||||
|
{
|
||||||
|
free(session);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the downstream component for this filter.
|
||||||
|
*
|
||||||
|
* @param instance The filter instance data
|
||||||
|
* @param session The session being closed
|
||||||
|
* @param downstream The downstream filter or router
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
|
||||||
|
{
|
||||||
|
HINT_SESSION *my_session = (HINT_SESSION *)session;
|
||||||
|
|
||||||
|
my_session->down = *downstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The routeQuery entry point. This is passed the query buffer
|
||||||
|
* to which the filter should be applied. Once applied the
|
||||||
|
* query shoudl normally be passed to the downstream component
|
||||||
|
* (filter or router) in the filter chain.
|
||||||
|
*
|
||||||
|
* @param instance The filter instance data
|
||||||
|
* @param session The filter session
|
||||||
|
* @param queue The query data
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||||
|
{
|
||||||
|
HINT_SESSION *my_session = (HINT_SESSION *)session;
|
||||||
|
char *ptr;
|
||||||
|
int rval, len, residual;
|
||||||
|
HINT *hint;
|
||||||
|
|
||||||
|
if (my_session->request == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No stored buffer, so this must be the first
|
||||||
|
* buffer of a new request.
|
||||||
|
*/
|
||||||
|
if (modutil_MySQL_Query(queue, &ptr, &len, &residual) == 0)
|
||||||
|
{
|
||||||
|
return my_session->down.routeQuery(
|
||||||
|
my_session->down.instance,
|
||||||
|
my_session->down.session, queue);
|
||||||
|
}
|
||||||
|
my_session->request = queue;
|
||||||
|
my_session->query_len = len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gwbuf_append(my_session->request, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gwbuf_length(my_session->request) < my_session->query_len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We have not got the entire SQL text, buffer and wait for
|
||||||
|
* the remainder.
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* We have the entire SQL text, parse for hints and attach to the
|
||||||
|
* buffer at the head of the queue.
|
||||||
|
*/
|
||||||
|
queue = my_session->request;
|
||||||
|
my_session->request = NULL;
|
||||||
|
my_session->query_len = 0;
|
||||||
|
hint = hint_parser(my_session, queue);
|
||||||
|
queue->hint = hint;
|
||||||
|
|
||||||
|
/* Now process the request */
|
||||||
|
rval = my_session->down.routeQuery(my_session->down.instance,
|
||||||
|
my_session->down.session, queue);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diagnostics routine
|
||||||
|
*
|
||||||
|
* If fsession is NULL then print diagnostics on the filter
|
||||||
|
* instance as a whole, otherwise print diagnostics for the
|
||||||
|
* particular session.
|
||||||
|
*
|
||||||
|
* @param instance The filter instance
|
||||||
|
* @param fsession Filter session, may be NULL
|
||||||
|
* @param dcb The DCB for diagnostic output
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
|
||||||
|
{
|
||||||
|
HINT_INSTANCE *my_instance = (HINT_INSTANCE *)instance;
|
||||||
|
HINT_SESSION *my_session = (HINT_SESSION *)fsession;
|
||||||
|
|
||||||
|
}
|
761
server/modules/filter/hint/hintparser.c
Normal file
761
server/modules/filter/hint/hintparser.c
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
/*
|
||||||
|
* This file is distributed as part of MaxScale by SkySQL. 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 2014
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <skygw_utils.h>
|
||||||
|
#include <log_manager.h>
|
||||||
|
#include <filter.h>
|
||||||
|
#include <modinfo.h>
|
||||||
|
#include <modutil.h>
|
||||||
|
#include <mysqlhint.h>
|
||||||
|
|
||||||
|
extern int lm_enabled_logfiles_bitmask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hintparser.c - Find any comment in the SQL packet and look for MAXSCALE
|
||||||
|
* hints in that comment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The keywords in the hint syntax
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
char *keyword;
|
||||||
|
TOKEN_VALUE token;
|
||||||
|
} keywords[] = {
|
||||||
|
{ "maxscale", TOK_MAXSCALE },
|
||||||
|
{ "prepare", TOK_PREPARE },
|
||||||
|
{ "start", TOK_START },
|
||||||
|
{ "begin", TOK_START },
|
||||||
|
{ "stop", TOK_STOP },
|
||||||
|
{ "end", TOK_STOP },
|
||||||
|
{ "=", TOK_EQUAL },
|
||||||
|
{ "route", TOK_ROUTE },
|
||||||
|
{ "to", TOK_TO },
|
||||||
|
{ "master", TOK_MASTER },
|
||||||
|
{ "slave", TOK_SLAVE },
|
||||||
|
{ "server", TOK_SERVER },
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
HINT_TOKEN kwords[] = {
|
||||||
|
{ TOK_MAXSCALE, "maxscale" },
|
||||||
|
{ TOK_PREPARE, "prepare" },
|
||||||
|
{ TOK_START, "start" },
|
||||||
|
{ TOK_START, "begin" },
|
||||||
|
{ TOK_STOP, "stop" },
|
||||||
|
{ TOK_STOP, "end" },
|
||||||
|
{ TOK_EQUAL, "=" },
|
||||||
|
{ TOK_ROUTE, "route" },
|
||||||
|
{ TOK_TO, "to" },
|
||||||
|
{ TOK_MASTER, "master" },
|
||||||
|
{ TOK_SLAVE, "slave" },
|
||||||
|
{ TOK_SERVER, "server" },
|
||||||
|
{ 0, NULL}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
static HINT_TOKEN *hint_next_token(GWBUF **buf, char **ptr);
|
||||||
|
static void hint_pop(HINT_SESSION *);
|
||||||
|
static HINT *lookup_named_hint(HINT_SESSION *, char *);
|
||||||
|
static void create_named_hint(HINT_SESSION *, char *, HINT *);
|
||||||
|
static void hint_push(HINT_SESSION *, HINT *);
|
||||||
|
static const char* token_get_keyword (HINT_TOKEN* token);
|
||||||
|
static void token_free(HINT_TOKEN* token);
|
||||||
|
|
||||||
|
typedef enum { HM_EXECUTE, HM_START, HM_PREPARE } HINT_MODE;
|
||||||
|
|
||||||
|
void token_free(HINT_TOKEN* token)
|
||||||
|
{
|
||||||
|
if (token->value != NULL)
|
||||||
|
{
|
||||||
|
free(token->value);
|
||||||
|
}
|
||||||
|
free(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char* token_get_keyword (
|
||||||
|
HINT_TOKEN* token)
|
||||||
|
{
|
||||||
|
switch (token->token) {
|
||||||
|
case TOK_EOL:
|
||||||
|
return "End of line";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOK_STRING:
|
||||||
|
return token->value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
while (i < TOK_EOL && keywords[i].token != token->token)
|
||||||
|
i++;
|
||||||
|
|
||||||
|
ss_dassert(i != TOK_EOL);
|
||||||
|
|
||||||
|
if (i == TOK_EOL)
|
||||||
|
{
|
||||||
|
return "Unknown token";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return keywords[i].keyword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parse the hint comments in the MySQL statement passed in request.
|
||||||
|
* Add any hints to the buffer for later processing.
|
||||||
|
*
|
||||||
|
* @param session The filter session
|
||||||
|
* @param request The MySQL request buffer
|
||||||
|
* @return The hints parsed in this statement or active on the
|
||||||
|
* stack
|
||||||
|
*/
|
||||||
|
HINT *
|
||||||
|
hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||||
|
{
|
||||||
|
char *ptr, lastch = ' ';
|
||||||
|
int len, residual, state;
|
||||||
|
int found, escape, quoted, squoted;
|
||||||
|
HINT *rval = NULL;
|
||||||
|
char *pname, *lvalue, *hintname = NULL;
|
||||||
|
GWBUF *buf;
|
||||||
|
HINT_TOKEN *tok;
|
||||||
|
HINT_MODE mode = HM_EXECUTE;
|
||||||
|
|
||||||
|
/* First look for any comment in the SQL */
|
||||||
|
modutil_MySQL_Query(request, &ptr, &len, &residual);
|
||||||
|
buf = request;
|
||||||
|
found = 0;
|
||||||
|
escape = 0;
|
||||||
|
quoted = 0;
|
||||||
|
squoted = 0;
|
||||||
|
do {
|
||||||
|
while (len--)
|
||||||
|
{
|
||||||
|
if (*ptr == '\\')
|
||||||
|
escape = 1;
|
||||||
|
else if (*ptr == '\"' && quoted)
|
||||||
|
quoted = 0;
|
||||||
|
else if (*ptr == '\"' && quoted == 0)
|
||||||
|
quoted = 0;
|
||||||
|
else if (*ptr == '\'' && squoted)
|
||||||
|
squoted = 0;
|
||||||
|
else if (*ptr == '\"' && squoted == 0)
|
||||||
|
squoted = 0;
|
||||||
|
else if (quoted || squoted)
|
||||||
|
;
|
||||||
|
else if (escape)
|
||||||
|
escape = 0;
|
||||||
|
else if (*ptr == '#')
|
||||||
|
{
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (*ptr == '/')
|
||||||
|
lastch = '/';
|
||||||
|
else if (*ptr == '*' && lastch == '/')
|
||||||
|
{
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (*ptr == '-' && lastch == '-')
|
||||||
|
{
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (*ptr == '-')
|
||||||
|
lastch = '-';
|
||||||
|
else
|
||||||
|
lastch = *ptr;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
break;
|
||||||
|
|
||||||
|
buf = buf->next;
|
||||||
|
if (buf)
|
||||||
|
{
|
||||||
|
len = GWBUF_LENGTH(buf);
|
||||||
|
ptr = GWBUF_DATA(buf);
|
||||||
|
}
|
||||||
|
} while (buf);
|
||||||
|
|
||||||
|
if (!found) /* No comment so we need do no more */
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have got here then we have a comment, ptr point to
|
||||||
|
* the comment character if it is a '#' comment or the second
|
||||||
|
* character of the comment if it is a -- or /* comment
|
||||||
|
*
|
||||||
|
* Move to the next character in the SQL.
|
||||||
|
*/
|
||||||
|
ptr++;
|
||||||
|
if (ptr > (char *)(buf->end))
|
||||||
|
{
|
||||||
|
buf = buf->next;
|
||||||
|
if (buf)
|
||||||
|
ptr = GWBUF_DATA(buf);
|
||||||
|
else
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
tok = hint_next_token(&buf, &ptr);
|
||||||
|
|
||||||
|
if (tok == NULL)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok->token != TOK_MAXSCALE)
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Invalid hint string '%s'. Hint should start "
|
||||||
|
"with keyword 'maxscale'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Invalid hint string '%s'. Hint should start "
|
||||||
|
"with keyword 'maxscale'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
token_free(tok);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
token_free(tok);
|
||||||
|
|
||||||
|
state = HS_INIT;
|
||||||
|
|
||||||
|
while ((tok = hint_next_token(&buf, &ptr)) != NULL
|
||||||
|
&& tok->token != TOK_EOL)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case HS_INIT:
|
||||||
|
switch (tok->token)
|
||||||
|
{
|
||||||
|
case TOK_ROUTE:
|
||||||
|
state = HS_ROUTE;
|
||||||
|
break;
|
||||||
|
case TOK_STRING:
|
||||||
|
state = HS_NAME;
|
||||||
|
lvalue = strdup(tok->value);
|
||||||
|
break;
|
||||||
|
case TOK_STOP:
|
||||||
|
/* Action: pop active hint */
|
||||||
|
hint_pop(session);
|
||||||
|
state = HS_INIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Error: expected hint, name or STOP */
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'route', 'stop' or hint name instead of "
|
||||||
|
"'%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'route', 'stop' or hint name instead of "
|
||||||
|
"'%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
token_free(tok);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HS_ROUTE:
|
||||||
|
if (tok->token != TOK_TO)
|
||||||
|
{
|
||||||
|
/* Error, expect TO */;
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'to' instead of '%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'to' instead of '%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
token_free(tok);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
state = HS_ROUTE1;
|
||||||
|
break;
|
||||||
|
case HS_ROUTE1:
|
||||||
|
switch (tok->token)
|
||||||
|
{
|
||||||
|
case TOK_MASTER:
|
||||||
|
rval = hint_create_route(rval,
|
||||||
|
HINT_ROUTE_TO_MASTER, NULL);
|
||||||
|
break;
|
||||||
|
case TOK_SLAVE:
|
||||||
|
rval = hint_create_route(rval,
|
||||||
|
HINT_ROUTE_TO_SLAVE, NULL);
|
||||||
|
break;
|
||||||
|
case TOK_SERVER:
|
||||||
|
state = HS_ROUTE_SERVER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Error expected MASTER, SLAVE or SERVER */
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'master', 'slave', or 'server' instead "
|
||||||
|
"of '%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'master', 'slave', or 'server' instead "
|
||||||
|
"of '%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
|
||||||
|
token_free(tok);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HS_ROUTE_SERVER:
|
||||||
|
if (tok->token == TOK_STRING)
|
||||||
|
{
|
||||||
|
rval = hint_create_route(rval,
|
||||||
|
HINT_ROUTE_TO_NAMED_SERVER, tok->value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Error: Expected server name */
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"server name instead of '%s'. Hint "
|
||||||
|
"ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"server name instead of '%s'. Hint "
|
||||||
|
"ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
token_free(tok);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HS_NAME:
|
||||||
|
switch (tok->token)
|
||||||
|
{
|
||||||
|
case TOK_EQUAL:
|
||||||
|
pname = lvalue;
|
||||||
|
state = HS_PVALUE;
|
||||||
|
break;
|
||||||
|
case TOK_PREPARE:
|
||||||
|
pname = lvalue;
|
||||||
|
state = HS_PREPARE;
|
||||||
|
break;
|
||||||
|
case TOK_START:
|
||||||
|
/* Action start(lvalue) */
|
||||||
|
hintname = lvalue;
|
||||||
|
mode = HM_START;
|
||||||
|
state = HS_INIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Error, token tok->value not expected */
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'=', 'prepare', or 'start' instead of "
|
||||||
|
"'%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'=', 'prepare', or 'start' instead of "
|
||||||
|
"'%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
token_free(tok);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HS_PVALUE:
|
||||||
|
/* Action: pname = tok->value */
|
||||||
|
rval = hint_create_parameter(rval, pname, tok->value);
|
||||||
|
state = HS_INIT;
|
||||||
|
break;
|
||||||
|
case HS_PREPARE:
|
||||||
|
mode = HM_PREPARE;
|
||||||
|
hintname = lvalue;
|
||||||
|
switch (tok->token)
|
||||||
|
{
|
||||||
|
case TOK_ROUTE:
|
||||||
|
state = HS_ROUTE;
|
||||||
|
break;
|
||||||
|
case TOK_STRING:
|
||||||
|
state = HS_NAME;
|
||||||
|
lvalue = tok->value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Error, token tok->value not expected */
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'route' or hint name instead of "
|
||||||
|
"'%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Syntax error in hint. Expected "
|
||||||
|
"'route' or hint name instead of "
|
||||||
|
"'%s'. Hint ignored.",
|
||||||
|
token_get_keyword(tok))));
|
||||||
|
token_free(tok);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
token_free(tok);
|
||||||
|
} /*< while */
|
||||||
|
|
||||||
|
if (tok->token == TOK_EOL)
|
||||||
|
{
|
||||||
|
token_free(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case HM_START:
|
||||||
|
/*
|
||||||
|
* We are starting either a predefined set of hints,
|
||||||
|
* creating a new set of hints and starting in a single
|
||||||
|
* operation or starting an annonymous block of hints.
|
||||||
|
*/
|
||||||
|
if (hintname == NULL && rval != NULL)
|
||||||
|
{
|
||||||
|
/* We are starting an anonymous block of hints */
|
||||||
|
hint_push(session, rval);
|
||||||
|
rval = NULL;
|
||||||
|
} else if (hintname && rval)
|
||||||
|
{
|
||||||
|
/* We are creating and starting a block of hints */
|
||||||
|
if (lookup_named_hint(session, hintname) != NULL)
|
||||||
|
{
|
||||||
|
/* Error hint with this name already exists */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
create_named_hint(session, hintname, rval);
|
||||||
|
hint_push(session, hint_dup(rval));
|
||||||
|
}
|
||||||
|
} else if (hintname && rval == NULL)
|
||||||
|
{
|
||||||
|
/* We starting an already define set of named hints */
|
||||||
|
rval = lookup_named_hint(session, hintname);
|
||||||
|
hint_push(session, hint_dup(rval));
|
||||||
|
free(hintname);
|
||||||
|
rval = NULL;
|
||||||
|
} else if (hintname == NULL && rval == NULL)
|
||||||
|
{
|
||||||
|
/* Error case */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HM_PREPARE:
|
||||||
|
/*
|
||||||
|
* We are preparing a named set of hints. Note this does
|
||||||
|
* not trigger the usage of these hints currently.
|
||||||
|
*/
|
||||||
|
if (hintname == NULL || rval == NULL)
|
||||||
|
{
|
||||||
|
/* Error case, name and hints must be defined */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
create_named_hint(session, hintname, rval);
|
||||||
|
}
|
||||||
|
/* We are not starting the hints now, so return an empty
|
||||||
|
* hint set.
|
||||||
|
*/
|
||||||
|
rval = NULL;
|
||||||
|
break;
|
||||||
|
case HM_EXECUTE:
|
||||||
|
/*
|
||||||
|
* We have a one-off hint for the statement we are
|
||||||
|
* currently forwarding.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
retblock:
|
||||||
|
if (rval == NULL)
|
||||||
|
{
|
||||||
|
/* No new hint parsed in this statement, apply the current
|
||||||
|
* top of stack if there is one.
|
||||||
|
*/
|
||||||
|
if (session->stack)
|
||||||
|
rval = hint_dup(session->stack->hint);
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the next token in the inout stream
|
||||||
|
*
|
||||||
|
* @param buf A pointer to the buffer point, will be updated if a
|
||||||
|
* new buffer is used.
|
||||||
|
* @param ptr The pointer within the buffer we are processing
|
||||||
|
* @return A HINT token
|
||||||
|
*/
|
||||||
|
static HINT_TOKEN *
|
||||||
|
hint_next_token(GWBUF **buf, char **ptr)
|
||||||
|
{
|
||||||
|
char word[100], *dest;
|
||||||
|
int inword = 0;
|
||||||
|
char inquote = '\0';
|
||||||
|
int i, found;
|
||||||
|
HINT_TOKEN *tok;
|
||||||
|
|
||||||
|
if ((tok = (HINT_TOKEN *)malloc(sizeof(HINT_TOKEN))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
tok->value = NULL;
|
||||||
|
dest = word;
|
||||||
|
while (*ptr < (char *)((*buf)->end) || (*buf)->next)
|
||||||
|
{
|
||||||
|
/** word ends, don't move ptr but return with read word */
|
||||||
|
if (inword && inquote == '\0' &&
|
||||||
|
(isspace(**ptr) || **ptr == '='))
|
||||||
|
{
|
||||||
|
inword = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/** found '=', move ptr and return with '=' */
|
||||||
|
else if (!inword && inquote == '\0' && **ptr == '=')
|
||||||
|
{
|
||||||
|
*dest = **ptr;
|
||||||
|
*dest++;
|
||||||
|
(*ptr)++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (**ptr == '\'' && inquote == '\'')
|
||||||
|
inquote = '\0';
|
||||||
|
else if (**ptr == '\'')
|
||||||
|
inquote = **ptr;
|
||||||
|
/** Any other character which belongs to the word, move ahead */
|
||||||
|
else if (inword || (isspace(**ptr) == 0))
|
||||||
|
{
|
||||||
|
*dest++ = **ptr;
|
||||||
|
inword = 1;
|
||||||
|
}
|
||||||
|
(*ptr)++;
|
||||||
|
|
||||||
|
if (*ptr > (char *)((*buf)->end) && (*buf)->next)
|
||||||
|
{
|
||||||
|
*buf = (*buf)->next;
|
||||||
|
*ptr = (*buf)->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest - word > 98)
|
||||||
|
break;
|
||||||
|
} /*< while */
|
||||||
|
*dest = 0;
|
||||||
|
|
||||||
|
/* We now have a word in the local word, check to see if it is a
|
||||||
|
* token we recognise.
|
||||||
|
*/
|
||||||
|
if (word[0] == '\0')
|
||||||
|
{
|
||||||
|
tok->token = TOK_EOL;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
found = 0;
|
||||||
|
for (i = 0; keywords[i].keyword; i++)
|
||||||
|
{
|
||||||
|
if (strcasecmp(word, keywords[i].keyword) == 0)
|
||||||
|
{
|
||||||
|
tok->token = keywords[i].token;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found == 0)
|
||||||
|
{
|
||||||
|
tok->token = TOK_STRING;
|
||||||
|
tok->value = strdup(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hint_pop - pop the hint off the top of the stack if it is not empty
|
||||||
|
*
|
||||||
|
* @param session The filter session.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hint_pop(HINT_SESSION *session)
|
||||||
|
{
|
||||||
|
HINTSTACK *ptr;
|
||||||
|
HINT *hint;
|
||||||
|
|
||||||
|
if ((ptr = session->stack) != NULL)
|
||||||
|
{
|
||||||
|
session->stack = ptr->next;
|
||||||
|
while (ptr->hint)
|
||||||
|
{
|
||||||
|
hint = ptr->hint;
|
||||||
|
ptr->hint = hint->next;
|
||||||
|
hint_free(hint);
|
||||||
|
}
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push a hint onto the stack of actie hints
|
||||||
|
*
|
||||||
|
* @param session The filter session
|
||||||
|
* @param hint The hint to push, the hint ownership is retained
|
||||||
|
* by the stack and should not be freed by the caller
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hint_push(HINT_SESSION *session, HINT *hint)
|
||||||
|
{
|
||||||
|
HINTSTACK *item;
|
||||||
|
|
||||||
|
if ((item = (HINTSTACK *)malloc(sizeof(HINTSTACK))) == NULL)
|
||||||
|
return;
|
||||||
|
item->hint = hint;
|
||||||
|
item->next = session->stack;
|
||||||
|
session->stack = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for a hint block that already exists with this name
|
||||||
|
*
|
||||||
|
* @param session The filter session
|
||||||
|
* @param name The name to lookup
|
||||||
|
* @return the HINT or NULL if the name was not found.
|
||||||
|
*/
|
||||||
|
static HINT *
|
||||||
|
lookup_named_hint(HINT_SESSION *session, char *name)
|
||||||
|
{
|
||||||
|
NAMEDHINTS *ptr = session->named_hints;
|
||||||
|
|
||||||
|
while (ptr)
|
||||||
|
{
|
||||||
|
if (strcmp(ptr->name, name) == 0)
|
||||||
|
return ptr->hints;
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a named hint block
|
||||||
|
*
|
||||||
|
* @param session The filter session
|
||||||
|
* @param name The name of the block to ceate
|
||||||
|
* @param hint The hints themselves
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
create_named_hint(HINT_SESSION *session, char *name, HINT *hint)
|
||||||
|
{
|
||||||
|
NAMEDHINTS *block;
|
||||||
|
|
||||||
|
if ((block = (NAMEDHINTS *)malloc(sizeof(NAMEDHINTS))) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
block->name = name;
|
||||||
|
block->hints = hint_dup(hint);
|
||||||
|
block->next = session->named_hints;
|
||||||
|
session->named_hints = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the given NAMEDHINTS struct and all included hints.
|
||||||
|
*
|
||||||
|
* @param named_hint NAMEDHINTS struct to be released
|
||||||
|
*
|
||||||
|
* @return pointer to next NAMEDHINTS struct.
|
||||||
|
*/
|
||||||
|
NAMEDHINTS* free_named_hint(
|
||||||
|
NAMEDHINTS* named_hint)
|
||||||
|
{
|
||||||
|
NAMEDHINTS* next;
|
||||||
|
|
||||||
|
if (named_hint != NULL)
|
||||||
|
{
|
||||||
|
HINT* hint;
|
||||||
|
|
||||||
|
next = named_hint->next;
|
||||||
|
|
||||||
|
while (named_hint->hints != NULL)
|
||||||
|
{
|
||||||
|
hint = named_hint->hints->next;
|
||||||
|
hint_free(named_hint->hints);
|
||||||
|
named_hint->hints = hint;
|
||||||
|
}
|
||||||
|
free(named_hint->name);
|
||||||
|
free(named_hint);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the given HINTSTACK struct and all included hints.
|
||||||
|
*
|
||||||
|
* @param hint_stack HINTSTACK struct to be released
|
||||||
|
*
|
||||||
|
* @return pointer to next HINTSTACK struct.
|
||||||
|
*/
|
||||||
|
HINTSTACK* free_hint_stack(
|
||||||
|
HINTSTACK* hint_stack)
|
||||||
|
{
|
||||||
|
HINTSTACK* next;
|
||||||
|
|
||||||
|
if (hint_stack != NULL)
|
||||||
|
{
|
||||||
|
HINT* hint;
|
||||||
|
|
||||||
|
next = hint_stack->next;
|
||||||
|
|
||||||
|
while (hint_stack->hint != NULL)
|
||||||
|
{
|
||||||
|
hint = hint_stack->hint->next;
|
||||||
|
hint_free(hint_stack->hint);
|
||||||
|
hint_stack->hint = hint;
|
||||||
|
}
|
||||||
|
free(hint_stack);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
114
server/modules/include/mysqlhint.h
Normal file
114
server/modules/include/mysqlhint.h
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#ifndef _MYSQLHINT_H
|
||||||
|
#define _MYSQLHINT_H
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 17-07-2014 Mark Riddoch Initial implementation
|
||||||
|
*/
|
||||||
|
#include <hint.h>
|
||||||
|
|
||||||
|
/* Parser tokens for the hint parser */
|
||||||
|
typedef enum {
|
||||||
|
TOK_MAXSCALE = 1,
|
||||||
|
TOK_PREPARE,
|
||||||
|
TOK_START,
|
||||||
|
TOK_STOP,
|
||||||
|
TOK_EQUAL,
|
||||||
|
TOK_STRING,
|
||||||
|
TOK_ROUTE,
|
||||||
|
TOK_TO,
|
||||||
|
TOK_MASTER,
|
||||||
|
TOK_SLAVE,
|
||||||
|
TOK_SERVER,
|
||||||
|
TOK_EOL
|
||||||
|
} TOKEN_VALUE;
|
||||||
|
|
||||||
|
/* The tokenising return type */
|
||||||
|
typedef struct {
|
||||||
|
TOKEN_VALUE token; // The token itself
|
||||||
|
char *value; // The string version of the token
|
||||||
|
} HINT_TOKEN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A named hint set.
|
||||||
|
*
|
||||||
|
* The hint "MaxScale name PREPARE ..." can be used to defined a named set
|
||||||
|
* of hints that can be later applied.
|
||||||
|
*/
|
||||||
|
typedef struct namedhints {
|
||||||
|
char *name; /*< Hintsets name */
|
||||||
|
HINT *hints;
|
||||||
|
struct namedhints
|
||||||
|
*next; /*< Next named hint */
|
||||||
|
} NAMEDHINTS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A session meaintains a stack of hints, the hints BEGIN and STOP are used
|
||||||
|
* push hints on and off the stack. The current top of the stack is added to
|
||||||
|
* any statement that does not explicitly define a hint for that signle
|
||||||
|
* statement.
|
||||||
|
*/
|
||||||
|
typedef struct hintstack {
|
||||||
|
HINT *hint;
|
||||||
|
struct hintstack
|
||||||
|
*next;
|
||||||
|
} HINTSTACK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hint instance structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int sessions;
|
||||||
|
} HINT_INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hint parser session structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
DOWNSTREAM down;
|
||||||
|
GWBUF *request;
|
||||||
|
int query_len;
|
||||||
|
HINTSTACK *stack;
|
||||||
|
NAMEDHINTS *named_hints; /* The named hints defined in this session */
|
||||||
|
} HINT_SESSION;
|
||||||
|
|
||||||
|
/* Some useful macros */
|
||||||
|
#define CURRENT_HINT(session) ((session)->stack ? \
|
||||||
|
(session)->stack->hints : NULL)
|
||||||
|
|
||||||
|
/* Hint Parser State Machine */
|
||||||
|
#define HS_INIT 0
|
||||||
|
#define HS_ROUTE 1
|
||||||
|
#define HS_ROUTE1 2
|
||||||
|
#define HS_ROUTE_SERVER 3
|
||||||
|
#define HS_NAME 4
|
||||||
|
#define HS_PVALUE 5
|
||||||
|
#define HS_PREPARE 6
|
||||||
|
|
||||||
|
|
||||||
|
extern HINT *hint_parser(HINT_SESSION *session, GWBUF *request);
|
||||||
|
NAMEDHINTS* free_named_hint(NAMEDHINTS* named_hint);
|
||||||
|
HINTSTACK* free_hint_stack(HINTSTACK* hint_stack);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -71,6 +71,20 @@ typedef enum backend_type_t {
|
|||||||
BE_COUNT
|
BE_COUNT
|
||||||
} backend_type_t;
|
} backend_type_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TARGET_MASTER = 0x01,
|
||||||
|
TARGET_SLAVE = 0x02,
|
||||||
|
TARGET_NAMED_SERVER = 0x04,
|
||||||
|
TARGET_ALL = 0x08,
|
||||||
|
TARGET_RLAG_MAX = 0x10
|
||||||
|
} route_target_t;
|
||||||
|
|
||||||
|
#define TARGET_IS_MASTER(t) (t & TARGET_MASTER)
|
||||||
|
#define TARGET_IS_SLAVE(t) (t & TARGET_SLAVE)
|
||||||
|
#define TARGET_IS_NAMED_SERVER(t) (t & TARGET_NAMED_SERVER)
|
||||||
|
#define TARGET_IS_ALL(t) (t & TARGET_ALL)
|
||||||
|
#define TARGET_IS_RLAG_MAX(t) (t & TARGET_RLAG_MAX)
|
||||||
|
|
||||||
typedef struct rses_property_st rses_property_t;
|
typedef struct rses_property_st rses_property_t;
|
||||||
typedef struct router_client_session ROUTER_CLIENT_SES;
|
typedef struct router_client_session ROUTER_CLIENT_SES;
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
# Revision History
|
# Revision History
|
||||||
# Date Who Description
|
# Date Who Description
|
||||||
# 08/07/13 Mark Riddoch Initial implementation
|
# 08/07/13 Mark Riddoch Initial implementation
|
||||||
|
# 28/07/14 Massimiliano Pinto new monitor ndbcluster added
|
||||||
|
|
||||||
include ../../../build_gateway.inc
|
include ../../../build_gateway.inc
|
||||||
include ../../../makefile.inc
|
include ../../../makefile.inc
|
||||||
@ -38,11 +39,13 @@ MYSQLSRCS=mysql_mon.c
|
|||||||
MYSQLOBJ=$(MYSQLSRCS:.c=.o)
|
MYSQLOBJ=$(MYSQLSRCS:.c=.o)
|
||||||
GALERASRCS=galera_mon.c
|
GALERASRCS=galera_mon.c
|
||||||
GALERAOBJ=$(GALERASRCS:.c=.o)
|
GALERAOBJ=$(GALERASRCS:.c=.o)
|
||||||
SRCS=$(MYSQLSRCS)
|
NDBCLUSTERSRCS=ndbcluster_mon.c
|
||||||
|
NDBCLUSTEROBJ=$(NDBCLUSTERSRCS:.c=.o)
|
||||||
|
SRCS=$(MYSQLSRCS) $(GALERASRCS) $(NDBCLUSTERSRCS)
|
||||||
OBJ=$(SRCS:.c=.o)
|
OBJ=$(SRCS:.c=.o)
|
||||||
LIBS=$(UTILSPATH)/skygw_utils.o -llog_manager \
|
LIBS=$(UTILSPATH)/skygw_utils.o -llog_manager \
|
||||||
-L$(EMBEDDED_LIB) -lmysqld
|
-L$(EMBEDDED_LIB) -lmysqld
|
||||||
MODULES=libmysqlmon.so libgaleramon.so
|
MODULES=libmysqlmon.so libgaleramon.so libndbclustermon.so
|
||||||
|
|
||||||
|
|
||||||
all: $(MODULES)
|
all: $(MODULES)
|
||||||
@ -53,6 +56,9 @@ libmysqlmon.so: $(MYSQLOBJ)
|
|||||||
libgaleramon.so: $(GALERAOBJ)
|
libgaleramon.so: $(GALERAOBJ)
|
||||||
$(CC) $(LDFLAGS) $(GALERAOBJ) $(LIBS) -o $@
|
$(CC) $(LDFLAGS) $(GALERAOBJ) $(LIBS) -o $@
|
||||||
|
|
||||||
|
libndbclustermon.so: $(NDBCLUSTEROBJ)
|
||||||
|
$(CC) $(LDFLAGS) $(NDBCLUSTEROBJ) $(LIBS) -o $@
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
$(CC) $(CFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
|
461
server/modules/monitor/ndbcluster_mon.c
Normal file
461
server/modules/monitor/ndbcluster_mon.c
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file ndbcluster_mon.c - A MySQL cluster SQL node monitor
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 25/07/14 Massimiliano Pinto Initial implementation
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <monitor.h>
|
||||||
|
#include <mysqlmon.h>
|
||||||
|
#include <thread.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <mysqld_error.h>
|
||||||
|
#include <skygw_utils.h>
|
||||||
|
#include <log_manager.h>
|
||||||
|
#include <secrets.h>
|
||||||
|
#include <dcb.h>
|
||||||
|
#include <modinfo.h>
|
||||||
|
|
||||||
|
extern int lm_enabled_logfiles_bitmask;
|
||||||
|
|
||||||
|
static void monitorMain(void *);
|
||||||
|
|
||||||
|
static char *version_str = "V1.0.0";
|
||||||
|
|
||||||
|
MODULE_INFO info = {
|
||||||
|
MODULE_API_MONITOR,
|
||||||
|
MODULE_BETA_RELEASE,
|
||||||
|
MONITOR_VERSION,
|
||||||
|
"A MySQL cluster SQL node monitor"
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *startMonitor(void *);
|
||||||
|
static void stopMonitor(void *);
|
||||||
|
static void registerServer(void *, SERVER *);
|
||||||
|
static void unregisterServer(void *, SERVER *);
|
||||||
|
static void defaultUsers(void *, char *, char *);
|
||||||
|
static void diagnostics(DCB *, void *);
|
||||||
|
static void setInterval(void *, unsigned long);
|
||||||
|
|
||||||
|
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUsers, diagnostics, setInterval, NULL, NULL };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the mandatory version entry point
|
||||||
|
*
|
||||||
|
* @return version string of the module
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
version()
|
||||||
|
{
|
||||||
|
return version_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module initialisation routine, called when the module
|
||||||
|
* is first loaded.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ModuleInit()
|
||||||
|
{
|
||||||
|
LOGIF(LM, (skygw_log_write(
|
||||||
|
LOGFILE_MESSAGE,
|
||||||
|
"Initialise the MySQL Cluster Monitor module %s.\n",
|
||||||
|
version_str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module entry point routine. It is this routine that
|
||||||
|
* must populate the structure that is referred to as the
|
||||||
|
* "module object", this is a structure with the set of
|
||||||
|
* external entry points for this module.
|
||||||
|
*
|
||||||
|
* @return The module object
|
||||||
|
*/
|
||||||
|
MONITOR_OBJECT *
|
||||||
|
GetModuleObject()
|
||||||
|
{
|
||||||
|
return &MyObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the instance of the monitor, returning a handle on the monitor.
|
||||||
|
*
|
||||||
|
* This function creates a thread to execute the actual monitoring.
|
||||||
|
*
|
||||||
|
* @return A handle to use when interacting with the monitor
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
startMonitor(void *arg)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle;
|
||||||
|
|
||||||
|
if (arg != NULL)
|
||||||
|
{
|
||||||
|
handle = (MYSQL_MONITOR *)arg;
|
||||||
|
handle->shutdown = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
handle->databases = NULL;
|
||||||
|
handle->shutdown = 0;
|
||||||
|
handle->defaultUser = NULL;
|
||||||
|
handle->defaultPasswd = NULL;
|
||||||
|
handle->id = MONITOR_DEFAULT_ID;
|
||||||
|
handle->interval = MONITOR_INTERVAL;
|
||||||
|
spinlock_init(&handle->lock);
|
||||||
|
}
|
||||||
|
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a running monitor
|
||||||
|
*
|
||||||
|
* @param arg Handle on thr running monior
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
stopMonitor(void *arg)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||||
|
|
||||||
|
handle->shutdown = 1;
|
||||||
|
thread_wait((void *)handle->tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a server that must be added to the monitored servers for
|
||||||
|
* a monitoring module.
|
||||||
|
*
|
||||||
|
* @param arg A handle on the running monitor module
|
||||||
|
* @param server The server to add
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
registerServer(void *arg, SERVER *server)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||||
|
MONITOR_SERVERS *ptr, *db;
|
||||||
|
|
||||||
|
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||||
|
return;
|
||||||
|
db->server = server;
|
||||||
|
db->con = NULL;
|
||||||
|
db->next = NULL;
|
||||||
|
spinlock_acquire(&handle->lock);
|
||||||
|
if (handle->databases == NULL)
|
||||||
|
handle->databases = db;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr = handle->databases;
|
||||||
|
while (ptr->next != NULL)
|
||||||
|
ptr = ptr->next;
|
||||||
|
ptr->next = db;
|
||||||
|
}
|
||||||
|
spinlock_release(&handle->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a server from those being monitored by a monitoring module
|
||||||
|
*
|
||||||
|
* @param arg A handle on the running monitor module
|
||||||
|
* @param server The server to remove
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
unregisterServer(void *arg, SERVER *server)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||||
|
MONITOR_SERVERS *ptr, *lptr;
|
||||||
|
|
||||||
|
spinlock_acquire(&handle->lock);
|
||||||
|
if (handle->databases == NULL)
|
||||||
|
{
|
||||||
|
spinlock_release(&handle->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (handle->databases->server == server)
|
||||||
|
{
|
||||||
|
ptr = handle->databases;
|
||||||
|
handle->databases = handle->databases->next;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr = handle->databases;
|
||||||
|
while (ptr->next != NULL && ptr->next->server != server)
|
||||||
|
ptr = ptr->next;
|
||||||
|
if (ptr->next)
|
||||||
|
{
|
||||||
|
lptr = ptr->next;
|
||||||
|
ptr->next = ptr->next->next;
|
||||||
|
free(lptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spinlock_release(&handle->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diagnostic interface
|
||||||
|
*
|
||||||
|
* @param dcb DCB to send output
|
||||||
|
* @param arg The monitor handle
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
diagnostics(DCB *dcb, void *arg)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||||
|
MONITOR_SERVERS *db;
|
||||||
|
char *sep;
|
||||||
|
|
||||||
|
switch (handle->status)
|
||||||
|
{
|
||||||
|
case MONITOR_RUNNING:
|
||||||
|
dcb_printf(dcb, "\tMonitor running\n");
|
||||||
|
break;
|
||||||
|
case MONITOR_STOPPING:
|
||||||
|
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||||
|
break;
|
||||||
|
case MONITOR_STOPPED:
|
||||||
|
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||||
|
dcb_printf(dcb, "\tMonitored servers: ");
|
||||||
|
|
||||||
|
db = handle->databases;
|
||||||
|
sep = "";
|
||||||
|
while (db)
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, "%s%s:%d", sep, db->server->name, db->server->port);
|
||||||
|
sep = ", ";
|
||||||
|
db = db->next;
|
||||||
|
}
|
||||||
|
dcb_printf(dcb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default username and password to use to monitor if the server does not
|
||||||
|
* override this.
|
||||||
|
*
|
||||||
|
* @param arg The handle allocated by startMonitor
|
||||||
|
* @param uname The default user name
|
||||||
|
* @param passwd The default password
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
defaultUsers(void *arg, char *uname, char *passwd)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||||
|
|
||||||
|
if (handle->defaultUser)
|
||||||
|
free(handle->defaultUser);
|
||||||
|
if (handle->defaultPasswd)
|
||||||
|
free(handle->defaultPasswd);
|
||||||
|
handle->defaultUser = strdup(uname);
|
||||||
|
handle->defaultPasswd = strdup(passwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitor an individual server
|
||||||
|
*
|
||||||
|
* @param database The database to probe
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd)
|
||||||
|
{
|
||||||
|
MYSQL_ROW row;
|
||||||
|
MYSQL_RES *result;
|
||||||
|
int num_fields;
|
||||||
|
int isjoined = 0;
|
||||||
|
char *uname = defaultUser, *passwd = defaultPasswd;
|
||||||
|
unsigned long int server_version = 0;
|
||||||
|
char *server_string;
|
||||||
|
|
||||||
|
if (database->server->monuser != NULL)
|
||||||
|
{
|
||||||
|
uname = database->server->monuser;
|
||||||
|
passwd = database->server->monpw;
|
||||||
|
}
|
||||||
|
if (uname == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Don't even probe server flagged as in maintenance */
|
||||||
|
if (SERVER_IN_MAINT(database->server))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||||
|
{
|
||||||
|
char *dpwd = decryptPassword(passwd);
|
||||||
|
int rc;
|
||||||
|
int read_timeout = 1;
|
||||||
|
|
||||||
|
database->con = mysql_init(NULL);
|
||||||
|
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||||
|
|
||||||
|
if (mysql_real_connect(database->con, database->server->name,
|
||||||
|
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Monitor was unable to connect to "
|
||||||
|
"server %s:%d : \"%s\"",
|
||||||
|
database->server->name,
|
||||||
|
database->server->port,
|
||||||
|
mysql_error(database->con))));
|
||||||
|
server_clear_status(database->server, SERVER_RUNNING);
|
||||||
|
database->server->node_id = -1;
|
||||||
|
free(dpwd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free(dpwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we get this far then we have a working connection */
|
||||||
|
server_set_status(database->server, SERVER_RUNNING);
|
||||||
|
|
||||||
|
/* get server version from current server */
|
||||||
|
server_version = mysql_get_server_version(database->con);
|
||||||
|
|
||||||
|
/* get server version string */
|
||||||
|
server_string = (char *)mysql_get_server_info(database->con);
|
||||||
|
if (server_string) {
|
||||||
|
database->server->server_string = strdup(server_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the the SQL node is able to contact one or more data nodes */
|
||||||
|
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0
|
||||||
|
&& (result = mysql_store_result(database->con)) != NULL)
|
||||||
|
{
|
||||||
|
num_fields = mysql_num_fields(result);
|
||||||
|
while ((row = mysql_fetch_row(result)))
|
||||||
|
{
|
||||||
|
if (atoi(row[1]) > 0)
|
||||||
|
isjoined = 1;
|
||||||
|
}
|
||||||
|
mysql_free_result(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the the SQL node id in the MySQL cluster */
|
||||||
|
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0
|
||||||
|
&& (result = mysql_store_result(database->con)) != NULL)
|
||||||
|
{
|
||||||
|
long cluster_node_id = -1;
|
||||||
|
num_fields = mysql_num_fields(result);
|
||||||
|
while ((row = mysql_fetch_row(result)))
|
||||||
|
{
|
||||||
|
cluster_node_id = strtol(row[1], NULL, 10);
|
||||||
|
if ((errno == ERANGE && (cluster_node_id == LONG_MAX
|
||||||
|
|| cluster_node_id == LONG_MIN)) || (errno != 0 && cluster_node_id == 0))
|
||||||
|
{
|
||||||
|
cluster_node_id = -1;
|
||||||
|
}
|
||||||
|
database->server->node_id = cluster_node_id;
|
||||||
|
}
|
||||||
|
mysql_free_result(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isjoined) {
|
||||||
|
server_set_status(database->server, SERVER_NDB);
|
||||||
|
database->server->depth = 0;
|
||||||
|
} else {
|
||||||
|
server_clear_status(database->server, SERVER_NDB);
|
||||||
|
database->server->depth = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry point for the monitoring module thread
|
||||||
|
*
|
||||||
|
* @param arg The handle of the monitor
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
monitorMain(void *arg)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||||
|
MONITOR_SERVERS *ptr;
|
||||||
|
long master_id;
|
||||||
|
|
||||||
|
if (mysql_thread_init())
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Fatal : mysql_thread_init failed in monitor "
|
||||||
|
"module. Exiting.\n")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handle->status = MONITOR_RUNNING;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
master_id = -1;
|
||||||
|
|
||||||
|
if (handle->shutdown)
|
||||||
|
{
|
||||||
|
handle->status = MONITOR_STOPPING;
|
||||||
|
mysql_thread_end();
|
||||||
|
handle->status = MONITOR_STOPPED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = handle->databases;
|
||||||
|
|
||||||
|
while (ptr)
|
||||||
|
{
|
||||||
|
unsigned int prev_status = ptr->server->status;
|
||||||
|
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd);
|
||||||
|
|
||||||
|
if (ptr->server->status != prev_status ||
|
||||||
|
SERVER_IS_DOWN(ptr->server))
|
||||||
|
{
|
||||||
|
LOGIF(LM, (skygw_log_write_flush(
|
||||||
|
LOGFILE_MESSAGE,
|
||||||
|
"Backend server %s:%d state : %s",
|
||||||
|
ptr->server->name,
|
||||||
|
ptr->server->port,
|
||||||
|
STRSRVSTATUS(ptr->server))));
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_millisleep(handle->interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the monitor sampling interval.
|
||||||
|
*
|
||||||
|
* @param arg The handle allocated by startMonitor
|
||||||
|
* @param interval The interval to set in monitor struct, in milliseconds
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
setInterval(void *arg, unsigned long interval)
|
||||||
|
{
|
||||||
|
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||||
|
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||||
|
}
|
@ -422,7 +422,6 @@ static int gw_read_backend_event(DCB *dcb) {
|
|||||||
GWBUF *read_buffer = NULL;
|
GWBUF *read_buffer = NULL;
|
||||||
ROUTER_OBJECT *router = NULL;
|
ROUTER_OBJECT *router = NULL;
|
||||||
ROUTER *router_instance = NULL;
|
ROUTER *router_instance = NULL;
|
||||||
void *rsession = NULL;
|
|
||||||
SESSION *session = dcb->session;
|
SESSION *session = dcb->session;
|
||||||
int nbytes_read = 0;
|
int nbytes_read = 0;
|
||||||
|
|
||||||
|
@ -1420,7 +1420,6 @@ static int route_by_statement(
|
|||||||
int rc = -1;
|
int rc = -1;
|
||||||
GWBUF* packetbuf;
|
GWBUF* packetbuf;
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
gwbuf_type_t prevtype;
|
|
||||||
GWBUF* tmpbuf;
|
GWBUF* tmpbuf;
|
||||||
|
|
||||||
tmpbuf = *p_readbuf;
|
tmpbuf = *p_readbuf;
|
||||||
|
@ -88,19 +88,15 @@ install: $(MODULES)
|
|||||||
(cd readwritesplit; make DEST=$(DEST) install)
|
(cd readwritesplit; make DEST=$(DEST) install)
|
||||||
|
|
||||||
cleantests:
|
cleantests:
|
||||||
$(MAKE) -C readwritesplit/test cleantests
|
|
||||||
$(MAKE) -C test cleantests
|
$(MAKE) -C test cleantests
|
||||||
|
|
||||||
buildtests:
|
buildtests:
|
||||||
$(MAKE) -C readwritesplit/test DEBUG=Y buildtests
|
|
||||||
$(MAKE) -C test DEBUG=Y buildtests
|
$(MAKE) -C test DEBUG=Y buildtests
|
||||||
|
|
||||||
runtests:
|
runtests:
|
||||||
$(MAKE) -C test runtests
|
$(MAKE) -C test runtests
|
||||||
$(MAKE) -C readwritesplit runtests
|
|
||||||
|
|
||||||
testall:
|
testall:
|
||||||
$(MAKE) -C test testall
|
$(MAKE) -C test testall
|
||||||
$(MAKE) -C readwritesplit testall
|
|
||||||
|
|
||||||
include depend.mk
|
include depend.mk
|
||||||
|
@ -831,6 +831,7 @@ static struct {
|
|||||||
{ "master", SERVER_MASTER },
|
{ "master", SERVER_MASTER },
|
||||||
{ "slave", SERVER_SLAVE },
|
{ "slave", SERVER_SLAVE },
|
||||||
{ "synced", SERVER_JOINED },
|
{ "synced", SERVER_JOINED },
|
||||||
|
{ "ndb", SERVER_NDB },
|
||||||
{ "maintenance", SERVER_MAINT },
|
{ "maintenance", SERVER_MAINT },
|
||||||
{ "maint", SERVER_MAINT },
|
{ "maint", SERVER_MAINT },
|
||||||
{ NULL, 0 }
|
{ NULL, 0 }
|
||||||
|
@ -311,6 +311,11 @@ char *weightby;
|
|||||||
inst->bitmask |= (SERVER_JOINED);
|
inst->bitmask |= (SERVER_JOINED);
|
||||||
inst->bitvalue |= SERVER_JOINED;
|
inst->bitvalue |= SERVER_JOINED;
|
||||||
}
|
}
|
||||||
|
else if (!strcasecmp(options[i], "ndb"))
|
||||||
|
{
|
||||||
|
inst->bitmask |= (SERVER_NDB);
|
||||||
|
inst->bitvalue |= SERVER_NDB;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGIF(LM, (skygw_log_write(
|
LOGIF(LM, (skygw_log_write(
|
||||||
@ -318,7 +323,7 @@ char *weightby;
|
|||||||
"* Warning : Unsupported router "
|
"* Warning : Unsupported router "
|
||||||
"option \'%s\' for readconnroute. "
|
"option \'%s\' for readconnroute. "
|
||||||
"Expected router options are "
|
"Expected router options are "
|
||||||
"[slave|master|synced]",
|
"[slave|master|synced|ndb]",
|
||||||
options[i])));
|
options[i])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,12 @@ static int rses_get_max_slavecount(ROUTER_CLIENT_SES* rses, int router_nservers
|
|||||||
static int rses_get_max_replication_lag(ROUTER_CLIENT_SES* rses);
|
static int rses_get_max_replication_lag(ROUTER_CLIENT_SES* rses);
|
||||||
static backend_ref_t* get_bref_from_dcb(ROUTER_CLIENT_SES* rses, DCB* dcb);
|
static backend_ref_t* get_bref_from_dcb(ROUTER_CLIENT_SES* rses, DCB* dcb);
|
||||||
|
|
||||||
|
static route_target_t get_route_target (
|
||||||
|
skygw_query_type_t qtype,
|
||||||
|
bool trx_active,
|
||||||
|
HINT* hint);
|
||||||
|
|
||||||
|
|
||||||
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
|
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
|
||||||
|
|
||||||
#if defined(NOT_USED)
|
#if defined(NOT_USED)
|
||||||
@ -154,7 +160,9 @@ static bool select_connect_backend_servers(
|
|||||||
static bool get_dcb(
|
static bool get_dcb(
|
||||||
DCB** dcb,
|
DCB** dcb,
|
||||||
ROUTER_CLIENT_SES* rses,
|
ROUTER_CLIENT_SES* rses,
|
||||||
backend_type_t btype);
|
backend_type_t btype,
|
||||||
|
char* name,
|
||||||
|
int max_rlag);
|
||||||
|
|
||||||
static void rwsplit_process_router_options(
|
static void rwsplit_process_router_options(
|
||||||
ROUTER_INSTANCE* router,
|
ROUTER_INSTANCE* router,
|
||||||
@ -913,19 +921,29 @@ static void freeSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a pointer to a suitable backend dcb.
|
* Provide the router with a pointer to a suitable backend dcb.
|
||||||
* Detect failures in server statuses and reselect backends if necessary.
|
* Detect failures in server statuses and reselect backends if necessary.
|
||||||
|
* If name is specified, server name becomes primary selection criteria.
|
||||||
|
*
|
||||||
|
* @param p_dcb Address of the pointer to the resulting DCB
|
||||||
|
* @param rses Pointer to router client session
|
||||||
|
* @param btype Backend type
|
||||||
|
* @param name Name of the backend which is primarily searched. May be NULL.
|
||||||
|
*
|
||||||
|
* @return True if proper DCB was found, false otherwise.
|
||||||
*/
|
*/
|
||||||
static bool get_dcb(
|
static bool get_dcb(
|
||||||
DCB** p_dcb,
|
DCB** p_dcb,
|
||||||
ROUTER_CLIENT_SES* rses,
|
ROUTER_CLIENT_SES* rses,
|
||||||
backend_type_t btype)
|
backend_type_t btype,
|
||||||
|
char* name,
|
||||||
|
int max_rlag)
|
||||||
{
|
{
|
||||||
backend_ref_t* backend_ref;
|
backend_ref_t* backend_ref;
|
||||||
int smallest_nconn = -1;
|
int smallest_nconn = -1;
|
||||||
int i;
|
int i;
|
||||||
bool succp = false;
|
bool succp = false;
|
||||||
BACKEND *master_host = NULL;
|
BACKEND* master_host;
|
||||||
|
|
||||||
CHK_CLIENT_RSES(rses);
|
CHK_CLIENT_RSES(rses);
|
||||||
ss_dassert(p_dcb != NULL && *(p_dcb) == NULL);
|
ss_dassert(p_dcb != NULL && *(p_dcb) == NULL);
|
||||||
@ -936,18 +954,69 @@ static bool get_dcb(
|
|||||||
}
|
}
|
||||||
backend_ref = rses->rses_backend_ref;
|
backend_ref = rses->rses_backend_ref;
|
||||||
|
|
||||||
/* get root master from availbal servers */
|
/** get root master from available servers */
|
||||||
master_host = get_root_master(backend_ref, rses->rses_nbackends);
|
master_host = get_root_master(backend_ref, rses->rses_nbackends);
|
||||||
|
|
||||||
if (btype == BE_SLAVE)
|
if (btype == BE_SLAVE)
|
||||||
|
{
|
||||||
|
if (name != NULL) /*< Choose backend by name (hint) */
|
||||||
{
|
{
|
||||||
for (i=0; i<rses->rses_nbackends; i++)
|
for (i=0; i<rses->rses_nbackends; i++)
|
||||||
{
|
{
|
||||||
BACKEND* b = backend_ref[i].bref_backend;
|
BACKEND* b = backend_ref[i].bref_backend;
|
||||||
/* check slave bit, also for relay servers (Master & Servers) */
|
|
||||||
|
/**
|
||||||
|
* To become chosen:
|
||||||
|
* backend must be in use, name must match,
|
||||||
|
* root master node must be found,
|
||||||
|
* backend's role must be either slave, relay
|
||||||
|
* server, or master.
|
||||||
|
*/
|
||||||
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
||||||
(SERVER_IS_SLAVE(b->backend_server) || SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
(strncasecmp(
|
||||||
(master_host != NULL && b->backend_server != master_host->backend_server) &&
|
name,
|
||||||
|
b->backend_server->unique_name,
|
||||||
|
MIN(strlen(b->backend_server->unique_name), PATH_MAX)) == 0) &&
|
||||||
|
master_host != NULL &&
|
||||||
|
#if 0
|
||||||
|
(max_rlag == MAX_RLAG_UNDEFINED ||
|
||||||
|
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||||
|
b->backend_server->rlag <= max_rlag)) &&
|
||||||
|
#endif
|
||||||
|
(SERVER_IS_SLAVE(b->backend_server) ||
|
||||||
|
SERVER_IS_RELAY_SERVER(b->backend_server) ||
|
||||||
|
SERVER_IS_MASTER(b->backend_server)))
|
||||||
|
{
|
||||||
|
*p_dcb = backend_ref[i].bref_dcb;
|
||||||
|
succp = true;
|
||||||
|
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!succp) /*< No hints or finding named backend failed */
|
||||||
|
{
|
||||||
|
for (i=0; i<rses->rses_nbackends; i++)
|
||||||
|
{
|
||||||
|
BACKEND* b = backend_ref[i].bref_backend;
|
||||||
|
/**
|
||||||
|
* To become chosen:
|
||||||
|
* backend must be in use,
|
||||||
|
* root master node must be found,
|
||||||
|
* backend is not allowed to be the master,
|
||||||
|
* backend's role can be either slave or relay
|
||||||
|
* server and it must have least connections
|
||||||
|
* at the moment.
|
||||||
|
*/
|
||||||
|
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
||||||
|
master_host != NULL &&
|
||||||
|
b->backend_server != master_host->backend_server &&
|
||||||
|
(max_rlag == MAX_RLAG_UNDEFINED ||
|
||||||
|
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||||
|
b->backend_server->rlag <= max_rlag)) &&
|
||||||
|
(SERVER_IS_SLAVE(b->backend_server) ||
|
||||||
|
SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||||
(smallest_nconn == -1 ||
|
(smallest_nconn == -1 ||
|
||||||
b->backend_conn_count < smallest_nconn))
|
b->backend_conn_count < smallest_nconn))
|
||||||
{
|
{
|
||||||
@ -957,21 +1026,11 @@ static bool get_dcb(
|
|||||||
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!succp)
|
if (!succp) /*< No valid slave was found, search master next */
|
||||||
{
|
{
|
||||||
backend_ref = rses->rses_master_ref;
|
btype = BE_MASTER;
|
||||||
|
|
||||||
if (BREF_IS_IN_USE(backend_ref))
|
|
||||||
{
|
|
||||||
*p_dcb = backend_ref->bref_dcb;
|
|
||||||
succp = true;
|
|
||||||
|
|
||||||
ss_dassert(backend_ref->bref_dcb->state != DCB_STATE_ZOMBIE);
|
|
||||||
|
|
||||||
ss_dassert(
|
|
||||||
(master_host && (backend_ref->bref_backend->backend_server == master_host->backend_server)) &&
|
|
||||||
smallest_nconn == -1);
|
|
||||||
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
@ -982,9 +1041,8 @@ static bool get_dcb(
|
|||||||
backend_ref->bref_backend->backend_server->port)));
|
backend_ref->bref_backend->backend_server->port)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ss_dassert(succp);
|
|
||||||
}
|
if (btype == BE_MASTER)
|
||||||
else if (btype == BE_MASTER)
|
|
||||||
{
|
{
|
||||||
for (i=0; i<rses->rses_nbackends; i++)
|
for (i=0; i<rses->rses_nbackends; i++)
|
||||||
{
|
{
|
||||||
@ -999,10 +1057,107 @@ static bool get_dcb(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return_succp:
|
return_succp:
|
||||||
return succp;
|
return succp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Examine the query type, transaction state and routing hints. Find out the
|
||||||
|
* target for query routing.
|
||||||
|
*
|
||||||
|
* @param qtype Type of query
|
||||||
|
* @param trx_active Is transacation active or not
|
||||||
|
* @param hint Pointer to list of hints attached to the query buffer
|
||||||
|
*
|
||||||
|
* @return bitfield including the routing target, or the target server name
|
||||||
|
* if the query would otherwise be routed to slave.
|
||||||
|
*/
|
||||||
|
static route_target_t get_route_target (
|
||||||
|
skygw_query_type_t qtype,
|
||||||
|
bool trx_active,
|
||||||
|
HINT* hint)
|
||||||
|
{
|
||||||
|
route_target_t target;
|
||||||
|
|
||||||
|
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||||
|
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||||
|
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT))
|
||||||
|
{
|
||||||
|
/** hints don't affect on routing */
|
||||||
|
target = TARGET_ALL;
|
||||||
|
}
|
||||||
|
else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) && !trx_active)
|
||||||
|
{
|
||||||
|
target = TARGET_SLAVE;
|
||||||
|
|
||||||
|
/** process routing hints */
|
||||||
|
while (hint != NULL)
|
||||||
|
{
|
||||||
|
if (hint->type == HINT_ROUTE_TO_MASTER)
|
||||||
|
{
|
||||||
|
target = TARGET_MASTER; /*< override */
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Hint: route to master.")));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
||||||
|
{
|
||||||
|
target |= TARGET_NAMED_SERVER; /*< add */
|
||||||
|
}
|
||||||
|
else if (hint->type == HINT_ROUTE_TO_UPTODATE_SERVER)
|
||||||
|
{
|
||||||
|
/** not implemented */
|
||||||
|
}
|
||||||
|
else if (hint->type == HINT_ROUTE_TO_ALL)
|
||||||
|
{
|
||||||
|
/** not implemented */
|
||||||
|
}
|
||||||
|
else if (hint->type == HINT_PARAMETER)
|
||||||
|
{
|
||||||
|
if (strncasecmp(
|
||||||
|
(char *)hint->data,
|
||||||
|
"max_slave_replication_lag",
|
||||||
|
strlen("max_slave_replication_lag")) == 0)
|
||||||
|
{
|
||||||
|
target |= TARGET_RLAG_MAX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Error : Unknown hint parameter "
|
||||||
|
"'%s' when 'max_slave_replication_lag' "
|
||||||
|
"was expected.",
|
||||||
|
(char *)hint->data)));
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Unknown hint parameter "
|
||||||
|
"'%s' when 'max_slave_replication_lag' "
|
||||||
|
"was expected.",
|
||||||
|
(char *)hint->data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (hint->type == HINT_ROUTE_TO_SLAVE)
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Hint: route to slave.")));
|
||||||
|
}
|
||||||
|
hint = hint->next;
|
||||||
|
} /*< while (hint != NULL) */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/** hints don't affect on routing */
|
||||||
|
target = TARGET_MASTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main routing entry, this is called with every packet that is
|
* The main routing entry, this is called with every packet that is
|
||||||
* received and has to be forwarded to the backend database.
|
* received and has to be forwarded to the backend database.
|
||||||
@ -1032,14 +1187,20 @@ static int routeQuery(
|
|||||||
GWBUF* querybuf)
|
GWBUF* querybuf)
|
||||||
{
|
{
|
||||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||||
|
GWBUF* plainsqlbuf = NULL;
|
||||||
|
char* querystr = NULL;
|
||||||
|
char* startpos;
|
||||||
mysql_server_cmd_t packet_type;
|
mysql_server_cmd_t packet_type;
|
||||||
uint8_t* packet;
|
uint8_t* packet;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
DCB* master_dcb = NULL;
|
DCB* master_dcb = NULL;
|
||||||
DCB* slave_dcb = NULL;
|
DCB* target_dcb = NULL;
|
||||||
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
|
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
|
||||||
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
||||||
bool rses_is_closed = false;
|
bool rses_is_closed = false;
|
||||||
|
size_t len;
|
||||||
|
MYSQL* mysql = NULL;
|
||||||
|
route_target_t route_target;
|
||||||
|
|
||||||
CHK_CLIENT_RSES(router_cli_ses);
|
CHK_CLIENT_RSES(router_cli_ses);
|
||||||
|
|
||||||
@ -1165,11 +1326,19 @@ static int routeQuery(
|
|||||||
router_cli_ses->rses_transaction_active = false;
|
router_cli_ses->rses_transaction_active = false;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Session update is always routed in the same way.
|
* Find out where to route the query. Result may not be clear; it is
|
||||||
|
* possible to have a hint for routing to a named server which can
|
||||||
|
* be either slave or master.
|
||||||
|
* If query would otherwise be routed to slave then the hint determines
|
||||||
|
* actual target server if it exists.
|
||||||
|
*
|
||||||
|
* route_target is a bitfield and may include multiple values.
|
||||||
*/
|
*/
|
||||||
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
route_target = get_route_target(qtype,
|
||||||
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
router_cli_ses->rses_transaction_active,
|
||||||
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT))
|
querybuf->hint);
|
||||||
|
|
||||||
|
if (TARGET_IS_ALL(route_target))
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* It is not sure if the session command in question requires
|
* It is not sure if the session command in question requires
|
||||||
@ -1188,27 +1357,101 @@ static int routeQuery(
|
|||||||
}
|
}
|
||||||
goto return_ret;
|
goto return_ret;
|
||||||
}
|
}
|
||||||
else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) &&
|
/**
|
||||||
!router_cli_ses->rses_transaction_active)
|
* Handle routing to master and to slave
|
||||||
|
*/
|
||||||
|
else
|
||||||
{
|
{
|
||||||
bool succp;
|
bool succp = true;
|
||||||
|
HINT* hint;
|
||||||
|
char* named_server = NULL;
|
||||||
|
int rlag_max = MAX_RLAG_UNDEFINED;
|
||||||
|
|
||||||
|
if (router_cli_ses->rses_transaction_active) /*< all to master */
|
||||||
|
{
|
||||||
|
route_target = TARGET_MASTER; /*< override old value */
|
||||||
|
|
||||||
LOGIF(LT, (skygw_log_write(
|
LOGIF(LT, (skygw_log_write(
|
||||||
LOGFILE_TRACE,
|
LOGFILE_TRACE,
|
||||||
"[%s]\tRead-only query, routing to Slave.",
|
"Transaction is active, routing to Master.")));
|
||||||
inst->service->name)));
|
}
|
||||||
ss_dassert(QUERY_IS_TYPE(qtype, QUERY_TYPE_READ));
|
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s", STRQTYPE(qtype))));
|
||||||
|
|
||||||
/** Lock router session */
|
/** Lock router session */
|
||||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||||
{
|
{
|
||||||
goto return_ret;
|
goto return_ret;
|
||||||
}
|
}
|
||||||
succp = get_dcb(&slave_dcb, router_cli_ses, BE_SLAVE);
|
|
||||||
|
|
||||||
if (succp)
|
if (TARGET_IS_SLAVE(route_target))
|
||||||
{
|
{
|
||||||
if ((ret = slave_dcb->func.write(slave_dcb, gwbuf_clone(querybuf))) == 1)
|
if (TARGET_IS_NAMED_SERVER(route_target) ||
|
||||||
|
TARGET_IS_RLAG_MAX(route_target))
|
||||||
|
{
|
||||||
|
hint = querybuf->hint;
|
||||||
|
|
||||||
|
while (hint != NULL)
|
||||||
|
{
|
||||||
|
if (hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
||||||
|
{
|
||||||
|
named_server = hint->data;
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Hint: route to server "
|
||||||
|
"'%s'",
|
||||||
|
named_server)));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (hint->type == HINT_PARAMETER &&
|
||||||
|
(strncasecmp(
|
||||||
|
(char *)hint->data,
|
||||||
|
"max_slave_replication_lag",
|
||||||
|
strlen("max_slave_replication_lag")) == 0))
|
||||||
|
{
|
||||||
|
int val = (int) strtol((char *)hint->value,
|
||||||
|
(char **)NULL, 10);
|
||||||
|
|
||||||
|
if (val != 0 || errno == 0)
|
||||||
|
{
|
||||||
|
rlag_max = val;
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Hint: "
|
||||||
|
"max_slave_replication_lag=%d",
|
||||||
|
rlag_max)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hint = hint->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rlag_max == MAX_RLAG_UNDEFINED) /*< no rlag max hint, use config */
|
||||||
|
{
|
||||||
|
rlag_max = rses_get_max_replication_lag(router_cli_ses);
|
||||||
|
}
|
||||||
|
|
||||||
|
succp = get_dcb(&target_dcb,
|
||||||
|
router_cli_ses,
|
||||||
|
BE_SLAVE,
|
||||||
|
named_server,
|
||||||
|
rlag_max);
|
||||||
|
}
|
||||||
|
else if (TARGET_IS_MASTER(route_target))
|
||||||
|
{
|
||||||
|
if (master_dcb == NULL)
|
||||||
|
{
|
||||||
|
succp = get_dcb(&master_dcb,
|
||||||
|
router_cli_ses,
|
||||||
|
BE_MASTER,
|
||||||
|
NULL,
|
||||||
|
MAX_RLAG_UNDEFINED);
|
||||||
|
}
|
||||||
|
target_dcb = master_dcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (succp) /*< Have DCB of the target backend */
|
||||||
|
{
|
||||||
|
if ((ret = target_dcb->func.write(target_dcb, querybuf)) == 1)
|
||||||
{
|
{
|
||||||
backend_ref_t* bref;
|
backend_ref_t* bref;
|
||||||
|
|
||||||
@ -1216,82 +1459,19 @@ static int routeQuery(
|
|||||||
/**
|
/**
|
||||||
* Add one query response waiter to backend reference
|
* Add one query response waiter to backend reference
|
||||||
*/
|
*/
|
||||||
bref = get_bref_from_dcb(router_cli_ses, slave_dcb);
|
bref = get_bref_from_dcb(router_cli_ses, target_dcb);
|
||||||
bref_set_state(bref, BREF_QUERY_ACTIVE);
|
bref_set_state(bref, BREF_QUERY_ACTIVE);
|
||||||
bref_set_state(bref, BREF_WAITING_RESULT);
|
bref_set_state(bref, BREF_WAITING_RESULT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char* query_str = modutil_get_query(querybuf);
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
"Error : Routing query \"%s\" failed.",
|
"Error : Routing query \"%s\" failed.",
|
||||||
(query_str == NULL ? "not available" : query_str))));
|
querystr)));
|
||||||
free(query_str);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rses_end_locked_router_action(router_cli_ses);
|
rses_end_locked_router_action(router_cli_ses);
|
||||||
|
|
||||||
ss_dassert(succp);
|
|
||||||
goto return_ret;
|
|
||||||
}
|
|
||||||
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.")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** Lock router session */
|
|
||||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
|
||||||
{
|
|
||||||
goto return_ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (master_dcb == NULL)
|
|
||||||
{
|
|
||||||
succp = get_dcb(&master_dcb, router_cli_ses, BE_MASTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (succp)
|
|
||||||
{
|
|
||||||
if ((ret = master_dcb->func.write(master_dcb, gwbuf_clone(querybuf))) == 1)
|
|
||||||
{
|
|
||||||
backend_ref_t* bref;
|
|
||||||
|
|
||||||
atomic_add(&inst->stats.n_master, 1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add one write response waiter to backend reference
|
|
||||||
*/
|
|
||||||
bref = get_bref_from_dcb(router_cli_ses, master_dcb);
|
|
||||||
bref_set_state(bref, BREF_QUERY_ACTIVE);
|
|
||||||
bref_set_state(bref, BREF_WAITING_RESULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rses_end_locked_router_action(router_cli_ses);
|
|
||||||
|
|
||||||
ss_dassert(succp);
|
|
||||||
|
|
||||||
if (ret == 0)
|
|
||||||
{
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
|
||||||
LOGFILE_ERROR,
|
|
||||||
"Error : Routing to master failed.")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return_ret:
|
return_ret:
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
@ -1315,7 +1495,7 @@ return_ret:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** to be inline'd */
|
|
||||||
/**
|
/**
|
||||||
* @node Acquires lock to router client session if it is not closed.
|
* @node Acquires lock to router client session if it is not closed.
|
||||||
*
|
*
|
||||||
@ -1520,7 +1700,6 @@ static void clientReply (
|
|||||||
if (LOG_IS_ENABLED(LOGFILE_ERROR) &&
|
if (LOG_IS_ENABLED(LOGFILE_ERROR) &&
|
||||||
MYSQL_IS_ERROR_PACKET(((uint8_t *)GWBUF_DATA(writebuf))))
|
MYSQL_IS_ERROR_PACKET(((uint8_t *)GWBUF_DATA(writebuf))))
|
||||||
{
|
{
|
||||||
SESSION* ses = backend_dcb->session;
|
|
||||||
uint8_t* buf =
|
uint8_t* buf =
|
||||||
(uint8_t *)GWBUF_DATA((scur->scmd_cur_cmd->my_sescmd_buf));
|
(uint8_t *)GWBUF_DATA((scur->scmd_cur_cmd->my_sescmd_buf));
|
||||||
size_t len = MYSQL_GET_PACKET_LEN(buf);
|
size_t len = MYSQL_GET_PACKET_LEN(buf);
|
||||||
@ -1845,7 +2024,9 @@ static bool select_connect_backend_servers(
|
|||||||
#endif
|
#endif
|
||||||
/* assert with master_host */
|
/* assert with master_host */
|
||||||
ss_dassert(!master_connected ||
|
ss_dassert(!master_connected ||
|
||||||
(master_host && ((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) && SERVER_MASTER));
|
(master_host &&
|
||||||
|
((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) &&
|
||||||
|
SERVER_MASTER));
|
||||||
/**
|
/**
|
||||||
* Sort the pointer list to servers according to connection counts. As
|
* Sort the pointer list to servers according to connection counts. As
|
||||||
* a consequence those backends having least connections are in the
|
* a consequence those backends having least connections are in the
|
||||||
@ -1923,8 +2104,8 @@ static bool select_connect_backend_servers(
|
|||||||
{
|
{
|
||||||
/* check also for relay servers and don't take the master_host */
|
/* check also for relay servers and don't take the master_host */
|
||||||
if (slaves_found < max_nslaves &&
|
if (slaves_found < max_nslaves &&
|
||||||
(max_slave_rlag == -2 ||
|
(max_slave_rlag == MAX_RLAG_UNDEFINED ||
|
||||||
(b->backend_server->rlag != -1 && /*< information currently not available */
|
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||||
b->backend_server->rlag <= max_slave_rlag)) &&
|
b->backend_server->rlag <= max_slave_rlag)) &&
|
||||||
(SERVER_IS_SLAVE(b->backend_server) || SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
(SERVER_IS_SLAVE(b->backend_server) || SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||||
(master_host != NULL && (b->backend_server != master_host->backend_server)))
|
(master_host != NULL && (b->backend_server != master_host->backend_server)))
|
||||||
@ -2054,7 +2235,8 @@ static bool select_connect_backend_servers(
|
|||||||
}
|
}
|
||||||
/* assert with master_host */
|
/* assert with master_host */
|
||||||
ss_dassert(!master_connected ||
|
ss_dassert(!master_connected ||
|
||||||
(master_host && ((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) && SERVER_MASTER));
|
(master_host && ((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) &&
|
||||||
|
SERVER_MASTER));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2117,14 +2299,10 @@ static bool select_connect_backend_servers(
|
|||||||
|
|
||||||
if (BREF_IS_IN_USE((&backend_ref[i])))
|
if (BREF_IS_IN_USE((&backend_ref[i])))
|
||||||
{
|
{
|
||||||
backend_type_t btype = BACKEND_TYPE(b);
|
|
||||||
|
|
||||||
LOGIF(LT, (skygw_log_write(
|
LOGIF(LT, (skygw_log_write(
|
||||||
LOGFILE_TRACE,
|
LOGFILE_TRACE,
|
||||||
"Selected %s in \t%s:%d",
|
"Selected %s in \t%s:%d",
|
||||||
(btype == BE_MASTER ? "master" :
|
STRSRVSTATUS(b->backend_server),
|
||||||
(btype == BE_SLAVE ? "slave" :
|
|
||||||
"unknown node type")),
|
|
||||||
b->backend_server->name,
|
b->backend_server->name,
|
||||||
b->backend_server->port)));
|
b->backend_server->port)));
|
||||||
}
|
}
|
||||||
@ -2943,7 +3121,6 @@ static bool route_session_write(
|
|||||||
/** Lock router session */
|
/** Lock router session */
|
||||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||||
{
|
{
|
||||||
rses_property_done(prop);
|
|
||||||
succp = false;
|
succp = false;
|
||||||
goto return_succp;
|
goto return_succp;
|
||||||
}
|
}
|
||||||
@ -3013,6 +3190,7 @@ return_succp:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(NOT_USED)
|
#if defined(NOT_USED)
|
||||||
|
|
||||||
static bool router_option_configured(
|
static bool router_option_configured(
|
||||||
ROUTER_INSTANCE* router,
|
ROUTER_INSTANCE* router,
|
||||||
const char* optionstr,
|
const char* optionstr,
|
||||||
@ -3189,6 +3367,11 @@ static bool handle_error_reply_client(
|
|||||||
CHK_DCB(client_dcb);
|
CHK_DCB(client_dcb);
|
||||||
client_dcb->func.write(client_dcb, errmsg);
|
client_dcb->func.write(client_dcb, errmsg);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ((errmsg=gwbuf_consume(errmsg, GWBUF_LENGTH(errmsg))) != NULL)
|
||||||
|
;
|
||||||
|
}
|
||||||
succp = false; /** false because new servers aren's selected. */
|
succp = false; /** false because new servers aren's selected. */
|
||||||
|
|
||||||
return succp;
|
return succp;
|
||||||
@ -3243,6 +3426,11 @@ static bool handle_error_new_connection(
|
|||||||
client_dcb->func.write(client_dcb, errmsg);
|
client_dcb->func.write(client_dcb, errmsg);
|
||||||
bref_clear_state(bref, BREF_WAITING_RESULT);
|
bref_clear_state(bref, BREF_WAITING_RESULT);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ((errmsg=gwbuf_consume(errmsg, GWBUF_LENGTH(errmsg))) != NULL)
|
||||||
|
;
|
||||||
|
}
|
||||||
bref_clear_state(bref, BREF_IN_USE);
|
bref_clear_state(bref, BREF_IN_USE);
|
||||||
bref_set_state(bref, BREF_CLOSED);
|
bref_set_state(bref, BREF_CLOSED);
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,7 @@ testall:
|
|||||||
-$(MAKE) cleantests
|
-$(MAKE) cleantests
|
||||||
-$(MAKE) DEBUG=Y buildtests
|
-$(MAKE) DEBUG=Y buildtests
|
||||||
-$(MAKE) runtests
|
-$(MAKE) runtests
|
||||||
|
-$(MAKE) -C test_hints testall
|
||||||
|
|
||||||
buildtests:
|
buildtests:
|
||||||
|
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
# cleantests - clean local and subdirectories' tests
|
||||||
|
# buildtests - build all local and subdirectories' tests
|
||||||
|
# runtests - run all local tests
|
||||||
|
# testall - clean, build and run local and subdirectories' tests
|
||||||
|
|
||||||
|
include ../../../../../../build_gateway.inc
|
||||||
|
include $(ROOT_PATH)/makefile.inc
|
||||||
|
include $(ROOT_PATH)/test.inc
|
||||||
|
|
||||||
|
ARGS=6
|
||||||
|
|
||||||
|
CC=cc
|
||||||
|
TESTLOG := $(shell pwd)/testrwsplit_hints.log
|
||||||
|
RET := -1
|
||||||
|
|
||||||
|
cleantests:
|
||||||
|
- $(DEL) *.o
|
||||||
|
- $(DEL) *~
|
||||||
|
- $(DEL) *.sql
|
||||||
|
- $(DEL) *.output
|
||||||
|
- $(DEL) *.log
|
||||||
|
|
||||||
|
testall:
|
||||||
|
-$(MAKE) cleantests
|
||||||
|
-$(MAKE) DEBUG=Y buildtests
|
||||||
|
-$(MAKE) runtests
|
||||||
|
|
||||||
|
buildtests:
|
||||||
|
|
||||||
|
|
||||||
|
runtests:
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
@echo "-------------------------------" >> $(TESTLOG)
|
||||||
|
@echo $(shell date) >> $(TESTLOG)
|
||||||
|
@echo "Test Read/Write split router - hint routing" >> $(TESTLOG)
|
||||||
|
@echo "-------------------------------" >> $(TESTLOG)
|
||||||
|
@echo "Running simple tests" >> $(TESTLOG)
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) simple_tests
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
@echo "Running syntax error tests" >> $(TESTLOG)
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
./syntax_check.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) error_tests
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
@echo "Running complex tests" >> $(TESTLOG)
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) complex_tests
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
@echo "Running stack tests" >> $(TESTLOG)
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) stack_tests
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
|
@ -0,0 +1,48 @@
|
|||||||
|
select @@server_id; -- maxscale begin route to master:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; -- maxscale route to server server3:3002
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; -- maxscale end:
|
||||||
|
select @@server_id; -- maxscale named1 prepare route to master:
|
||||||
|
select @@server_id; -- maxscale named1 begin:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; -- maxscale route to server server3:3002
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; -- maxscale end:
|
||||||
|
select @@server_id; -- maxscale shorthand1 begin route to server server2:3001
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; -- maxscale route to server server3:3002
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; -- maxscale end:
|
||||||
|
select @@server_id; # maxscale begin route to master:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; # maxscale route to server server3:3002
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; # maxscale end:
|
||||||
|
select @@server_id; # maxscale named2 prepare route to master:
|
||||||
|
select @@server_id; # maxscale named2 begin:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; # maxscale route to server server3:3002
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; # maxscale end:
|
||||||
|
select @@server_id; # maxscale shorthand2 begin route to server server2:3001
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; # maxscale route to server server3:3002
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; # maxscale end:
|
||||||
|
select @@server_id/* maxscale begin route to master */;:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id/* maxscale route to server server3 */;:3002
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id/* maxscale end */;:
|
||||||
|
select @@server_id/* maxscale named3 prepare route to master */;:
|
||||||
|
select @@server_id/* maxscale named3 begin */;:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id/* maxscale route to server server3 */;:3002
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id/* maxscale end */;:
|
||||||
|
select @@server_id/* maxscale shorthand3 begin route to server server2 */; :3001
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id/* maxscale route to server server3 */;:3002
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id/* maxscale end */;:
|
@ -0,0 +1,39 @@
|
|||||||
|
select @@server_id; -- maxscalemaxscale route to master:
|
||||||
|
select @@server_id; -- master to route maxscale:
|
||||||
|
select @@server_id; -- route to master:
|
||||||
|
select @@server_id; -- maxscale to master:
|
||||||
|
select @@server_id; -- maxscale route master:
|
||||||
|
select @@server_id; -- maxscale route to:
|
||||||
|
select @@server_id; -- maxscale begin master:
|
||||||
|
select @@server_id; -- maxscale master route to master:
|
||||||
|
select @@server_id; -- maxscale route to maxscale route to master:
|
||||||
|
select @@server_id; -- maxscale maxscale route to master:
|
||||||
|
select @@server_id; -- maxscale route to to server =):
|
||||||
|
select @@server_id; -- maxscale route to maxscale server server1:
|
||||||
|
select @@server_id; -- maxscale route to server1:
|
||||||
|
select @@server_id; # maxscalemaxscale route to master:
|
||||||
|
select @@server_id; # master to route maxscale:
|
||||||
|
select @@server_id; # route to master:
|
||||||
|
select @@server_id; # maxscale to master:
|
||||||
|
select @@server_id; # maxscale route master:
|
||||||
|
select @@server_id; # maxscale route to:
|
||||||
|
select @@server_id; # maxscale begin master:
|
||||||
|
select @@server_id; # maxscale master route to master:
|
||||||
|
select @@server_id; # maxscale route to maxscale route to master:
|
||||||
|
select @@server_id; # maxscale maxscale route to master:
|
||||||
|
select @@server_id; # maxscale route to to server =):
|
||||||
|
select @@server_id; # maxscale route to maxscale server server1:
|
||||||
|
select @@server_id; # maxscale route to server1:
|
||||||
|
select @@server_id; /* maxscalemaxscale route to master */;:
|
||||||
|
select @@server_id; /* master to route maxscale */;:
|
||||||
|
select @@server_id; /* route to master */;:
|
||||||
|
select @@server_id; /* maxscale to master */;:
|
||||||
|
select @@server_id; /* maxscale route master */;:
|
||||||
|
select @@server_id; /* maxscale route to */;:
|
||||||
|
select @@server_id; /* maxscale begin master */;:
|
||||||
|
select @@server_id; /* maxscale master route to master */;:
|
||||||
|
select @@server_id; /* maxscale route to maxscale route to master */;:
|
||||||
|
select @@server_id; /* maxscale maxscale route to master */;:
|
||||||
|
select @@server_id; /* maxscale route to to server =) */;:
|
||||||
|
select @@server_id; /* maxscale route to maxscale server server1 */;:
|
||||||
|
select @@server_id; /* maxscale route to server1 */;:
|
@ -0,0 +1,9 @@
|
|||||||
|
-- maxscale route to master:3000
|
||||||
|
-- maxscale route to server server1:3000
|
||||||
|
-- maxscale route to server server2:3001
|
||||||
|
-- maxscale route to server server3:3002
|
||||||
|
-- maxscale route to server server4:3003
|
||||||
|
-- maxscale max_slave_replication_lag = 100:
|
||||||
|
-- maxscale max_slave_replication_lag= 100:
|
||||||
|
-- maxscale max_slave_replication_lag =100:
|
||||||
|
-- maxscale max_slave_replication_lag=100:
|
65
server/modules/routing/readwritesplit/test/test_hints/rwsplit_hints.sh
Executable file
65
server/modules/routing/readwritesplit/test/test_hints/rwsplit_hints.sh
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
NARGS=7
|
||||||
|
TLOG=$1
|
||||||
|
THOST=$2
|
||||||
|
TPORT=$3
|
||||||
|
TMASTER_ID=$4
|
||||||
|
TUSER=$5
|
||||||
|
TPWD=$6
|
||||||
|
TESTINPUT=$7
|
||||||
|
|
||||||
|
if [ $# != $NARGS ] ;
|
||||||
|
then
|
||||||
|
echo""
|
||||||
|
echo "Wrong number of arguments, gave "$#" but "$NARGS" is required"
|
||||||
|
echo ""
|
||||||
|
echo "Usage :"
|
||||||
|
echo " rwsplit_hints.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
RUNCMD=mysql\ --host=$THOST\ -P$TPORT\ -u$TUSER\ -p$TPWD\ --unbuffered=true\ --disable-reconnect\ --silent\ --comment
|
||||||
|
i=0
|
||||||
|
|
||||||
|
while read -r LINE
|
||||||
|
do
|
||||||
|
TINPUT[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[1]}'`
|
||||||
|
TRETVAL[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[2]}'`
|
||||||
|
echo "${TINPUT[i]}" >> $TESTINPUT.sql
|
||||||
|
i=$((i+1))
|
||||||
|
done < $TESTINPUT
|
||||||
|
|
||||||
|
`$RUNCMD < $TESTINPUT.sql > $TESTINPUT.output`
|
||||||
|
|
||||||
|
x=0
|
||||||
|
crash=1
|
||||||
|
all_passed=1
|
||||||
|
|
||||||
|
while read -r TOUTPUT
|
||||||
|
do
|
||||||
|
crash=0
|
||||||
|
if [ "$TOUTPUT" != "${TRETVAL[x]}" -a "${TRETVAL[x]}" != "" ]
|
||||||
|
then
|
||||||
|
all_passed=0
|
||||||
|
echo "$TESTINPUT:$((x + 1)): ${TINPUT[x]} FAILED, return value $TOUTPUT when ${TRETVAL[x]} was expected">>$TLOG;
|
||||||
|
fi
|
||||||
|
x=$((x+1))
|
||||||
|
done < $TESTINPUT.output
|
||||||
|
|
||||||
|
if [ $crash -eq 1 ]
|
||||||
|
then
|
||||||
|
all_passed=0
|
||||||
|
for ((v=0;v<$i;v++))
|
||||||
|
do
|
||||||
|
echo "${TINPUT[v]} FAILED, nothing was returned">>$TLOG;
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $all_passed -eq 1 ]
|
||||||
|
then
|
||||||
|
echo "Test set: PASSED">>$TLOG;
|
||||||
|
else
|
||||||
|
echo "Test set: FAILED">>$TLOG;
|
||||||
|
fi
|
@ -0,0 +1,18 @@
|
|||||||
|
select @@server_id; -- maxscale route to master:3000
|
||||||
|
select @@server_id; -- maxscale route to slave:
|
||||||
|
select @@server_id; -- maxscale route to server server1:3000
|
||||||
|
select @@server_id; -- maxscale route to server server2:3001
|
||||||
|
select @@server_id; -- maxscale route to server server3:3002
|
||||||
|
select @@server_id; -- maxscale route to server server4:3003
|
||||||
|
select @@server_id; # maxscale route to master:3000
|
||||||
|
select @@server_id; # maxscale route to slave:
|
||||||
|
select @@server_id; # maxscale route to server server1:3000
|
||||||
|
select @@server_id; # maxscale route to server server2:3001
|
||||||
|
select @@server_id; # maxscale route to server server3:3002
|
||||||
|
select @@server_id; # maxscale route to server server4:3003
|
||||||
|
select @@server_id/* maxscale route to master */;:3000
|
||||||
|
select @@server_id/* maxscale route to slave */;:
|
||||||
|
select @@server_id/* maxscale route to server server1 */;:3000
|
||||||
|
select @@server_id/* maxscale route to server server2 */;:3001
|
||||||
|
select @@server_id/* maxscale route to server server3 */;:3002
|
||||||
|
select @@server_id/* maxscale route to server server4 */;:3003
|
@ -0,0 +1,50 @@
|
|||||||
|
select @@server_id; -- maxscale stack_named1 prepare route to server server1:
|
||||||
|
select @@server_id; -- maxscale stack_named2 prepare route to server server2:
|
||||||
|
select @@server_id; -- maxscale stack_named3 prepare route to server server3:
|
||||||
|
select @@server_id; -- maxscale stack_named4 prepare route to server server4:
|
||||||
|
select @@server_id; -- maxscale stack_named1 begin:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; -- maxscale stack_named2 begin:3001
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; -- maxscale stack_named3 begin:3002
|
||||||
|
select @@server_id;:3002
|
||||||
|
select @@server_id; -- maxscale stack_named4 begin:3003
|
||||||
|
select @@server_id;:3003
|
||||||
|
select @@server_id; -- maxscale stack_shorthand1 begin route to server server1:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; -- maxscale stack_shorthand2 begin route to server server2:3001
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; -- maxscale stack_shorthand3 begin route to server server3:3002
|
||||||
|
select @@server_id;:3002
|
||||||
|
select @@server_id; -- maxscale stack_shorthand4 begin route to server server4:3003
|
||||||
|
select @@server_id;:3003
|
||||||
|
select @@server_id; -- maxscale end:3002
|
||||||
|
select @@server_id;:3002
|
||||||
|
select @@server_id; -- maxscale end:3001
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; -- maxscale end:3000
|
||||||
|
select @@server_id;:3000
|
||||||
|
select @@server_id; -- maxscale end:3003
|
||||||
|
select @@server_id;:3003
|
||||||
|
select @@server_id; -- maxscale end:3002
|
||||||
|
select @@server_id;:3002
|
||||||
|
select @@server_id; -- maxscale end:3001
|
||||||
|
select @@server_id;:3001
|
||||||
|
select @@server_id; -- maxscale end:3000
|
||||||
|
select @@server_id; -- maxscale end:
|
||||||
|
select @@server_id; -- maxscale stack_shorthand1 begin:3000
|
||||||
|
select @@server_id; -- maxscale stack_shorthand2 begin:3001
|
||||||
|
select @@server_id; -- maxscale stack_shorthand3 begin:3002
|
||||||
|
select @@server_id; -- maxscale stack_shorthand4 begin:3003
|
||||||
|
select @@server_id; -- maxscale stack_named1 begin:3000
|
||||||
|
select @@server_id; -- maxscale stack_named2 begin:3001
|
||||||
|
select @@server_id; -- maxscale stack_named3 begin:3002
|
||||||
|
select @@server_id; -- maxscale stack_named4 begin:3003
|
||||||
|
select @@server_id; -- maxscale end:3002
|
||||||
|
select @@server_id; -- maxscale end:3001
|
||||||
|
select @@server_id; -- maxscale end:3000
|
||||||
|
select @@server_id; -- maxscale end:3003
|
||||||
|
select @@server_id; -- maxscale end:3002
|
||||||
|
select @@server_id; -- maxscale end:3001
|
||||||
|
select @@server_id; -- maxscale end:3000
|
||||||
|
select @@server_id; -- maxscale end:
|
33
server/modules/routing/readwritesplit/test/test_hints/syntax_check.sh
Executable file
33
server/modules/routing/readwritesplit/test/test_hints/syntax_check.sh
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
NARGS=7
|
||||||
|
TLOG=$1
|
||||||
|
THOST=$2
|
||||||
|
TPORT=$3
|
||||||
|
TMASTER_ID=$4
|
||||||
|
TUSER=$5
|
||||||
|
TPWD=$6
|
||||||
|
TESTINPUT=$7
|
||||||
|
|
||||||
|
if [ $# != $NARGS ] ;
|
||||||
|
then
|
||||||
|
echo""
|
||||||
|
echo "Wrong number of arguments, gave "$#" but "$NARGS" is required"
|
||||||
|
echo ""
|
||||||
|
echo "Usage :"
|
||||||
|
echo " syntax_check.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
./rwsplit_hints.sh dummy.log $THOST $TPORT $TMASTER_ID $TUSER $TPWD $TESTINPUT
|
||||||
|
|
||||||
|
exp_count=`cat error_tests|wc -l`
|
||||||
|
err_count=`tac ../../../../../test/log/skygw_err* | gawk '/enabled/{if(!bg){ bg = 1} else exit 0}{if(bg) print}'|grep -c 'Hint ignored'`
|
||||||
|
|
||||||
|
if [[ $err_count -ge $exp_count ]]
|
||||||
|
then
|
||||||
|
echo "Test set: PASSED">>$TLOG;
|
||||||
|
else
|
||||||
|
echo "Expected $exp_count ignored hints in the error log but found $err_count instead">>$TLOG
|
||||||
|
echo "Test set: FAILED">>$TLOG;
|
||||||
|
fi
|
616
server/modules/routing/webserver.c
Normal file
616
server/modules/routing/webserver.c
Normal file
@ -0,0 +1,616 @@
|
|||||||
|
/*
|
||||||
|
* This file is distributed as part of MaxScale. 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 2014
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <router.h>
|
||||||
|
#include <modinfo.h>
|
||||||
|
#include <server.h>
|
||||||
|
#include <service.h>
|
||||||
|
#include <session.h>
|
||||||
|
#include <monitor.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance structure for this router.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
SERVICE *service;
|
||||||
|
} WEB_INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The session structure for this router.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
SESSION *session;
|
||||||
|
} WEB_SESSION;
|
||||||
|
|
||||||
|
static char *version_str = "V1.0.0";
|
||||||
|
|
||||||
|
MODULE_INFO info = {
|
||||||
|
MODULE_API_ROUTER,
|
||||||
|
MODULE_IN_DEVELOPMENT,
|
||||||
|
ROUTER_VERSION,
|
||||||
|
"A test router - not for use in real systems"
|
||||||
|
};
|
||||||
|
|
||||||
|
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||||
|
static void *newSession(ROUTER *instance, SESSION *session);
|
||||||
|
static void closeSession(ROUTER *instance, void *session);
|
||||||
|
static void freeSession(ROUTER *instance, void *session);
|
||||||
|
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
|
||||||
|
static void diagnostic(ROUTER *instance, DCB *dcb);
|
||||||
|
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
|
||||||
|
|
||||||
|
|
||||||
|
static ROUTER_OBJECT MyObject = {
|
||||||
|
createInstance,
|
||||||
|
newSession,
|
||||||
|
closeSession,
|
||||||
|
freeSession,
|
||||||
|
routeQuery,
|
||||||
|
diagnostic,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
getCapabilities
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void send_index(WEB_SESSION *);
|
||||||
|
static void send_css(WEB_SESSION *);
|
||||||
|
static void send_menu(WEB_SESSION *);
|
||||||
|
static void send_blank(WEB_SESSION *);
|
||||||
|
static void send_title(WEB_SESSION *);
|
||||||
|
static void send_frame1(WEB_SESSION *);
|
||||||
|
static void send_services(WEB_SESSION *);
|
||||||
|
static void send_sessions(WEB_SESSION *);
|
||||||
|
static void send_servers(WEB_SESSION *);
|
||||||
|
static void send_monitors(WEB_SESSION *);
|
||||||
|
static void respond_error(WEB_SESSION *, int, char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of URL to function that implements the URL
|
||||||
|
*/
|
||||||
|
static struct {
|
||||||
|
char *page; /* URL */
|
||||||
|
void (*fcn)(WEB_SESSION *); /* Function to call */
|
||||||
|
} pages[] = {
|
||||||
|
{ "index.html", send_index },
|
||||||
|
{ "services.html", send_services },
|
||||||
|
{ "menu.html", send_menu },
|
||||||
|
{ "sessions.html", send_sessions },
|
||||||
|
{ "blank.html", send_blank },
|
||||||
|
{ "title.html", send_title },
|
||||||
|
{ "frame1.html", send_frame1 },
|
||||||
|
{ "servers.html", send_servers },
|
||||||
|
{ "monitors.html", send_monitors },
|
||||||
|
{ "styles.css", send_css },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the mandatory version entry point
|
||||||
|
*
|
||||||
|
* @return version string of the module
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
version()
|
||||||
|
{
|
||||||
|
return version_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module initialisation routine, called when the module
|
||||||
|
* is first loaded.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ModuleInit()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module entry point routine. It is this routine that
|
||||||
|
* must populate the structure that is referred to as the
|
||||||
|
* "module object", this is a structure with the set of
|
||||||
|
* external entry points for this module.
|
||||||
|
*
|
||||||
|
* @return The module object
|
||||||
|
*/
|
||||||
|
ROUTER_OBJECT *
|
||||||
|
GetModuleObject()
|
||||||
|
{
|
||||||
|
return &MyObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the router for a particular service
|
||||||
|
* within the gateway.
|
||||||
|
*
|
||||||
|
* @param service The service this router is being create for
|
||||||
|
* @param options The options for this query router
|
||||||
|
*
|
||||||
|
* @return The instance data for this new instance
|
||||||
|
*/
|
||||||
|
static ROUTER *
|
||||||
|
createInstance(SERVICE *service, char **options)
|
||||||
|
{
|
||||||
|
WEB_INSTANCE *inst;
|
||||||
|
|
||||||
|
if ((inst = (WEB_INSTANCE *)malloc(sizeof(WEB_INSTANCE))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
inst->service = service;
|
||||||
|
return (ROUTER *)inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a new session with this instance of the router.
|
||||||
|
*
|
||||||
|
* @param instance The router instance data
|
||||||
|
* @param session The session itself
|
||||||
|
* @return Session specific data for this session
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
newSession(ROUTER *instance, SESSION *session)
|
||||||
|
{
|
||||||
|
WEB_SESSION *wsession;
|
||||||
|
|
||||||
|
if ((wsession = (WEB_SESSION *)malloc(sizeof(WEB_SESSION))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
wsession->session = session;
|
||||||
|
return wsession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a session with the router, this is the mechanism
|
||||||
|
* by which a router may cleanup data structure etc.
|
||||||
|
*
|
||||||
|
* @param instance The router instance data
|
||||||
|
* @param session The session being closed
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
closeSession(ROUTER *instance, void *session)
|
||||||
|
{
|
||||||
|
free(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeSession(
|
||||||
|
ROUTER* router_instance,
|
||||||
|
void* router_client_session)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
routeQuery(ROUTER *instance, void *session, GWBUF *queue)
|
||||||
|
{
|
||||||
|
WEB_SESSION *wsession = (WEB_SESSION *)session;
|
||||||
|
char *ptr;
|
||||||
|
int i, found = 0;
|
||||||
|
char *url;
|
||||||
|
|
||||||
|
if ((url = gwbuf_get_property(queue, "URL")) == NULL)
|
||||||
|
{
|
||||||
|
respond_error(wsession, 404, "No URL available");
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = strrchr(url, '/');
|
||||||
|
if (ptr)
|
||||||
|
ptr++;
|
||||||
|
else
|
||||||
|
ptr = url;
|
||||||
|
for (i = 0; pages[i].page; i++)
|
||||||
|
{
|
||||||
|
if (!strcmp(ptr, pages[i].page))
|
||||||
|
{
|
||||||
|
(pages[i].fcn)(wsession);
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
respond_error(wsession, 404, "Unrecognised URL received");
|
||||||
|
gwbuf_free(queue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diagnostics routine
|
||||||
|
*
|
||||||
|
* @param instance The router instance
|
||||||
|
* @param dcb The DCB for diagnostic output
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
diagnostic(ROUTER *instance, DCB *dcb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the router capabilities bitmask
|
||||||
|
*
|
||||||
|
* @param inst The router instance
|
||||||
|
* @param router_session The router session
|
||||||
|
* @return Router capabilities bitmask
|
||||||
|
*/
|
||||||
|
static uint8_t
|
||||||
|
getCapabilities(ROUTER *inst, void *router_session)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HTML of the index page.
|
||||||
|
*/
|
||||||
|
static char *index_page =
|
||||||
|
"<HTML><HEAD>"
|
||||||
|
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
|
||||||
|
"<TITLE>MaxScale</TITLE>"
|
||||||
|
"</HEAD>"
|
||||||
|
"<FRAMESET ROWS=\"60,*\">"
|
||||||
|
"<FRAME SRC=\"title.html\">"
|
||||||
|
"<FRAME SRC=\"frame1.html\">"
|
||||||
|
"</FRAMESET>"
|
||||||
|
"</HTML>";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HTML of the title page
|
||||||
|
*/
|
||||||
|
static char *title_page =
|
||||||
|
"<HTML><HEAD>"
|
||||||
|
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
|
||||||
|
"<TITLE>MaxScale</TITLE>"
|
||||||
|
"</HEAD><BODY>"
|
||||||
|
"<H1>MaxScale - Status View</H1>"
|
||||||
|
"</BODY></HTML>";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML of the main frames, those below the title frame
|
||||||
|
*/
|
||||||
|
static char *frame1_page =
|
||||||
|
"<HTML>"
|
||||||
|
"<FRAMESET COLS=\"20%,80%\">"
|
||||||
|
"<FRAME SRC=\"menu.html\">"
|
||||||
|
"<FRAME SRC=\"blank.html\" NAME=\"darea\">"
|
||||||
|
"</FRAMESET>"
|
||||||
|
"</HTML>";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The menu page HTML
|
||||||
|
*/
|
||||||
|
static char *menu_page =
|
||||||
|
"<HTML><HEAD>"
|
||||||
|
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
|
||||||
|
"</HEAD><BODY>"
|
||||||
|
"<H2>Options</H2><P>"
|
||||||
|
"<UL><LI><A HREF=\"monitors.html\" target=\"darea\">Monitors</A>"
|
||||||
|
"<LI><A HREF=\"services.html\" target=\"darea\">Services</A>"
|
||||||
|
"<LI><A HREF=\"servers.html\" target=\"darea\">Servers</A>"
|
||||||
|
"<LI><A HREF=\"sessions.html\" target=\"darea\">Sessions</A>"
|
||||||
|
"</UL></BODY></HTML>";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A blank page, contents of the display area when we first connect
|
||||||
|
*/
|
||||||
|
static char *blank_page = "<HTML><BODY> </BODY></HTML>";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CSS used for every "page"
|
||||||
|
*/
|
||||||
|
static char *css =
|
||||||
|
"table, td, th { border: 1px solid blue; }\n"
|
||||||
|
"th { background-color: blue; color: white; padding: 5px }\n"
|
||||||
|
"td { padding: 5px; }\n"
|
||||||
|
"table { border-collapse: collapse; }\n"
|
||||||
|
"a:link { color: #0000FF; }\n"
|
||||||
|
"a:visted { color: #0000FF; }\n"
|
||||||
|
"a:hover { color: #FF0000; }\n"
|
||||||
|
"a:active { color: #0000FF; }\n"
|
||||||
|
"h1 { color: blue; font-family: serif }\n"
|
||||||
|
"h2 { color: blue; font-family: serif }\n"
|
||||||
|
"p { font-family: serif }\n"
|
||||||
|
"li { font-family: serif }\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the standard HTTP headers for an HTML file
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_html_header(DCB *dcb)
|
||||||
|
{
|
||||||
|
char date[64] = "";
|
||||||
|
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT";
|
||||||
|
|
||||||
|
time_t httpd_current_time = time(NULL);
|
||||||
|
|
||||||
|
strftime(date, sizeof(date), fmt, localtime(&httpd_current_time));
|
||||||
|
|
||||||
|
dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/html\r\n", date, "MaxScale");
|
||||||
|
|
||||||
|
dcb_printf(dcb, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a static HTML page
|
||||||
|
*
|
||||||
|
* @param dcb The DCB of the connection to the browser
|
||||||
|
* @param html The HTML to send
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_static_html(DCB *dcb, char *html)
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, html);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the index page
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_index(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
send_static_html(dcb, index_page);
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the CSS
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_css(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
send_static_html(dcb, css);
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the title page
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_title(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
send_static_html(dcb, title_page);
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the frame1 page
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_frame1(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
send_static_html(dcb, frame1_page);
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the menu page
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_menu(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
send_static_html(dcb, menu_page);
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a blank page
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_blank(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
send_static_html(dcb, blank_page);
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a table row for a service. This is called using the service
|
||||||
|
* iterator function
|
||||||
|
*
|
||||||
|
* @param service The service to display
|
||||||
|
* @param dcb The DCB to print the HTML to
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
service_row(SERVICE *service, DCB *dcb)
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%d</TD></TR>\n",
|
||||||
|
service->name, service->routerModule,
|
||||||
|
service->stats.n_current, service->stats.n_sessions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the services page. This produces a table by means of the
|
||||||
|
* serviceIterate call.
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_services(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
dcb_printf(dcb, "<HTML><HEAD>");
|
||||||
|
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||||
|
dcb_printf(dcb, "<BODY><H2>Services</H2><P>");
|
||||||
|
dcb_printf(dcb, "<TABLE><TR><TH>Name</TH><TH>Router</TH><TH>");
|
||||||
|
dcb_printf(dcb, "Current Sessions</TH><TH>Total Sessions</TH></TR>\n");
|
||||||
|
serviceIterate(service_row, dcb);
|
||||||
|
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a session row for a session. this is called using the session
|
||||||
|
* iterator function
|
||||||
|
*
|
||||||
|
* @param session The session to display
|
||||||
|
* @param dcb The DCB to send the HTML to
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
session_row(SESSION *session, DCB *dcb)
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, "<TR><TD>%-16p</TD><TD>%s</TD><TD>%s</TD><TD>%s</TD></TR>\n",
|
||||||
|
session, ((session->client && session->client->remote)
|
||||||
|
? session->client->remote : ""),
|
||||||
|
(session->service && session->service->name
|
||||||
|
? session->service->name : ""),
|
||||||
|
session_state(session->state));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the sessions page. The produces a table of all the current sessions
|
||||||
|
* display. It makes use of the sessionIterate call to call the function
|
||||||
|
* session_row() with each session.
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_sessions(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
dcb_printf(dcb, "<HTML><HEAD>");
|
||||||
|
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||||
|
dcb_printf(dcb, "<BODY><H2>Sessions</H2><P>");
|
||||||
|
dcb_printf(dcb, "<TABLE><TR><TH>Session</TH><TH>Client</TH><TH>");
|
||||||
|
dcb_printf(dcb, "Service</TH><TH>State</TH></TR>\n");
|
||||||
|
sessionIterate(session_row, dcb);
|
||||||
|
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a table row for a particular server. This is called via the
|
||||||
|
* serverIterate call in send_servers.
|
||||||
|
*
|
||||||
|
* @param server The server to print
|
||||||
|
* @param dcb The DCB to send the HTML to
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
server_row(SERVER *server, DCB *dcb)
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%s</TD><TD>%d</TD></TR>\n",
|
||||||
|
server->unique_name, server->name, server->port,
|
||||||
|
server_status(server), server->stats.n_current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the servers page
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_servers(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
dcb_printf(dcb, "<HTML><HEAD>");
|
||||||
|
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||||
|
dcb_printf(dcb, "<BODY><H2>Servers</H2><P>");
|
||||||
|
dcb_printf(dcb, "<TABLE><TR><TH>Server</TH><TH>Address</TH><TH>");
|
||||||
|
dcb_printf(dcb, "Port</TH><TH>State</TH><TH>Connections</TH></TR>\n");
|
||||||
|
serverIterate(server_row, dcb);
|
||||||
|
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a table row for the monitors table
|
||||||
|
*
|
||||||
|
* @param monitor The monitor to print
|
||||||
|
* @param dcb The DCB to print to
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
monitor_row(MONITOR *monitor, DCB *dcb)
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD></TR>\n",
|
||||||
|
monitor->name, monitor->state & MONITOR_STATE_RUNNING
|
||||||
|
? "Running" : "Stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the monitors page. This iterates on all the monitors and send
|
||||||
|
* the rows via the monitor_monitor.
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
send_monitors(WEB_SESSION *session)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
send_html_header(dcb);
|
||||||
|
dcb_printf(dcb, "<HTML><HEAD>");
|
||||||
|
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||||
|
dcb_printf(dcb, "<BODY><H2>Monitors</H2><P>");
|
||||||
|
dcb_printf(dcb, "<TABLE><TR><TH>Monitor</TH><TH>State</TH></TR>\n");
|
||||||
|
monitorIterate(monitor_row, dcb);
|
||||||
|
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond with an HTTP error
|
||||||
|
*
|
||||||
|
* @param session The router session
|
||||||
|
* @param err The HTTP error code to send
|
||||||
|
* @param msg The message to print
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
respond_error(WEB_SESSION *session, int err, char *msg)
|
||||||
|
{
|
||||||
|
DCB *dcb = session->session->client;
|
||||||
|
|
||||||
|
dcb_printf(dcb, "HTTP/1.1 %d %s\n", err, msg);
|
||||||
|
dcb_printf(dcb, "Content-Type: text/html\n");
|
||||||
|
dcb_printf(dcb, "\n");
|
||||||
|
dcb_printf(dcb, "<HTML><BODY>\n");
|
||||||
|
dcb_printf(dcb, "MaxScale webserver plugin unable to satisfy request.\n");
|
||||||
|
dcb_printf(dcb, "<P>Code: %d, %s\n", err, msg);
|
||||||
|
dcb_printf(dcb, "</BODY></HTML>");
|
||||||
|
dcb_close(dcb);
|
||||||
|
}
|
@ -20,11 +20,13 @@ threads=1
|
|||||||
# user =<user name - must have slave replication and
|
# user =<user name - must have slave replication and
|
||||||
# slave client privileges>
|
# slave client privileges>
|
||||||
# passwd=<password of the above user, plain text currently>
|
# passwd=<password of the above user, plain text currently>
|
||||||
|
# monitor_interval=<sampling interval in milliseconds,
|
||||||
|
# default value is 10000>
|
||||||
|
|
||||||
[MySQL Monitor]
|
[MySQL Monitor]
|
||||||
type=monitor
|
type=monitor
|
||||||
module=mysqlmon
|
module=mysqlmon
|
||||||
servers=server1,server2,server3
|
servers=server1,server2,server3,server4
|
||||||
user=maxuser
|
user=maxuser
|
||||||
passwd=maxpwd
|
passwd=maxpwd
|
||||||
|
|
||||||
@ -36,25 +38,42 @@ passwd=maxpwd
|
|||||||
# servers=<server name>,<server name>,...
|
# servers=<server name>,<server name>,...
|
||||||
# user=<User to fetch password inforamtion with>
|
# user=<User to fetch password inforamtion with>
|
||||||
# passwd=<Password of the user, plain text currently>
|
# passwd=<Password of the user, plain text currently>
|
||||||
|
# enable_root_user=<0 or 1, default is 0>
|
||||||
|
# version_string=<specific string for server handshake,
|
||||||
|
# default is the MariaDB embedded library version>
|
||||||
#
|
#
|
||||||
# Valid router modules currently are:
|
# Valid router modules currently are:
|
||||||
# readwritesplit, readconnroute and debugcli
|
# readwritesplit, readconnroute and debugcli
|
||||||
|
|
||||||
|
|
||||||
[RW Split Router]
|
[RW Split Router]
|
||||||
type=service
|
type=service
|
||||||
router=readwritesplit
|
router=readwritesplit
|
||||||
servers=server1,server2,server3
|
servers=server1,server2,server3,server4
|
||||||
|
max_slave_connections=90%
|
||||||
user=maxuser
|
user=maxuser
|
||||||
passwd=maxpwd
|
passwd=maxpwd
|
||||||
|
|
||||||
|
|
||||||
|
[RW Split Hint Router]
|
||||||
|
type=service
|
||||||
|
router=readwritesplit
|
||||||
|
servers=server1,server2,server3,server4
|
||||||
|
max_slave_connections=90%
|
||||||
|
user=maxuser
|
||||||
|
passwd=maxpwd
|
||||||
|
filters=Hint
|
||||||
|
|
||||||
|
|
||||||
[Read Connection Router]
|
[Read Connection Router]
|
||||||
type=service
|
type=service
|
||||||
router=readconnroute
|
router=readconnroute
|
||||||
router_options=slave
|
router_options=master
|
||||||
servers=server1,server2,server3
|
servers=server1
|
||||||
user=maxuser
|
user=maxuser
|
||||||
passwd=maxpwd
|
passwd=maxpwd
|
||||||
|
|
||||||
|
|
||||||
[HTTPD Router]
|
[HTTPD Router]
|
||||||
type=service
|
type=service
|
||||||
router=testroute
|
router=testroute
|
||||||
@ -64,6 +83,12 @@ servers=server1,server2,server3
|
|||||||
type=service
|
type=service
|
||||||
router=debugcli
|
router=debugcli
|
||||||
|
|
||||||
|
|
||||||
|
[Hint]
|
||||||
|
type=filter
|
||||||
|
module=hintfilter
|
||||||
|
|
||||||
|
|
||||||
# Listener definitions for the services
|
# Listener definitions for the services
|
||||||
#
|
#
|
||||||
# Valid options are:
|
# Valid options are:
|
||||||
@ -71,6 +96,8 @@ router=debugcli
|
|||||||
# service=<name of service defined elsewhere>
|
# service=<name of service defined elsewhere>
|
||||||
# protocol=<name of protocol module with which to listen>
|
# protocol=<name of protocol module with which to listen>
|
||||||
# port=<Listening port>
|
# port=<Listening port>
|
||||||
|
# address=<Address to bind to>
|
||||||
|
# socket=<Listening socket>
|
||||||
|
|
||||||
[RW Split Listener]
|
[RW Split Listener]
|
||||||
type=listener
|
type=listener
|
||||||
@ -78,17 +105,25 @@ service=RW Split Router
|
|||||||
protocol=MySQLClient
|
protocol=MySQLClient
|
||||||
port=4006
|
port=4006
|
||||||
|
|
||||||
|
[RW Split Hint Listener]
|
||||||
|
type=listener
|
||||||
|
service=RW Split Hint Router
|
||||||
|
protocol=MySQLClient
|
||||||
|
port=4009
|
||||||
|
|
||||||
[Read Connection Listener]
|
[Read Connection Listener]
|
||||||
type=listener
|
type=listener
|
||||||
service=Read Connection Router
|
service=Read Connection Router
|
||||||
protocol=MySQLClient
|
protocol=MySQLClient
|
||||||
port=4008
|
port=4008
|
||||||
|
#socket=/tmp/readconn.sock
|
||||||
|
|
||||||
[Debug Listener]
|
[Debug Listener]
|
||||||
type=listener
|
type=listener
|
||||||
service=Debug Interface
|
service=Debug Interface
|
||||||
protocol=telnetd
|
protocol=telnetd
|
||||||
port=4442
|
port=4442
|
||||||
|
#address=127.0.0.1
|
||||||
|
|
||||||
[HTTPD Listener]
|
[HTTPD Listener]
|
||||||
type=listener
|
type=listener
|
||||||
@ -115,3 +150,9 @@ type=server
|
|||||||
address=127.0.0.1
|
address=127.0.0.1
|
||||||
port=3002
|
port=3002
|
||||||
protocol=MySQLBackend
|
protocol=MySQLBackend
|
||||||
|
|
||||||
|
[server4]
|
||||||
|
type=server
|
||||||
|
address=127.0.0.1
|
||||||
|
port=3003
|
||||||
|
protocol=MySQLBackend
|
||||||
|
5
test.inc
5
test.inc
@ -19,6 +19,11 @@ TPORT_RCONN :=
|
|||||||
#
|
#
|
||||||
TPORT_RW :=
|
TPORT_RW :=
|
||||||
#
|
#
|
||||||
|
# port of read/write split router module with hints, for example:
|
||||||
|
# TPORT_RW_HINT := 4009
|
||||||
|
#
|
||||||
|
TPORT_RW_HINT :=
|
||||||
|
#
|
||||||
# username of MaxScale user, for example:
|
# username of MaxScale user, for example:
|
||||||
# TUSER := maxuser
|
# TUSER := maxuser
|
||||||
#
|
#
|
||||||
|
@ -134,7 +134,8 @@ typedef enum skygw_chk_t {
|
|||||||
((t) == QUERY_TYPE_SESSION_WRITE ? "QUERY_TYPE_SESSION_WRITE" : \
|
((t) == QUERY_TYPE_SESSION_WRITE ? "QUERY_TYPE_SESSION_WRITE" : \
|
||||||
((t) == QUERY_TYPE_UNKNOWN ? "QUERY_TYPE_UNKNOWN" : \
|
((t) == QUERY_TYPE_UNKNOWN ? "QUERY_TYPE_UNKNOWN" : \
|
||||||
((t) == QUERY_TYPE_LOCAL_READ ? "QUERY_TYPE_LOCAL_READ" : \
|
((t) == QUERY_TYPE_LOCAL_READ ? "QUERY_TYPE_LOCAL_READ" : \
|
||||||
"Unknown query type")))))
|
((t) == QUERY_TYPE_EXEC_STMT ? "QUERY_TYPE_EXEC_STMT" : \
|
||||||
|
"Unknown query type"))))))
|
||||||
|
|
||||||
#define STRLOGID(i) ((i) == LOGFILE_TRACE ? "LOGFILE_TRACE" : \
|
#define STRLOGID(i) ((i) == LOGFILE_TRACE ? "LOGFILE_TRACE" : \
|
||||||
((i) == LOGFILE_MESSAGE ? "LOGFILE_MESSAGE" : \
|
((i) == LOGFILE_MESSAGE ? "LOGFILE_MESSAGE" : \
|
||||||
@ -232,11 +233,13 @@ typedef enum skygw_chk_t {
|
|||||||
((c) == LEAST_BEHIND_MASTER ? "LEAST_BEHIND_MASTER" : \
|
((c) == LEAST_BEHIND_MASTER ? "LEAST_BEHIND_MASTER" : \
|
||||||
((c) == LEAST_CURRENT_OPERATIONS ? "LEAST_CURRENT_OPERATIONS" : "Unknown criteria")))))
|
((c) == LEAST_CURRENT_OPERATIONS ? "LEAST_CURRENT_OPERATIONS" : "Unknown criteria")))))
|
||||||
|
|
||||||
#define STRSRVSTATUS(s) ((SERVER_IS_RUNNING(s) && SERVER_IS_MASTER(s)) ? "RUNNING MASTER" : \
|
#define STRSRVSTATUS(s) (SERVER_IS_MASTER(s) ? "RUNNING MASTER" : \
|
||||||
((SERVER_IS_RUNNING(s) && SERVER_IS_SLAVE(s)) ? "RUNNING SLAVE" : \
|
(SERVER_IS_SLAVE(s) ? "RUNNING SLAVE" : \
|
||||||
((SERVER_IS_RUNNING(s) && SERVER_IS_JOINED(s)) ? "RUNNING JOINED" : \
|
(SERVER_IS_JOINED(s) ? "RUNNING JOINED" : \
|
||||||
|
(SERVER_IS_NDB(s) ? "RUNNING NDB" : \
|
||||||
((SERVER_IS_RUNNING(s) && SERVER_IN_MAINT(s)) ? "RUNNING MAINTENANCE" : \
|
((SERVER_IS_RUNNING(s) && SERVER_IN_MAINT(s)) ? "RUNNING MAINTENANCE" : \
|
||||||
(SERVER_IS_RUNNING(s) ? "RUNNING (only)" : "NO STATUS")))))
|
(SERVER_IS_RELAY_SERVER(s) ? "RUNNING RELAY" : \
|
||||||
|
(SERVER_IS_RUNNING(s) ? "RUNNING (only)" : "NO STATUS")))))))
|
||||||
|
|
||||||
#define CHK_MLIST(l) { \
|
#define CHK_MLIST(l) { \
|
||||||
ss_info_dassert((l->mlist_chk_top == CHK_NUM_MLIST && \
|
ss_info_dassert((l->mlist_chk_top == CHK_NUM_MLIST && \
|
||||||
|
Reference in New Issue
Block a user