Z3 merge
This commit is contained in:
1
Makefile
1
Makefile
@ -48,6 +48,7 @@ clean:
|
|||||||
(cd client; touch depend.mk; make clean)
|
(cd client; touch depend.mk; make clean)
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
|
echo '#define MAXSCALE_VERSION "'`cat $(ROOT_PATH)/VERSION`'"' > $(ROOT_PATH)/server/include/version.h
|
||||||
(cd log_manager; make depend)
|
(cd log_manager; make depend)
|
||||||
(cd query_classifier; make depend)
|
(cd query_classifier; make depend)
|
||||||
(cd server; make depend)
|
(cd server; make depend)
|
||||||
|
2
README
2
README
@ -182,7 +182,7 @@ on localhost:
|
|||||||
|
|
||||||
* a master on port 3000, with server_id=2
|
* a master on port 3000, with server_id=2
|
||||||
* a slave on port 3001, server_id doesn't matter
|
* a slave on port 3001, server_id doesn't matter
|
||||||
* a slave on port 2002, server_id doesn't matter
|
* a slave on port 3002, server_id doesn't matter
|
||||||
|
|
||||||
On the master full privileges on the databases "test" and "FOO"
|
On the master full privileges on the databases "test" and "FOO"
|
||||||
are needed, on the saves SELECT permissions on test.* should
|
are needed, on the saves SELECT permissions on test.* should
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#
|
#
|
||||||
# Set debug flags
|
# Set debug flags
|
||||||
#
|
#
|
||||||
DEBUG :=
|
DEBUG := ${MAXSCALE_DEBUG}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Set build env
|
# Set build env
|
||||||
@ -22,7 +22,7 @@ UNIX := Y
|
|||||||
#
|
#
|
||||||
# Set MaxScale branch directory
|
# Set MaxScale branch directory
|
||||||
#
|
#
|
||||||
ROOT_PATH := $(HOME)/src/bazaar/tmp/maxscale
|
ROOT_PATH := $(HOME)/${MAXSCALE_SOURCE}
|
||||||
|
|
||||||
INC_PATH := $(HOME)/usr/include
|
INC_PATH := $(HOME)/usr/include
|
||||||
#
|
#
|
||||||
@ -38,7 +38,7 @@ MYSQL_HEADERS := -I$(INC_PATH) -I$(MYSQL_ROOT)/ -I$(MYSQL_ROOT)/private/ -I$(MYS
|
|||||||
#
|
#
|
||||||
# Set DYNLIB=Y if you want to link MaxScale with dynamic embedded lib
|
# Set DYNLIB=Y if you want to link MaxScale with dynamic embedded lib
|
||||||
#
|
#
|
||||||
DYNLIB :=
|
DYNLIB := ${MAXSCALE_DYNLIB}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Set path to Embedded MySQL Server
|
# Set path to Embedded MySQL Server
|
||||||
@ -51,3 +51,4 @@ endif
|
|||||||
# Set path to MySQL errors file
|
# Set path to MySQL errors file
|
||||||
#
|
#
|
||||||
ERRMSG := $(HOME)/usr/share/mysql
|
ERRMSG := $(HOME)/usr/share/mysql
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
# client program
|
# client program
|
||||||
# 18/06/14 Mark Riddoch Addition of conditional for histedit
|
# 18/06/14 Mark Riddoch Addition of conditional for histedit
|
||||||
|
|
||||||
|
include ../build_gateway.inc
|
||||||
|
include ../makefile.inc
|
||||||
|
|
||||||
ifeq ($(wildcard /usr/include/histedit.h), )
|
ifeq ($(wildcard /usr/include/histedit.h), )
|
||||||
HISTLIB=
|
HISTLIB=
|
||||||
HISTFLAG=
|
HISTFLAG=
|
||||||
@ -63,13 +66,14 @@ maxadmin: $(OBJ)
|
|||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) maxadmin
|
$(DEL) $(OBJ) maxadmin
|
||||||
|
$(DEL) *.so
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
ctags $(SRCS) $(HDRS)
|
ctags $(SRCS) $(HDRS)
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@rm -f depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
|
|
||||||
install: maxadmin
|
install: maxadmin
|
||||||
|
169
gcov.diff
169
gcov.diff
@ -1,169 +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 cb0250d..fe0f579 100644
|
|
||||||
--- a/server/core/Makefile
|
|
||||||
+++ b/server/core/Makefile
|
|
||||||
@@ -46,7 +46,7 @@ CC=cc
|
|
||||||
CFLAGS=-c -I/usr/include -I../include -I../modules/include -I../inih \
|
|
||||||
$(MYSQL_HEADERS) \
|
|
||||||
-I$(LOGPATH) -I$(UTILSPATH) \
|
|
||||||
- -Wall -g
|
|
||||||
+ -Wall -g -fprofile-arcs -ftest-coverage
|
|
||||||
|
|
||||||
include ../../makefile.inc
|
|
||||||
|
|
||||||
@@ -76,7 +76,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 f8cf910..ca022b8 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 libhintfilter.so
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/server/modules/filter/hint/Makefile b/server/modules/filter/hint/Makefile
|
|
||||||
index 4f21947..3ad0e3b 100644
|
|
||||||
--- a/server/modules/filter/hint/Makefile
|
|
||||||
+++ b/server/modules/filter/hint/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
|
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
|
||||||
|
|
||||||
SRCS= hintfilter.c hintparser.c
|
|
||||||
OBJ=$(SRCS:.c=.o)
|
|
||||||
-LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
|
||||||
+LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager -lgcov
|
|
||||||
|
|
||||||
libhintfilter.so: $(OBJ)
|
|
||||||
$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
|
|
||||||
diff --git a/server/modules/monitor/Makefile b/server/modules/monitor/Makefile
|
|
||||||
index f815efe..308f496 100644
|
|
||||||
--- a/server/modules/monitor/Makefile
|
|
||||||
+++ b/server/modules/monitor/Makefile
|
|
||||||
@@ -25,7 +25,7 @@ UTILSPATH := $(ROOT_PATH)/utils
|
|
||||||
|
|
||||||
CC=cc
|
|
||||||
CFLAGS=-c -fPIC -I. -I/usr/include -I../include -I../../include -I$(LOGPATH) \
|
|
||||||
- -I$(UTILSPATH) $(MYSQL_HEADERS) -Wall -g
|
|
||||||
+ -I$(UTILSPATH) $(MYSQL_HEADERS) -Wall -g -fprofile-arcs -ftest-coverage
|
|
||||||
|
|
||||||
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
|
||||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) \
|
|
||||||
@@ -42,7 +42,7 @@ NDBCLUSTEROBJ=$(NDBCLUSTERSRCS:.c=.o)
|
|
||||||
SRCS=$(MYSQLSRCS) $(GALERASRCS) $(NDBCLUSTERSRCS)
|
|
||||||
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 libndbclustermon.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 dbb0503..8304766 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
|
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ WEBSRCS=webserver.o
|
|
||||||
WEBOBJ=$(WEBSRCS:.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 \
|
|
||||||
libwebserver.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)
|
|
@ -255,11 +255,7 @@ static int logmanager_write_log(
|
|||||||
static blockbuf_t* blockbuf_init(logfile_id_t id);
|
static blockbuf_t* blockbuf_init(logfile_id_t id);
|
||||||
static void blockbuf_node_done(void* bb_data);
|
static void blockbuf_node_done(void* bb_data);
|
||||||
static char* blockbuf_get_writepos(
|
static char* blockbuf_get_writepos(
|
||||||
#if 0
|
|
||||||
int** refcount,
|
|
||||||
#else
|
|
||||||
blockbuf_t** p_bb,
|
blockbuf_t** p_bb,
|
||||||
#endif
|
|
||||||
logfile_id_t id,
|
logfile_id_t id,
|
||||||
size_t str_len,
|
size_t str_len,
|
||||||
bool flush);
|
bool flush);
|
||||||
@ -653,7 +649,16 @@ static int logmanager_write_log(
|
|||||||
int safe_str_len;
|
int safe_str_len;
|
||||||
|
|
||||||
timestamp_len = get_timestamp_len();
|
timestamp_len = get_timestamp_len();
|
||||||
safe_str_len = MIN(timestamp_len-1+str_len, lf->lf_buf_size);
|
|
||||||
|
/** Findout how much can be safely written with current block size */
|
||||||
|
if (timestamp_len-1+str_len > lf->lf_buf_size)
|
||||||
|
{
|
||||||
|
safe_str_len = lf->lf_buf_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
safe_str_len = timestamp_len-1+str_len;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Seek write position and register to block buffer.
|
* Seek write position and register to block buffer.
|
||||||
* Then print formatted string to write position.
|
* Then print formatted string to write position.
|
||||||
@ -673,9 +678,9 @@ static int logmanager_write_log(
|
|||||||
* of the timestamp string.
|
* of the timestamp string.
|
||||||
*/
|
*/
|
||||||
if (use_valist) {
|
if (use_valist) {
|
||||||
vsnprintf(wp+timestamp_len, safe_str_len, str, valist);
|
vsnprintf(wp+timestamp_len, safe_str_len-timestamp_len, str, valist);
|
||||||
} else {
|
} else {
|
||||||
snprintf(wp+timestamp_len, safe_str_len, "%s", str);
|
snprintf(wp+timestamp_len, safe_str_len-timestamp_len, "%s", str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** write to syslog */
|
/** write to syslog */
|
||||||
@ -694,12 +699,7 @@ static int logmanager_write_log(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wp[safe_str_len-1] = '\n';
|
||||||
/** remove double line feed */
|
|
||||||
if (wp[timestamp_len+str_len-2] == '\n') {
|
|
||||||
wp[timestamp_len+str_len-2]=' ';
|
|
||||||
}
|
|
||||||
wp[timestamp_len+str_len-1]='\n';
|
|
||||||
blockbuf_unregister(bb);
|
blockbuf_unregister(bb);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,7 @@ install: liblink
|
|||||||
install liblog_manager.so.1.0.1 liblog_manager.so $(DEST)/lib
|
install liblog_manager.so.1.0.1 liblog_manager.so $(DEST)/lib
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@rm -f depend
|
@$(DEL) depend
|
||||||
$(CPP) -M $(CFLAGS) \
|
$(CPP) -M $(CFLAGS) \
|
||||||
$(MYSQL_HEADERS) \
|
$(MYSQL_HEADERS) \
|
||||||
-I$(UTILS_PATH) -I./ \
|
-I$(UTILS_PATH) -I./ \
|
||||||
|
@ -39,7 +39,7 @@ buildtests:
|
|||||||
-o testlog \
|
-o testlog \
|
||||||
-I$(MARIADB_SRC_PATH)/include \
|
-I$(MARIADB_SRC_PATH)/include \
|
||||||
-I$(LOG_MANAGER_PATH) -I$(UTILS_PATH) testlog.c \
|
-I$(LOG_MANAGER_PATH) -I$(UTILS_PATH) testlog.c \
|
||||||
-llog_manager $(LDLIBS) \
|
-lstdc++ -llog_manager $(LDLIBS) \
|
||||||
$(UTILS_PATH)/skygw_utils.o
|
$(UTILS_PATH)/skygw_utils.o
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ SRCS := query_classifier.cc
|
|||||||
UTILS_PATH := $(ROOT_PATH)/utils
|
UTILS_PATH := $(ROOT_PATH)/utils
|
||||||
QUERY_CLASSIFIER_PATH := $(ROOT_PATH)/query_classifier
|
QUERY_CLASSIFIER_PATH := $(ROOT_PATH)/query_classifier
|
||||||
LOG_MANAGER_PATH := $(ROOT_PATH)/log_manager
|
LOG_MANAGER_PATH := $(ROOT_PATH)/log_manager
|
||||||
|
SERVER_INC_PATH := $(ROOT_PATH)/server/include
|
||||||
|
MODULE_INC_PATH := $(ROOT_PATH)/server/modules/include
|
||||||
|
|
||||||
makeall: clean all
|
makeall: clean all
|
||||||
|
|
||||||
@ -33,7 +35,6 @@ runtests:
|
|||||||
testall:
|
testall:
|
||||||
$(MAKE) -C test testall
|
$(MAKE) -C test testall
|
||||||
|
|
||||||
|
|
||||||
utils:
|
utils:
|
||||||
$(MAKE) -C $(UTILS_PATH) clean all
|
$(MAKE) -C $(UTILS_PATH) clean all
|
||||||
|
|
||||||
@ -43,6 +44,9 @@ libcomp:
|
|||||||
$(CPP) -c $(CFLAGS) \
|
$(CPP) -c $(CFLAGS) \
|
||||||
$(MYSQL_HEADERS) \
|
$(MYSQL_HEADERS) \
|
||||||
-I$(LOG_MANAGER_PATH) \
|
-I$(LOG_MANAGER_PATH) \
|
||||||
|
-I$(SERVER_INC_PATH) \
|
||||||
|
-I$(MODULE_INC_PATH) \
|
||||||
|
-I$(UTILS_PATH) \
|
||||||
-I./ \
|
-I./ \
|
||||||
-fPIC ./query_classifier.cc -o query_classifier.o
|
-fPIC ./query_classifier.cc -o query_classifier.o
|
||||||
|
|
||||||
@ -62,10 +66,13 @@ install: liblink
|
|||||||
install ./libquery_classifier.so.1.0.1 ./libquery_classifier.so $(DEST)/lib
|
install ./libquery_classifier.so.1.0.1 ./libquery_classifier.so $(DEST)/lib
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@rm -f depend
|
@$(DEL) depend
|
||||||
$(CPP) -M $(CFLAGS) \
|
$(CPP) -M $(CFLAGS) \
|
||||||
$(MYSQL_HEADERS) \
|
$(MYSQL_HEADERS) \
|
||||||
-I$(LOG_MANAGER_PATH) \
|
-I$(LOG_MANAGER_PATH) \
|
||||||
|
-I$(SERVER_INC_PATH) \
|
||||||
|
-I$(MODULE_INC_PATH) \
|
||||||
|
-I$(UTILS_PATH) \
|
||||||
-I./ \
|
-I./ \
|
||||||
$(SRCS) > depend
|
$(SRCS) > depend
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "../utils/skygw_types.h"
|
#include "../utils/skygw_types.h"
|
||||||
#include "../utils/skygw_debug.h"
|
#include "../utils/skygw_debug.h"
|
||||||
#include <log_manager.h>
|
#include <log_manager.h>
|
||||||
|
#include <mysql_client_server_protocol.h>
|
||||||
|
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
@ -83,122 +84,158 @@ static bool skygw_stmt_causes_implicit_commit(
|
|||||||
static int is_autocommit_stmt(
|
static int is_autocommit_stmt(
|
||||||
LEX* lex);
|
LEX* lex);
|
||||||
|
|
||||||
|
static void parsing_info_set_plain_str(void* ptr,
|
||||||
|
char* str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @node (write brief function description here)
|
* Calls parser for the query includede in the buffer. Creates and adds parsing
|
||||||
|
* information to buffer if it doesn't exist already. Resolves the query type.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* @param querybuf buffer including the query and possibly the parsing information
|
||||||
* @param query_str - <usage>
|
|
||||||
* <description>
|
|
||||||
*
|
|
||||||
* @param client_flag - <usage>
|
|
||||||
* <description>
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @details (write detailed description here)
|
|
||||||
*
|
*
|
||||||
|
* @return query type
|
||||||
*/
|
*/
|
||||||
skygw_query_type_t skygw_query_classifier_get_type(
|
skygw_query_type_t query_classifier_get_type(
|
||||||
const char* query,
|
GWBUF* querybuf)
|
||||||
unsigned long client_flags,
|
|
||||||
MYSQL** p_mysql)
|
|
||||||
{
|
{
|
||||||
MYSQL* mysql;
|
MYSQL* mysql;
|
||||||
char* query_str;
|
|
||||||
const char* user = "skygw";
|
|
||||||
const char* db = "skygw";
|
|
||||||
THD* thd;
|
|
||||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||||
bool failp = FALSE;
|
bool succp;
|
||||||
|
|
||||||
ss_info_dassert(query != NULL, ("query_str is NULL"));
|
ss_info_dassert(querybuf != NULL, ("querybuf is NULL"));
|
||||||
|
|
||||||
query_str = const_cast<char*>(query);
|
/** Create parsing info for the query and store it to buffer */
|
||||||
LOGIF(LT, (skygw_log_write(
|
succp = query_is_parsed(querybuf);
|
||||||
LOGFILE_TRACE,
|
|
||||||
"Query : \"%s\"", query_str)));
|
|
||||||
|
|
||||||
/** Get server handle */
|
if (!succp)
|
||||||
mysql = mysql_init(NULL);
|
|
||||||
|
|
||||||
if (mysql == NULL) {
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
|
||||||
LOGFILE_ERROR,
|
|
||||||
"Error : call to mysql_real_connect failed due %d, %s.",
|
|
||||||
mysql_errno(mysql),
|
|
||||||
mysql_error(mysql))));
|
|
||||||
|
|
||||||
mysql_library_end();
|
|
||||||
goto return_qtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_mysql != NULL)
|
|
||||||
{
|
{
|
||||||
*p_mysql = mysql;
|
succp = parse_query(querybuf);
|
||||||
}
|
}
|
||||||
/** Set methods and authentication to mysql */
|
/** Read thd pointer and resolve the query type with it. */
|
||||||
mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_skygw");
|
if (succp)
|
||||||
mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
|
|
||||||
mysql->methods = &embedded_methods;
|
|
||||||
mysql->user = my_strdup(user, MYF(0));
|
|
||||||
mysql->db = my_strdup(db, MYF(0));
|
|
||||||
mysql->passwd = NULL;
|
|
||||||
|
|
||||||
/** Get one or create new THD object to be use in parsing */
|
|
||||||
thd = get_or_create_thd_for_parsing(mysql, query_str);
|
|
||||||
|
|
||||||
if (thd == NULL)
|
|
||||||
{
|
{
|
||||||
skygw_query_classifier_free(mysql);
|
parsing_info_t* pi;
|
||||||
*p_mysql = NULL;
|
|
||||||
goto return_qtype;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Create parse_tree inside thd.
|
|
||||||
* thd and even lex are readable even if parser failed so let it
|
|
||||||
* continue despite failure.
|
|
||||||
*/
|
|
||||||
failp = create_parse_tree(thd);
|
|
||||||
qtype = resolve_query_type(thd);
|
|
||||||
|
|
||||||
if (p_mysql == NULL)
|
pi = (parsing_info_t*)gwbuf_get_buffer_object_data(querybuf,
|
||||||
{
|
GWBUF_PARSING_INFO);
|
||||||
skygw_query_classifier_free(mysql);
|
|
||||||
|
if (pi != NULL)
|
||||||
|
{
|
||||||
|
mysql = (MYSQL *)pi->pi_handle;
|
||||||
|
|
||||||
|
/** Find out the query type */
|
||||||
|
if (mysql != NULL)
|
||||||
|
{
|
||||||
|
qtype = resolve_query_type((THD *)mysql->thd);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return_qtype:
|
|
||||||
return qtype;
|
return qtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
void skygw_query_classifier_free(
|
* Create parsing info and try to parse the query included in the query buffer.
|
||||||
MYSQL* mysql)
|
* Store pointer to created parse_tree_t object to buffer.
|
||||||
|
*
|
||||||
|
* @param querybuf buffer including the query and possibly the parsing information
|
||||||
|
*
|
||||||
|
* @return true if succeed, false otherwise
|
||||||
|
*/
|
||||||
|
bool parse_query (
|
||||||
|
GWBUF* querybuf)
|
||||||
{
|
{
|
||||||
if (mysql->thd != NULL)
|
bool succp;
|
||||||
|
THD* thd;
|
||||||
|
uint8_t* data;
|
||||||
|
size_t len;
|
||||||
|
char* query_str;
|
||||||
|
parsing_info_t* pi;
|
||||||
|
|
||||||
|
CHK_GWBUF(querybuf);
|
||||||
|
/** Do not parse without releasing previous parse info first */
|
||||||
|
ss_dassert(!query_is_parsed(querybuf));
|
||||||
|
|
||||||
|
if (query_is_parsed(querybuf))
|
||||||
{
|
{
|
||||||
(*mysql->methods->free_embedded_thd)(mysql);
|
return false;
|
||||||
mysql->thd = NULL;
|
|
||||||
}
|
}
|
||||||
mysql_close(mysql);
|
/** Create parsing info */
|
||||||
mysql_thread_end();
|
pi = parsing_info_init(parsing_info_done);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
succp = false;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
/** Extract query and copy it to different buffer */
|
||||||
|
data = (uint8_t*)GWBUF_DATA(querybuf);
|
||||||
|
len = MYSQL_GET_PACKET_LEN(data)-1; /*< distract 1 for packet type byte */
|
||||||
|
query_str = (char *)malloc(len+1);
|
||||||
|
|
||||||
|
if (query_str == NULL)
|
||||||
|
{
|
||||||
|
/** Free parsing info data */
|
||||||
|
parsing_info_done(pi);
|
||||||
|
succp = false;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
memcpy(query_str, &data[5], len);
|
||||||
|
memset(&query_str[len], 0, 1);
|
||||||
|
parsing_info_set_plain_str(pi, query_str);
|
||||||
|
|
||||||
|
/** Get one or create new THD object to be use in parsing */
|
||||||
|
thd = get_or_create_thd_for_parsing((MYSQL *)pi->pi_handle, query_str);
|
||||||
|
|
||||||
|
if (thd == NULL)
|
||||||
|
{
|
||||||
|
/** Free parsing info data */
|
||||||
|
parsing_info_done(pi);
|
||||||
|
succp = false;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create parse_tree inside thd.
|
||||||
|
* thd and lex are readable even if creating parse tree fails.
|
||||||
|
*/
|
||||||
|
create_parse_tree(thd);
|
||||||
|
/** Add complete parsing info struct to the query buffer */
|
||||||
|
gwbuf_add_buffer_object(querybuf,
|
||||||
|
GWBUF_PARSING_INFO,
|
||||||
|
(void *)pi,
|
||||||
|
parsing_info_done);
|
||||||
|
|
||||||
|
succp = true;
|
||||||
|
retblock:
|
||||||
|
return succp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If buffer has non-NULL gwbuf_parsing_info it is parsed and it has parsing
|
||||||
|
* information included.
|
||||||
|
*
|
||||||
|
* @param buf buffer being examined
|
||||||
|
*
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
bool query_is_parsed(
|
||||||
|
GWBUF* buf)
|
||||||
|
{
|
||||||
|
CHK_GWBUF(buf);
|
||||||
|
return GWBUF_IS_PARSED(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @node (write brief function description here)
|
* Create a thread context, thd, init embedded server, connect to it, and allocate
|
||||||
|
* query to thd.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* @param mysql - <usage>
|
* @param mysql Database handle
|
||||||
* <description>
|
|
||||||
*
|
*
|
||||||
* @param query_str - <usage>
|
* @param query_str Query in plain txt string
|
||||||
* <description>
|
|
||||||
*
|
*
|
||||||
* @return
|
* @return Thread context pointer
|
||||||
*
|
|
||||||
*
|
|
||||||
* @details (write detailed description here)
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static THD* get_or_create_thd_for_parsing(
|
static THD* get_or_create_thd_for_parsing(
|
||||||
@ -475,7 +512,15 @@ static skygw_query_type_t resolve_query_type(
|
|||||||
}
|
}
|
||||||
else if (lex->option_type == OPT_SESSION)
|
else if (lex->option_type == OPT_SESSION)
|
||||||
{
|
{
|
||||||
type |= QUERY_TYPE_SESSION_WRITE;
|
/** SHOW commands are all reads to one backend */
|
||||||
|
if (lex->sql_command == SQLCOM_SHOW_VARIABLES)
|
||||||
|
{
|
||||||
|
type |= QUERY_TYPE_SESSION_READ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type |= QUERY_TYPE_SESSION_WRITE;
|
||||||
|
}
|
||||||
goto return_qtype;
|
goto return_qtype;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -494,10 +539,18 @@ static skygw_query_type_t resolve_query_type(
|
|||||||
force_data_modify_op_replication)
|
force_data_modify_op_replication)
|
||||||
{
|
{
|
||||||
type |= QUERY_TYPE_SESSION_WRITE;
|
type |= QUERY_TYPE_SESSION_WRITE;
|
||||||
} else {
|
|
||||||
type |= QUERY_TYPE_WRITE;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type |= QUERY_TYPE_WRITE;
|
||||||
|
|
||||||
|
if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE &&
|
||||||
|
lex->sql_command == SQLCOM_CREATE_TABLE)
|
||||||
|
{
|
||||||
|
type |= QUERY_TYPE_CREATE_TMP_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
goto return_qtype;
|
goto return_qtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,7 +688,6 @@ static skygw_query_type_t resolve_query_type(
|
|||||||
"%lu [resolve_query_type] "
|
"%lu [resolve_query_type] "
|
||||||
"functype FUNC_SP, stored proc "
|
"functype FUNC_SP, stored proc "
|
||||||
"or unknown function.",
|
"or unknown function.",
|
||||||
"%s:%s",
|
|
||||||
pthread_self())));
|
pthread_self())));
|
||||||
break;
|
break;
|
||||||
case Item_func::UDF_FUNC:
|
case Item_func::UDF_FUNC:
|
||||||
@ -648,7 +700,6 @@ static skygw_query_type_t resolve_query_type(
|
|||||||
pthread_self())));
|
pthread_self())));
|
||||||
break;
|
break;
|
||||||
case Item_func::NOW_FUNC:
|
case Item_func::NOW_FUNC:
|
||||||
case Item_func::GSYSVAR_FUNC:
|
|
||||||
func_qtype |= QUERY_TYPE_LOCAL_READ;
|
func_qtype |= QUERY_TYPE_LOCAL_READ;
|
||||||
LOGIF(LD, (skygw_log_write(
|
LOGIF(LD, (skygw_log_write(
|
||||||
LOGFILE_DEBUG,
|
LOGFILE_DEBUG,
|
||||||
@ -657,8 +708,30 @@ static skygw_query_type_t resolve_query_type(
|
|||||||
"executed in MaxScale.",
|
"executed in MaxScale.",
|
||||||
pthread_self())));
|
pthread_self())));
|
||||||
break;
|
break;
|
||||||
|
/** System session variable */
|
||||||
|
case Item_func::GSYSVAR_FUNC:
|
||||||
|
/** User-defined variable read */
|
||||||
|
case Item_func::GUSERVAR_FUNC:
|
||||||
|
/** User-defined variable modification */
|
||||||
|
case Item_func::SUSERVAR_FUNC:
|
||||||
|
func_qtype |= QUERY_TYPE_SESSION_READ;
|
||||||
|
LOGIF(LD, (skygw_log_write(
|
||||||
|
LOGFILE_DEBUG,
|
||||||
|
"%lu [resolve_query_type] "
|
||||||
|
"functype SUSERVAR_FUNC, could be "
|
||||||
|
"executed in MaxScale.",
|
||||||
|
pthread_self())));
|
||||||
|
break;
|
||||||
case Item_func::UNKNOWN_FUNC:
|
case Item_func::UNKNOWN_FUNC:
|
||||||
func_qtype |= QUERY_TYPE_READ;
|
if (item->name != NULL &&
|
||||||
|
strcmp(item->name, "last_insert_id()") == 0)
|
||||||
|
{
|
||||||
|
func_qtype |= QUERY_TYPE_SESSION_READ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
func_qtype |= QUERY_TYPE_READ;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Many built-in functions are of this
|
* Many built-in functions are of this
|
||||||
* type, for example, rand(), soundex(),
|
* type, for example, rand(), soundex(),
|
||||||
@ -816,3 +889,379 @@ char* skygw_query_classifier_get_stmtname(
|
|||||||
return ((THD *)(mysql->thd))->lex->prepared_stmt_name.str;
|
return ((THD *)(mysql->thd))->lex->prepared_stmt_name.str;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the head of the list of tables affected by the current select statement.
|
||||||
|
* @param thd Pointer to a valid THD
|
||||||
|
* @return Pointer to the head of the TABLE_LIST chain or NULL in case of an error
|
||||||
|
*/
|
||||||
|
void* skygw_get_affected_tables(void* thdp)
|
||||||
|
{
|
||||||
|
THD* thd = (THD*)thdp;
|
||||||
|
|
||||||
|
if(thd == NULL ||
|
||||||
|
thd->lex == NULL ||
|
||||||
|
thd->lex->current_select == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(thd != NULL &&
|
||||||
|
thd->lex != NULL &&
|
||||||
|
thd->lex->current_select != NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void*)thd->lex->current_select->table_list.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the parsetree and lists all the affected tables and views in the query.
|
||||||
|
* In the case of an error, the size of the table is set to zero and no memory is allocated.
|
||||||
|
* The caller must free the allocated memory.
|
||||||
|
*
|
||||||
|
* @param querybuf GWBUF where the table names are extracted from
|
||||||
|
* @param tblsize Pointer where the number of tables is written
|
||||||
|
* @return Array of null-terminated strings with the table names
|
||||||
|
*/
|
||||||
|
char** skygw_get_table_names(GWBUF* querybuf,int* tblsize)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi;
|
||||||
|
MYSQL* mysql;
|
||||||
|
THD* thd;
|
||||||
|
TABLE_LIST* tbl;
|
||||||
|
int i = 0, currtblsz = 0;
|
||||||
|
char**tables,**tmp;
|
||||||
|
|
||||||
|
if (!GWBUF_IS_PARSED(querybuf))
|
||||||
|
{
|
||||||
|
tables = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
|
||||||
|
GWBUF_PARSING_INFO);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
tables = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pi->pi_query_plain_str == NULL ||
|
||||||
|
(mysql = (MYSQL *)pi->pi_handle) == NULL ||
|
||||||
|
(thd = (THD *)mysql->thd) == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(pi->pi_query_plain_str != NULL &&
|
||||||
|
mysql != NULL &&
|
||||||
|
thd != NULL);
|
||||||
|
tables = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
thd->lex->current_select = thd->lex->all_selects_list;
|
||||||
|
|
||||||
|
while(thd->lex->current_select){
|
||||||
|
|
||||||
|
tbl = (TABLE_LIST*)skygw_get_affected_tables(thd);
|
||||||
|
|
||||||
|
while (tbl)
|
||||||
|
{
|
||||||
|
if(i >= currtblsz){
|
||||||
|
|
||||||
|
tmp = (char**)malloc(sizeof(char*)*(currtblsz*2+1));
|
||||||
|
|
||||||
|
if(tmp){
|
||||||
|
if(currtblsz > 0){
|
||||||
|
int x;
|
||||||
|
for(x = 0;x<currtblsz;x++){
|
||||||
|
tmp[x] = tables[x];
|
||||||
|
}
|
||||||
|
free(tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
tables = tmp;
|
||||||
|
currtblsz = currtblsz*2 + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
tables[i++] = strdup(tbl->alias);
|
||||||
|
tbl=tbl->next_local;
|
||||||
|
}
|
||||||
|
thd->lex->current_select = thd->lex->current_select->next_select_in_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
retblock:
|
||||||
|
*tblsize = i;
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract the name of the created table.
|
||||||
|
* @param querybuf Buffer to use.
|
||||||
|
* @return A pointer to the name if a table was created, otherwise NULL
|
||||||
|
*/
|
||||||
|
char* skygw_get_created_table_name(GWBUF* querybuf)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi;
|
||||||
|
MYSQL* mysql;
|
||||||
|
THD* thd;
|
||||||
|
if (!GWBUF_IS_PARSED(querybuf))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
|
||||||
|
GWBUF_PARSING_INFO);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mysql = (MYSQL *)pi->pi_handle) == NULL ||
|
||||||
|
(thd = (THD *)mysql->thd) == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(mysql != NULL &&
|
||||||
|
thd != NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(thd->lex->create_last_non_select_table &&
|
||||||
|
thd->lex->create_last_non_select_table->table_name){
|
||||||
|
char* name = strdup(thd->lex->create_last_non_select_table->table_name);
|
||||||
|
return name;
|
||||||
|
}else{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks whether the buffer contains a DROP TABLE... query.
|
||||||
|
* @param querybuf Buffer to inspect
|
||||||
|
* @return true if it contains the query otherwise false
|
||||||
|
*/
|
||||||
|
bool is_drop_table_query(GWBUF* querybuf)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi;
|
||||||
|
MYSQL* mysql;
|
||||||
|
THD* thd;
|
||||||
|
|
||||||
|
if (!GWBUF_IS_PARSED(querybuf))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
|
||||||
|
GWBUF_PARSING_INFO);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mysql = (MYSQL *)pi->pi_handle) == NULL ||
|
||||||
|
(thd = (THD *)mysql->thd) == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(mysql != NULL &&
|
||||||
|
thd != NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return thd->lex->sql_command == SQLCOM_DROP_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Replace user-provided literals with question marks. Return a copy of the
|
||||||
|
* querystr with replacements.
|
||||||
|
*
|
||||||
|
* @param querybuf GWBUF buffer including necessary parsing info
|
||||||
|
*
|
||||||
|
* @return Copy of querystr where literals are replaces with question marks or
|
||||||
|
* NULL if querystr is NULL, thread context or lex are NULL or if replacement
|
||||||
|
* function fails.
|
||||||
|
*
|
||||||
|
* Replaced literal types are STRING_ITEM,INT_ITEM,DECIMAL_ITEM,REAL_ITEM,
|
||||||
|
* VARBIN_ITEM,NULL_ITEM
|
||||||
|
*/
|
||||||
|
char* skygw_get_canonical(
|
||||||
|
GWBUF* querybuf)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi;
|
||||||
|
MYSQL* mysql;
|
||||||
|
THD* thd;
|
||||||
|
LEX* lex;
|
||||||
|
Item* item;
|
||||||
|
char* querystr;
|
||||||
|
|
||||||
|
if (!GWBUF_IS_PARSED(querybuf))
|
||||||
|
{
|
||||||
|
querystr = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
|
||||||
|
GWBUF_PARSING_INFO);
|
||||||
|
CHK_PARSING_INFO(pi);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
querystr = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pi->pi_query_plain_str == NULL ||
|
||||||
|
(mysql = (MYSQL *)pi->pi_handle) == NULL ||
|
||||||
|
(thd = (THD *)mysql->thd) == NULL ||
|
||||||
|
(lex = thd->lex) == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(pi->pi_query_plain_str != NULL &&
|
||||||
|
mysql != NULL &&
|
||||||
|
thd != NULL &&
|
||||||
|
lex != NULL);
|
||||||
|
querystr = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
querystr = strdup(pi->pi_query_plain_str);
|
||||||
|
|
||||||
|
for (item=thd->free_list; item != NULL; item=item->next)
|
||||||
|
{
|
||||||
|
Item::Type itype;
|
||||||
|
|
||||||
|
if (item->name == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
itype = item->type();
|
||||||
|
|
||||||
|
if (itype == Item::STRING_ITEM)
|
||||||
|
{
|
||||||
|
String tokenstr;
|
||||||
|
String* res = item->val_str_ascii(&tokenstr);
|
||||||
|
|
||||||
|
if (res->is_empty()) /*< empty string */
|
||||||
|
{
|
||||||
|
querystr = replace_literal(querystr, "\"\"", "\"?\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
querystr = replace_literal(querystr, res->ptr(), "?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (itype == Item::INT_ITEM ||
|
||||||
|
itype == Item::DECIMAL_ITEM ||
|
||||||
|
itype == Item::REAL_ITEM ||
|
||||||
|
itype == Item::VARBIN_ITEM ||
|
||||||
|
itype == Item::NULL_ITEM)
|
||||||
|
{
|
||||||
|
querystr = replace_literal(querystr, item->name, "?");
|
||||||
|
}
|
||||||
|
} /*< for */
|
||||||
|
retblock:
|
||||||
|
return querystr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create parsing information; initialize mysql handle, allocate parsing info
|
||||||
|
* struct and set handle and free function pointer to it.
|
||||||
|
*
|
||||||
|
* @param donefun pointer to free function
|
||||||
|
*
|
||||||
|
* @return pointer to parsing information
|
||||||
|
*/
|
||||||
|
parsing_info_t* parsing_info_init(
|
||||||
|
void (*donefun)(void *))
|
||||||
|
{
|
||||||
|
parsing_info_t* pi = NULL;
|
||||||
|
MYSQL* mysql;
|
||||||
|
const char* user = "skygw";
|
||||||
|
const char* db = "skygw";
|
||||||
|
|
||||||
|
ss_dassert(donefun != NULL);
|
||||||
|
|
||||||
|
/** Get server handle */
|
||||||
|
mysql = mysql_init(NULL);
|
||||||
|
ss_dassert(mysql != NULL);
|
||||||
|
|
||||||
|
if (mysql == NULL) {
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : call to mysql_real_connect failed due %d, %s.",
|
||||||
|
mysql_errno(mysql),
|
||||||
|
mysql_error(mysql))));
|
||||||
|
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
/** Set methods and authentication to mysql */
|
||||||
|
mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_skygw");
|
||||||
|
mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
|
||||||
|
mysql->methods = &embedded_methods;
|
||||||
|
mysql->user = my_strdup(user, MYF(0));
|
||||||
|
mysql->db = my_strdup(db, MYF(0));
|
||||||
|
mysql->passwd = NULL;
|
||||||
|
|
||||||
|
pi = (parsing_info_t*)calloc(1, sizeof(parsing_info_t));
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
mysql_close(mysql);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
pi->pi_chk_top = CHK_NUM_PINFO;
|
||||||
|
pi->pi_chk_tail = CHK_NUM_PINFO;
|
||||||
|
#endif
|
||||||
|
/** Set handle and free function to parsing info struct */
|
||||||
|
pi->pi_handle = mysql;
|
||||||
|
pi->pi_done_fp = donefun;
|
||||||
|
|
||||||
|
retblock:
|
||||||
|
return pi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free function for parsing info. Called by gwbuf_free or in case initialization
|
||||||
|
* of parsing information fails.
|
||||||
|
*
|
||||||
|
* @param ptr Pointer to parsing information, cast required
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parsing_info_done(
|
||||||
|
void* ptr)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi = (parsing_info_t *)ptr;
|
||||||
|
|
||||||
|
if (pi->pi_handle != NULL)
|
||||||
|
{
|
||||||
|
MYSQL* mysql = (MYSQL *)pi->pi_handle;
|
||||||
|
|
||||||
|
if (mysql->thd != NULL)
|
||||||
|
{
|
||||||
|
(*mysql->methods->free_embedded_thd)(mysql);
|
||||||
|
mysql->thd = NULL;
|
||||||
|
}
|
||||||
|
mysql_close(mysql);
|
||||||
|
}
|
||||||
|
/** Free plain text query string */
|
||||||
|
if (pi->pi_query_plain_str != NULL)
|
||||||
|
{
|
||||||
|
free(pi->pi_query_plain_str);
|
||||||
|
}
|
||||||
|
free(pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add plain text query string to parsing info.
|
||||||
|
*
|
||||||
|
* @param ptr Pointer to parsing info struct, cast required
|
||||||
|
* @param str String to be added
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
static void parsing_info_set_plain_str(
|
||||||
|
void* ptr,
|
||||||
|
char* str)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi = (parsing_info_t *)ptr;
|
||||||
|
CHK_PARSING_INFO(pi);
|
||||||
|
|
||||||
|
pi->pi_query_plain_str = str;
|
||||||
|
}
|
@ -20,7 +20,8 @@ Copyright SkySQL Ab
|
|||||||
/** getpid */
|
/** getpid */
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#include "../utils/skygw_utils.h"
|
#include <skygw_utils.h>
|
||||||
|
#include <buffer.h>
|
||||||
|
|
||||||
EXTERN_C_BLOCK_BEGIN
|
EXTERN_C_BLOCK_BEGIN
|
||||||
|
|
||||||
@ -43,23 +44,46 @@ typedef enum {
|
|||||||
QUERY_TYPE_COMMIT = 0x0200, /*< COMMIT */
|
QUERY_TYPE_COMMIT = 0x0200, /*< COMMIT */
|
||||||
QUERY_TYPE_PREPARE_NAMED_STMT = 0x0400, /*< Prepared stmt with name from user */
|
QUERY_TYPE_PREPARE_NAMED_STMT = 0x0400, /*< Prepared stmt with name from user */
|
||||||
QUERY_TYPE_PREPARE_STMT = 0x0800, /*< Prepared stmt with id provided by server */
|
QUERY_TYPE_PREPARE_STMT = 0x0800, /*< Prepared stmt with id provided by server */
|
||||||
QUERY_TYPE_EXEC_STMT = 0x1000 /*< Execute prepared statement */
|
QUERY_TYPE_EXEC_STMT = 0x1000, /*< Execute prepared statement */
|
||||||
|
QUERY_TYPE_SESSION_READ = 0x2000, /*< Read session data (from master 31.8.14) */
|
||||||
|
QUERY_TYPE_CREATE_TMP_TABLE = 0x4000, /*< Create temporary table */
|
||||||
|
QUERY_TYPE_READ_TMP_TABLE = 0x8000 /*< Read temporary table */
|
||||||
} skygw_query_type_t;
|
} skygw_query_type_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct parsing_info_st {
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
skygw_chk_t pi_chk_top;
|
||||||
|
#endif
|
||||||
|
void* pi_handle; /*< parsing info object pointer */
|
||||||
|
char* pi_query_plain_str; /*< query as plain string */
|
||||||
|
void (*pi_done_fp)(void *); /*< clean-up function for parsing info */
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
skygw_chk_t pi_chk_tail;
|
||||||
|
#endif
|
||||||
|
} parsing_info_t;
|
||||||
|
|
||||||
|
|
||||||
#define QUERY_IS_TYPE(mask,type) ((mask & type) == type)
|
#define QUERY_IS_TYPE(mask,type) ((mask & type) == type)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create THD and use it for creating parse tree. Examine parse tree and
|
* Create THD and use it for creating parse tree. Examine parse tree and
|
||||||
* classify the query.
|
* classify the query.
|
||||||
*/
|
*/
|
||||||
skygw_query_type_t skygw_query_classifier_get_type(
|
skygw_query_type_t query_classifier_get_type(GWBUF* querybuf);
|
||||||
const char* query_str,
|
|
||||||
unsigned long client_flags,
|
|
||||||
MYSQL** mysql);
|
|
||||||
|
|
||||||
/** Free THD context and close MYSQL */
|
/** Free THD context and close MYSQL */
|
||||||
void skygw_query_classifier_free(MYSQL* mysql);
|
char* skygw_query_classifier_get_stmtname(MYSQL* mysql);
|
||||||
char* skygw_query_classifier_get_stmtname(MYSQL* mysql);
|
char* skygw_get_created_table_name(GWBUF* querybuf);
|
||||||
|
bool is_drop_table_query(GWBUF* querybuf);
|
||||||
|
void* skygw_get_affected_tables(void* thdp);
|
||||||
|
char** skygw_get_table_names(GWBUF* querybuf,int* tblsize);
|
||||||
|
char* skygw_get_canonical(GWBUF* querybuf);
|
||||||
|
bool parse_query (GWBUF* querybuf);
|
||||||
|
parsing_info_t* parsing_info_init(void (*donefun)(void *));
|
||||||
|
void parsing_info_done(void* ptr);
|
||||||
|
bool query_is_parsed(GWBUF* buf);
|
||||||
|
|
||||||
|
|
||||||
EXTERN_C_BLOCK_END
|
EXTERN_C_BLOCK_END
|
||||||
|
|
||||||
|
64
query_classifier/test/canonical_tests/Makefile
Normal file
64
query_classifier/test/canonical_tests/Makefile
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# 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 ../../../makefile.inc
|
||||||
|
include ../../../test.inc
|
||||||
|
|
||||||
|
CC = gcc
|
||||||
|
CPP = g++
|
||||||
|
|
||||||
|
TESTPATH := $(shell pwd)
|
||||||
|
TESTLOG := $(TESTPATH)/testqclass.log
|
||||||
|
QUERY_CLASSIFIER_PATH := $(ROOT_PATH)/query_classifier
|
||||||
|
LOG_MANAGER_PATH := $(ROOT_PATH)/log_manager
|
||||||
|
UTILS_PATH := $(ROOT_PATH)/utils
|
||||||
|
CORE_PATH := $(ROOT_PATH)/server/core
|
||||||
|
TESTAPP = $(TESTPATH)/canonizer
|
||||||
|
|
||||||
|
LDFLAGS=-L$(QUERY_CLASSIFIER_PATH) \
|
||||||
|
-L$(LOG_MANAGER_PATH) \
|
||||||
|
-L$(EMBEDDED_LIB) \
|
||||||
|
-Wl,-rpath,$(DEST)/lib \
|
||||||
|
-Wl,-rpath,$(EMBEDDED_LIB) \
|
||||||
|
-Wl,-rpath,$(LOG_MANAGER_PATH) \
|
||||||
|
-Wl,-rpath,$(QUERY_CLASSIFIER_PATH)
|
||||||
|
|
||||||
|
LIBS=-lstdc++ -lpthread -lquery_classifier -lz -ldl -lssl -laio -lcrypt -lcrypto -lrt \
|
||||||
|
-llog_manager $(UTILS_PATH)/skygw_utils.o $(CORE_PATH)/buffer.o $(CORE_PATH)/atomic.o $(CORE_PATH)/spinlock.o
|
||||||
|
|
||||||
|
CFLAGS=-g $(MYSQL_HEADERS) \
|
||||||
|
-I$(QUERY_CLASSIFIER_PATH) \
|
||||||
|
$(MYSQL_HEADERS) \
|
||||||
|
-I$(ROOT_PATH)/server/include \
|
||||||
|
-I$(UTILS_PATH)
|
||||||
|
|
||||||
|
EMBFLAGS=$(shell mysql_config --cflags --libmysqld-libs)
|
||||||
|
|
||||||
|
|
||||||
|
testall:
|
||||||
|
$(MAKE) cleantests
|
||||||
|
$(MAKE) buildtests
|
||||||
|
$(MAKE) runtests
|
||||||
|
|
||||||
|
cleantests:
|
||||||
|
- $(DEL) *.o
|
||||||
|
- $(DEL) *~
|
||||||
|
- $(DEL) canonizer
|
||||||
|
- $(DEL) aria_log*
|
||||||
|
- $(DEL) ib*
|
||||||
|
|
||||||
|
buildtests: $(OBJS)
|
||||||
|
cp $(ERRMSG)/errmsg.sys .
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) $(EMBFLAGS) $(LIBS) canonizer.c -o $(TESTAPP)
|
||||||
|
|
||||||
|
runtests:
|
||||||
|
@echo "" > $(TESTLOG)
|
||||||
|
@echo "-------------------------------" >> $(TESTLOG)
|
||||||
|
@echo $(shell date) >> $(TESTLOG)
|
||||||
|
@echo "Canonical Query Tests" >> $(TESTLOG)
|
||||||
|
@echo "-------------------------------" >> $(TESTLOG)
|
||||||
|
@echo "" >> $(TESTLOG)
|
||||||
|
./canontest.sh $(TESTLOG) input.sql output.sql expected.sql
|
101
query_classifier/test/canonical_tests/canonizer.c
Normal file
101
query_classifier/test/canonical_tests/canonizer.c
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <query_classifier.h>
|
||||||
|
#include <buffer.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
|
||||||
|
static char* server_options[] = {
|
||||||
|
"SkySQL Gateway",
|
||||||
|
"--datadir=./",
|
||||||
|
"--language=./",
|
||||||
|
"--skip-innodb",
|
||||||
|
"--default-storage-engine=myisam",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1;
|
||||||
|
|
||||||
|
static char* server_groups[] = {
|
||||||
|
"embedded",
|
||||||
|
"server",
|
||||||
|
"server",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
|
||||||
|
int fdin,fdout,i=0,fnamelen,fsz,lines = 0;
|
||||||
|
unsigned int psize;
|
||||||
|
GWBUF** qbuff;
|
||||||
|
char *qin, *outnm, *buffer, *tok;
|
||||||
|
|
||||||
|
if(argc != 3){
|
||||||
|
printf("Usage: canonizer <input file> <output file>\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool failed = mysql_library_init(num_elements, server_options, server_groups);
|
||||||
|
|
||||||
|
if(failed){
|
||||||
|
printf("Embedded server init failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fnamelen = strlen(argv[1]) + 16;
|
||||||
|
fdin = open(argv[1],O_RDONLY);
|
||||||
|
fsz = lseek(fdin,0,SEEK_END);
|
||||||
|
lseek(fdin,0,SEEK_SET);
|
||||||
|
|
||||||
|
if(!(buffer = malloc(sizeof(char)*fsz))){
|
||||||
|
printf("Error: Failed to allocate memory.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
read(fdin,buffer,fsz);
|
||||||
|
tok = strpbrk(buffer,"\n");
|
||||||
|
lines = 1;
|
||||||
|
|
||||||
|
while((tok = strpbrk(tok + 1,"\n"))){
|
||||||
|
lines++;
|
||||||
|
}
|
||||||
|
|
||||||
|
qbuff = malloc(sizeof(GWBUF*)*lines);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
tok = strtok(buffer,"\n");
|
||||||
|
|
||||||
|
while(tok){
|
||||||
|
qin = strdup(tok);
|
||||||
|
psize = strlen(qin);
|
||||||
|
qbuff[i] = gwbuf_alloc(psize + 6);
|
||||||
|
*(qbuff[i]->sbuf->data + 0) = (unsigned char)psize;
|
||||||
|
*(qbuff[i]->sbuf->data + 1) = (unsigned char)(psize>>8);
|
||||||
|
*(qbuff[i]->sbuf->data + 2) = (unsigned char)(psize>>16);
|
||||||
|
*(qbuff[i]->sbuf->data + 4) = 0x03;
|
||||||
|
memcpy(qbuff[i]->sbuf->data + 5,qin,psize);
|
||||||
|
*(qbuff[i]->sbuf->data + 5 + psize) = 0x00;
|
||||||
|
tok = strtok(NULL,"\n");
|
||||||
|
free(qin);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdout = open(argv[2],O_TRUNC|O_CREAT|O_WRONLY,S_IRWXU|S_IXGRP|S_IXOTH);
|
||||||
|
|
||||||
|
for(i = 0;i<lines;i++){
|
||||||
|
parse_query(qbuff[i]);
|
||||||
|
tok = skygw_get_canonical(qbuff[i]);
|
||||||
|
write(fdout,tok,strlen(tok));
|
||||||
|
write(fdout,"\n",1);
|
||||||
|
gwbuf_free(qbuff[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fdin);
|
||||||
|
close(fdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
21
query_classifier/test/canonical_tests/canontest.sh
Executable file
21
query_classifier/test/canonical_tests/canontest.sh
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
if [[ $# -ne 4 ]]
|
||||||
|
then
|
||||||
|
echo "Usage: canontest.sh <logfile name> <input file> <output file> <expected output>"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
TESTLOG=$1
|
||||||
|
INPUT=$2
|
||||||
|
OUTPUT=$3
|
||||||
|
EXPECTED=$4
|
||||||
|
DIFFLOG=diff.out
|
||||||
|
$PWD/canonizer $INPUT $OUTPUT
|
||||||
|
diff $OUTPUT $EXPECTED > $DIFFLOG
|
||||||
|
if [ $? -eq 0 ]
|
||||||
|
then
|
||||||
|
echo "PASSED" >> $TESTLOG
|
||||||
|
else
|
||||||
|
echo "FAILED" >> $TESTLOG
|
||||||
|
echo "Diff output: " >> $TESTLOG
|
||||||
|
cat $DIFFLOG >> $TESTLOG
|
||||||
|
fi
|
17
query_classifier/test/canonical_tests/expected.sql
Executable file
17
query_classifier/test/canonical_tests/expected.sql
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
select md5(?) =?, sleep(?), rand(?);
|
||||||
|
select * from my1 where md5(?) =?;
|
||||||
|
select md5(?) =?;
|
||||||
|
select * from my1 where md5(?) =?;
|
||||||
|
select sleep(?)
|
||||||
|
select * from tst where lname='?'
|
||||||
|
select ?,?,?,?,?,? from tst
|
||||||
|
select * from tst where fname like '?'
|
||||||
|
select * from tst where lname like '?' order by fname
|
||||||
|
insert into tst values ("?","?"),("?",?),("?","?")
|
||||||
|
drop table if exists tst
|
||||||
|
create table tst(fname varchar(30), lname varchar(30))
|
||||||
|
update tst set lname="?" where fname like '?' or lname like '?'
|
||||||
|
delete from tst where lname like '?' and fname like '?'
|
||||||
|
select ? from tst where fname='?' or lname like '?'
|
||||||
|
select ?,?,?,? from tst where name='?' or name='?' or name='?'
|
||||||
|
select count(?),count(?),count(?),count(?),count (?),count(?) from tst
|
17
query_classifier/test/canonical_tests/input.sql
Executable file
17
query_classifier/test/canonical_tests/input.sql
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
select md5("200000foo") =10, sleep(2), rand(100);
|
||||||
|
select * from my1 where md5("110") =10;
|
||||||
|
select md5("100foo") =10;
|
||||||
|
select * from my1 where md5("100") =10;
|
||||||
|
select sleep(2);
|
||||||
|
select * from tst where lname='Doe';
|
||||||
|
select 1,2,3,4,5,6 from tst;
|
||||||
|
select * from tst where fname like '%a%';
|
||||||
|
select * from tst where lname like '%e%' order by fname;
|
||||||
|
insert into tst values ("John","Doe"),("Plato",null),("Nietzsche","");
|
||||||
|
drop table if exists tst;
|
||||||
|
create table tst(fname varchar(30), lname varchar(30));
|
||||||
|
update tst set lname="Human" where fname like '%a%' or lname like '%a%';
|
||||||
|
delete from tst where lname like '%man%' and fname like '%ard%';
|
||||||
|
select 100 from tst where fname='10' or lname like '%100%';
|
||||||
|
select 1,20,300,4000 from tst where name='1000' or name='200' or name='30' or name='4';
|
||||||
|
select count(1),count(10),count(100),count(2),count (20),count(200) from tst;
|
@ -18,7 +18,7 @@ UTILS_PATH := $(ROOT_PATH)/utils
|
|||||||
TESTAPP = $(TESTPATH)/testmain
|
TESTAPP = $(TESTPATH)/testmain
|
||||||
|
|
||||||
testall:buildtests
|
testall:buildtests
|
||||||
|
$(MAKE) -C canonical_tests testall
|
||||||
testalllaters:
|
testalllaters:
|
||||||
$(MAKE) cleantests
|
$(MAKE) cleantests
|
||||||
$(MAKE) DEBUG=Y DYNLIB=Y buildtests
|
$(MAKE) DEBUG=Y DYNLIB=Y buildtests
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
# 08/07/13 Mark Riddoch Addition of monitor modules
|
# 08/07/13 Mark Riddoch Addition of monitor modules
|
||||||
# 16/07/13 Mark Riddoch Renamed things to match the new naming
|
# 16/07/13 Mark Riddoch Renamed things to match the new naming
|
||||||
|
|
||||||
|
include ../build_gateway.inc
|
||||||
|
include ../makefile.inc
|
||||||
|
|
||||||
DEST=$(HOME)/usr/local/skysql
|
DEST=$(HOME)/usr/local/skysql
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@ -45,7 +48,7 @@ testall:
|
|||||||
$(MAKE) -C test HAVE_SRV=$(HAVE_SRV) testall
|
$(MAKE) -C test HAVE_SRV=$(HAVE_SRV) testall
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
(cd Documentation; rm -rf html)
|
(cd Documentation; $(DEL) html)
|
||||||
(cd core; touch depend.mk ; make clean)
|
(cd core; touch depend.mk ; make clean)
|
||||||
(cd modules/routing; touch depend.mk ; make clean)
|
(cd modules/routing; touch depend.mk ; make clean)
|
||||||
(cd modules/protocol; touch depend.mk ; make clean)
|
(cd modules/protocol; touch depend.mk ; make clean)
|
||||||
|
@ -109,14 +109,14 @@ maxpasswd: $(POBJS)
|
|||||||
echo '#define MAXSCALE_VERSION "'`cat ../../VERSION`'"' > ../include/version.h
|
echo '#define MAXSCALE_VERSION "'`cat ../../VERSION`'"' > ../include/version.h
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) maxscale
|
$(DEL) $(OBJ) maxscale
|
||||||
- rm *.so
|
$(DEL) *.so
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
ctags $(SRCS) $(HDRS)
|
ctags $(SRCS) $(HDRS)
|
||||||
|
|
||||||
depend: ../include/version.h
|
depend: ../include/version.h
|
||||||
@rm -f depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
|
|
||||||
install: maxscale maxkeys maxpasswd
|
install: maxscale maxkeys maxpasswd
|
||||||
|
@ -298,6 +298,7 @@ char* admin_remove_user(
|
|||||||
fname,
|
fname,
|
||||||
err)));
|
err)));
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
fclose(fp_tmp);
|
||||||
unlink(fname_tmp);
|
unlink(fname_tmp);
|
||||||
return ADMIN_ERR_PWDFILEACCESS;
|
return ADMIN_ERR_PWDFILEACCESS;
|
||||||
}
|
}
|
||||||
@ -325,6 +326,7 @@ char* admin_remove_user(
|
|||||||
fname,
|
fname,
|
||||||
err)));
|
err)));
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
fclose(fp_tmp);
|
||||||
unlink(fname_tmp);
|
unlink(fname_tmp);
|
||||||
return ADMIN_ERR_PWDFILEACCESS;
|
return ADMIN_ERR_PWDFILEACCESS;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,11 @@
|
|||||||
#include <atomic.h>
|
#include <atomic.h>
|
||||||
#include <skygw_debug.h>
|
#include <skygw_debug.h>
|
||||||
|
|
||||||
|
static buffer_object_t* gwbuf_remove_buffer_object(
|
||||||
|
GWBUF* buf,
|
||||||
|
buffer_object_t* bufobj);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a new gateway buffer structure of size bytes.
|
* Allocate a new gateway buffer structure of size bytes.
|
||||||
*
|
*
|
||||||
@ -78,7 +83,7 @@ SHARED_BUF *sbuf;
|
|||||||
free(sbuf);
|
free(sbuf);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
spinlock_init(&rval->lock);
|
spinlock_init(&rval->gwbuf_lock);
|
||||||
rval->start = sbuf->data;
|
rval->start = sbuf->data;
|
||||||
rval->end = rval->start + size;
|
rval->end = rval->start + size;
|
||||||
sbuf->refcount = 1;
|
sbuf->refcount = 1;
|
||||||
@ -87,7 +92,8 @@ SHARED_BUF *sbuf;
|
|||||||
rval->hint = NULL;
|
rval->hint = NULL;
|
||||||
rval->properties = NULL;
|
rval->properties = NULL;
|
||||||
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
|
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
|
||||||
rval->command = 0;
|
rval->gwbuf_info = GWBUF_INFO_NONE;
|
||||||
|
rval->gwbuf_bufobj = NULL;
|
||||||
CHK_GWBUF(rval);
|
CHK_GWBUF(rval);
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -102,11 +108,20 @@ gwbuf_free(GWBUF *buf)
|
|||||||
{
|
{
|
||||||
BUF_PROPERTY *prop;
|
BUF_PROPERTY *prop;
|
||||||
|
|
||||||
|
buffer_object_t* bo;
|
||||||
|
|
||||||
CHK_GWBUF(buf);
|
CHK_GWBUF(buf);
|
||||||
if (atomic_add(&buf->sbuf->refcount, -1) == 1)
|
if (atomic_add(&buf->sbuf->refcount, -1) == 1)
|
||||||
{
|
{
|
||||||
free(buf->sbuf->data);
|
free(buf->sbuf->data);
|
||||||
free(buf->sbuf);
|
free(buf->sbuf);
|
||||||
|
bo = buf->gwbuf_bufobj;
|
||||||
|
|
||||||
|
while (bo != NULL)
|
||||||
|
{
|
||||||
|
bo = gwbuf_remove_buffer_object(buf, bo);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
while (buf->properties)
|
while (buf->properties)
|
||||||
{
|
{
|
||||||
@ -153,6 +168,8 @@ GWBUF *rval;
|
|||||||
rval->gwbuf_type = buf->gwbuf_type;
|
rval->gwbuf_type = buf->gwbuf_type;
|
||||||
rval->properties = NULL;
|
rval->properties = NULL;
|
||||||
rval->hint = NULL;
|
rval->hint = NULL;
|
||||||
|
rval->gwbuf_info = buf->gwbuf_info;
|
||||||
|
rval->gwbuf_bufobj = buf->gwbuf_bufobj;
|
||||||
rval->next = NULL;
|
rval->next = NULL;
|
||||||
CHK_GWBUF(rval);
|
CHK_GWBUF(rval);
|
||||||
return rval;
|
return rval;
|
||||||
@ -181,6 +198,8 @@ GWBUF *gwbuf_clone_portion(
|
|||||||
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->properties = NULL;
|
||||||
clonebuf->hint = NULL;
|
clonebuf->hint = NULL;
|
||||||
|
clonebuf->gwbuf_info = buf->gwbuf_info;
|
||||||
|
clonebuf->gwbuf_bufobj = buf->gwbuf_bufobj;
|
||||||
clonebuf->next = NULL;
|
clonebuf->next = NULL;
|
||||||
CHK_GWBUF(clonebuf);
|
CHK_GWBUF(clonebuf);
|
||||||
return clonebuf;
|
return clonebuf;
|
||||||
@ -323,11 +342,13 @@ int rval = 0;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trim bytes form the end of a GWBUF structure
|
* Trim bytes form the end of a GWBUF structure. If the
|
||||||
|
* buffer has n_bytes or less then it will be freed and
|
||||||
|
* NULL will be returned.
|
||||||
*
|
*
|
||||||
* @param buf The buffer to trim
|
* @param buf The buffer to trim
|
||||||
* @param nbytes The number of bytes to trim off
|
* @param n_bytes The number of bytes to trim off
|
||||||
* @return The buffer chain
|
* @return The buffer chain or NULL if buffer has <= n_bytes
|
||||||
*/
|
*/
|
||||||
GWBUF *
|
GWBUF *
|
||||||
gwbuf_trim(GWBUF *buf, unsigned int n_bytes)
|
gwbuf_trim(GWBUF *buf, unsigned int n_bytes)
|
||||||
@ -361,6 +382,90 @@ void gwbuf_set_type(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a buffer object to GWBUF buffer.
|
||||||
|
*
|
||||||
|
* @param buf GWBUF where object is added
|
||||||
|
* @param id Type identifier for object
|
||||||
|
* @param data Object data
|
||||||
|
* @param donefun_dp Clean-up function to be executed before buffer is freed.
|
||||||
|
*/
|
||||||
|
void gwbuf_add_buffer_object(
|
||||||
|
GWBUF* buf,
|
||||||
|
bufobj_id_t id,
|
||||||
|
void* data,
|
||||||
|
void (*donefun_fp)(void *))
|
||||||
|
{
|
||||||
|
buffer_object_t** p_b;
|
||||||
|
buffer_object_t* newb;
|
||||||
|
|
||||||
|
CHK_GWBUF(buf);
|
||||||
|
newb = (buffer_object_t *)malloc(sizeof(buffer_object_t));
|
||||||
|
newb->bo_id = id;
|
||||||
|
newb->bo_data = data;
|
||||||
|
newb->bo_donefun_fp = donefun_fp;
|
||||||
|
newb->bo_next = NULL;
|
||||||
|
/** Lock */
|
||||||
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
|
p_b = &buf->gwbuf_bufobj;
|
||||||
|
/** Search the end of the list and add there */
|
||||||
|
while (*p_b != NULL)
|
||||||
|
{
|
||||||
|
p_b = &(*p_b)->bo_next;
|
||||||
|
}
|
||||||
|
*p_b = newb;
|
||||||
|
/** Set flag */
|
||||||
|
buf->gwbuf_info |= GWBUF_INFO_PARSED;
|
||||||
|
/** Unlock */
|
||||||
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search buffer object which matches with the id.
|
||||||
|
*
|
||||||
|
* @param buf GWBUF to be searched
|
||||||
|
* @param id Identifier for the object
|
||||||
|
*
|
||||||
|
* @return Searched buffer object or NULL if not found
|
||||||
|
*/
|
||||||
|
void* gwbuf_get_buffer_object_data(
|
||||||
|
GWBUF* buf,
|
||||||
|
bufobj_id_t id)
|
||||||
|
{
|
||||||
|
buffer_object_t* bo;
|
||||||
|
|
||||||
|
CHK_GWBUF(buf);
|
||||||
|
/** Lock */
|
||||||
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
|
bo = buf->gwbuf_bufobj;
|
||||||
|
|
||||||
|
while (bo != NULL && bo->bo_id != id)
|
||||||
|
{
|
||||||
|
bo = bo->bo_next;
|
||||||
|
}
|
||||||
|
/** Unlock */
|
||||||
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
|
|
||||||
|
return bo->bo_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return pointer to next buffer object or NULL
|
||||||
|
*/
|
||||||
|
static buffer_object_t* gwbuf_remove_buffer_object(
|
||||||
|
GWBUF* buf,
|
||||||
|
buffer_object_t* bufobj)
|
||||||
|
{
|
||||||
|
buffer_object_t* next;
|
||||||
|
|
||||||
|
next = bufobj->bo_next;
|
||||||
|
/** Call corresponding clean-up function to clean buffer object's data */
|
||||||
|
bufobj->bo_donefun_fp(bufobj->bo_data);
|
||||||
|
free(bufobj);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a property to a buffer.
|
* Add a property to a buffer.
|
||||||
@ -380,10 +485,10 @@ BUF_PROPERTY *prop;
|
|||||||
|
|
||||||
prop->name = strdup(name);
|
prop->name = strdup(name);
|
||||||
prop->value = strdup(value);
|
prop->value = strdup(value);
|
||||||
spinlock_acquire(&buf->lock);
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
prop->next = buf->properties;
|
prop->next = buf->properties;
|
||||||
buf->properties = prop;
|
buf->properties = prop;
|
||||||
spinlock_release(&buf->lock);
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,11 +503,11 @@ gwbuf_get_property(GWBUF *buf, char *name)
|
|||||||
{
|
{
|
||||||
BUF_PROPERTY *prop;
|
BUF_PROPERTY *prop;
|
||||||
|
|
||||||
spinlock_acquire(&buf->lock);
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
prop = buf->properties;
|
prop = buf->properties;
|
||||||
while (prop && strcmp(prop->name, name) != 0)
|
while (prop && strcmp(prop->name, name) != 0)
|
||||||
prop = prop->next;
|
prop = prop->next;
|
||||||
spinlock_release(&buf->lock);
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
if (prop)
|
if (prop)
|
||||||
return prop->value;
|
return prop->value;
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -451,7 +556,7 @@ gwbuf_add_hint(GWBUF *buf, HINT *hint)
|
|||||||
{
|
{
|
||||||
HINT *ptr;
|
HINT *ptr;
|
||||||
|
|
||||||
spinlock_acquire(&buf->lock);
|
spinlock_acquire(&buf->gwbuf_lock);
|
||||||
if (buf->hint)
|
if (buf->hint)
|
||||||
{
|
{
|
||||||
ptr = buf->hint;
|
ptr = buf->hint;
|
||||||
@ -463,7 +568,6 @@ HINT *ptr;
|
|||||||
{
|
{
|
||||||
buf->hint = hint;
|
buf->hint = hint;
|
||||||
}
|
}
|
||||||
spinlock_release(&buf->lock);
|
spinlock_release(&buf->gwbuf_lock);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +65,29 @@ static char *config_file = NULL;
|
|||||||
static GATEWAY_CONF gateway;
|
static GATEWAY_CONF gateway;
|
||||||
char *version_string = NULL;
|
char *version_string = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trim whitespace from the front and rear of a string
|
||||||
|
*
|
||||||
|
* @param str String to trim
|
||||||
|
* @return Trimmed string, changes are done in situ
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
trim(char *str)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
while (isspace(*str))
|
||||||
|
str++;
|
||||||
|
|
||||||
|
/* Point to last character of the string */
|
||||||
|
ptr = str + strlen(str) - 1;
|
||||||
|
while (ptr > str && isspace(*ptr))
|
||||||
|
*ptr-- = 0;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config item handler for the ini file reader
|
* Config item handler for the ini file reader
|
||||||
*
|
*
|
||||||
@ -508,7 +531,7 @@ int error_count = 0;
|
|||||||
CONFIG_CONTEXT *obj1 = context;
|
CONFIG_CONTEXT *obj1 = context;
|
||||||
while (obj1)
|
while (obj1)
|
||||||
{
|
{
|
||||||
if (strcmp(s, obj1->object) == 0 &&
|
if (strcmp(trim(s), obj1->object) == 0 &&
|
||||||
obj->element && obj1->element)
|
obj->element && obj1->element)
|
||||||
{
|
{
|
||||||
serviceAddBackend(
|
serviceAddBackend(
|
||||||
|
@ -187,14 +187,6 @@ getUsers(SERVICE *service, struct users *users)
|
|||||||
if (service_user == NULL || service_passwd == NULL)
|
if (service_user == NULL || service_passwd == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/** multi-thread environment requires that thread init succeeds. */
|
|
||||||
if (mysql_thread_init()) {
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
|
||||||
LOGFILE_ERROR,
|
|
||||||
"Error : mysql_thread_init failed.")));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
con = mysql_init(NULL);
|
con = mysql_init(NULL);
|
||||||
|
|
||||||
if (con == NULL) {
|
if (con == NULL) {
|
||||||
@ -388,10 +380,9 @@ getUsers(SERVICE *service, struct users *users)
|
|||||||
memcpy(users->cksum, hash, SHA_DIGEST_LENGTH);
|
memcpy(users->cksum, hash, SHA_DIGEST_LENGTH);
|
||||||
|
|
||||||
free(users_data);
|
free(users_data);
|
||||||
|
free(key.user);
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
mysql_close(con);
|
mysql_close(con);
|
||||||
mysql_thread_end();
|
|
||||||
|
|
||||||
return total_users;
|
return total_users;
|
||||||
}
|
}
|
||||||
|
@ -978,9 +978,10 @@ int below_water;
|
|||||||
}
|
}
|
||||||
if (dolog)
|
if (dolog)
|
||||||
{
|
{
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LD, (skygw_log_write(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_DEBUG,
|
||||||
"Error : Writing to %s socket failed due %d, %s.",
|
"%lu [dcb_write] Writing to %s socket failed due %d, %s.",
|
||||||
|
pthread_self(),
|
||||||
dcb_isclient(dcb) ? "client" : "backend server",
|
dcb_isclient(dcb) ? "client" : "backend server",
|
||||||
saved_errno,
|
saved_errno,
|
||||||
strerror(saved_errno))));
|
strerror(saved_errno))));
|
||||||
@ -1703,14 +1704,15 @@ int gw_write(
|
|||||||
/**
|
/**
|
||||||
* Add a callback
|
* Add a callback
|
||||||
*
|
*
|
||||||
* Duplicate registrations are not allowed, therefore an error will be returned if
|
* Duplicate registrations are not allowed, therefore an error will be
|
||||||
* the specific function, reason and userdata triple are already registered.
|
* returned if the specific function, reason and userdata triple
|
||||||
|
* are already registered.
|
||||||
* An error will also be returned if the is insufficient memeory available to
|
* An error will also be returned if the is insufficient memeory available to
|
||||||
* create the registration.
|
* create the registration.
|
||||||
*
|
*
|
||||||
* @param dcb The DCB to add the callback to
|
* @param dcb The DCB to add the callback to
|
||||||
* @param reason The callback reason
|
* @param reason The callback reason
|
||||||
* @param cb The callback function to call
|
* @param callback The callback function to call
|
||||||
* @param userdata User data to send in the call
|
* @param userdata User data to send in the call
|
||||||
* @return Non-zero (true) if the callback was added
|
* @return Non-zero (true) if the callback was added
|
||||||
*/
|
*/
|
||||||
@ -1766,7 +1768,7 @@ int rval = 1;
|
|||||||
*
|
*
|
||||||
* @param dcb The DCB to add the callback to
|
* @param dcb The DCB to add the callback to
|
||||||
* @param reason The callback reason
|
* @param reason The callback reason
|
||||||
* @param cb The callback function to call
|
* @param callback The callback function to call
|
||||||
* @param userdata User data to send in the call
|
* @param userdata User data to send in the call
|
||||||
* @return Non-zero (true) if the callback was removed
|
* @return Non-zero (true) if the callback was removed
|
||||||
*/
|
*/
|
||||||
@ -1839,7 +1841,7 @@ DCB_CALLBACK *cb, *nextcb;
|
|||||||
/**
|
/**
|
||||||
* Check the passed DCB to ensure it is in the list of allDCBS
|
* Check the passed DCB to ensure it is in the list of allDCBS
|
||||||
*
|
*
|
||||||
* @param DCB The DCB to check
|
* @param dcb The DCB to check
|
||||||
* @return 1 if the DCB is in the list, otherwise 0
|
* @return 1 if the DCB is in the list, otherwise 0
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
@ -1936,8 +1938,8 @@ void dcb_call_foreach (
|
|||||||
* Null protocol write routine used for cloned dcb's. It merely consumes
|
* Null protocol write routine used for cloned dcb's. It merely consumes
|
||||||
* buffers written on the cloned DCB.
|
* buffers written on the cloned DCB.
|
||||||
*
|
*
|
||||||
* @params dcb The descriptor control block
|
* @param dcb The descriptor control block
|
||||||
* @params buf The buffer beign written
|
* @param buf The buffer being written
|
||||||
* @return Always returns a good write operation result
|
* @return Always returns a good write operation result
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
@ -39,8 +39,8 @@
|
|||||||
|
|
||||||
extern int lm_enabled_logfiles_bitmask;
|
extern int lm_enabled_logfiles_bitmask;
|
||||||
|
|
||||||
static SPINLOCK filter_spin = SPINLOCK_INIT;
|
static SPINLOCK filter_spin = SPINLOCK_INIT; /**< Protects the list of all filters */
|
||||||
static FILTER_DEF *allFilters = NULL;
|
static FILTER_DEF *allFilters = NULL; /**< The list of all filters */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a new filter within MaxScale
|
* Allocate a new filter within MaxScale
|
||||||
@ -79,7 +79,7 @@ FILTER_DEF *filter;
|
|||||||
/**
|
/**
|
||||||
* Deallocate the specified filter
|
* Deallocate the specified filter
|
||||||
*
|
*
|
||||||
* @param server The service to deallocate
|
* @param filter The filter to deallocate
|
||||||
* @return Returns true if the server was freed
|
* @return Returns true if the server was freed
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
@ -243,8 +243,8 @@ int i;
|
|||||||
/**
|
/**
|
||||||
* Add a router option to a service
|
* Add a router option to a service
|
||||||
*
|
*
|
||||||
* @param service The service to add the router option to
|
* @param filter The filter to add the option to
|
||||||
* @param option The option string
|
* @param option The option string
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
filterAddOption(FILTER_DEF *filter, char *option)
|
filterAddOption(FILTER_DEF *filter, char *option)
|
||||||
@ -273,7 +273,7 @@ int i;
|
|||||||
/**
|
/**
|
||||||
* Add a router parameter to a service
|
* Add a router parameter to a service
|
||||||
*
|
*
|
||||||
* @param service The service to add the router option to
|
* @param filter The filter to add the parameter to
|
||||||
* @param name The parameter name
|
* @param name The parameter name
|
||||||
* @param value The parameter value
|
* @param value The parameter value
|
||||||
*/
|
*/
|
||||||
@ -318,6 +318,9 @@ filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream)
|
|||||||
{
|
{
|
||||||
DOWNSTREAM *me;
|
DOWNSTREAM *me;
|
||||||
|
|
||||||
|
if (filter == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (filter->obj == NULL)
|
if (filter->obj == NULL)
|
||||||
{
|
{
|
||||||
/* Filter not yet loaded */
|
/* Filter not yet loaded */
|
||||||
@ -359,7 +362,7 @@ DOWNSTREAM *me;
|
|||||||
UPSTREAM *
|
UPSTREAM *
|
||||||
filterUpstream(FILTER_DEF *filter, void *fsession, UPSTREAM *upstream)
|
filterUpstream(FILTER_DEF *filter, void *fsession, UPSTREAM *upstream)
|
||||||
{
|
{
|
||||||
UPSTREAM *me;
|
UPSTREAM *me = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The the filter has no setUpstream entry point then is does
|
* The the filter has no setUpstream entry point then is does
|
||||||
|
@ -1339,11 +1339,16 @@ int main(int argc, char **argv)
|
|||||||
/* Init MaxScale poll system */
|
/* Init MaxScale poll system */
|
||||||
poll_init();
|
poll_init();
|
||||||
|
|
||||||
/*<
|
/**
|
||||||
* Start the services that were created above
|
* Init mysql thread context for main thread as well. Needed when users
|
||||||
*/
|
* are queried from backends.
|
||||||
|
*/
|
||||||
|
mysql_thread_init();
|
||||||
|
|
||||||
|
/** Start the services that were created above */
|
||||||
n_services = serviceStartAll();
|
n_services = serviceStartAll();
|
||||||
if (n_services == 0)
|
|
||||||
|
if (n_services == 0)
|
||||||
{
|
{
|
||||||
char* logerr = "Failed to start any MaxScale services. Exiting.";
|
char* logerr = "Failed to start any MaxScale services. Exiting.";
|
||||||
print_log_n_stderr(true, !daemon_mode, logerr, logerr, 0);
|
print_log_n_stderr(true, !daemon_mode, logerr, logerr, 0);
|
||||||
@ -1396,9 +1401,13 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
/*< Stop all the monitors */
|
/*< Stop all the monitors */
|
||||||
monitorStopAll();
|
monitorStopAll();
|
||||||
|
|
||||||
LOGIF(LM, (skygw_log_write(
|
LOGIF(LM, (skygw_log_write(
|
||||||
LOGFILE_MESSAGE,
|
LOGFILE_MESSAGE,
|
||||||
"MaxScale is shutting down.")));
|
"MaxScale is shutting down.")));
|
||||||
|
/** Release mysql thread context*/
|
||||||
|
mysql_thread_end();
|
||||||
|
|
||||||
datadir_cleanup();
|
datadir_cleanup();
|
||||||
LOGIF(LM, (skygw_log_write(
|
LOGIF(LM, (skygw_log_write(
|
||||||
LOGFILE_MESSAGE,
|
LOGFILE_MESSAGE,
|
||||||
|
@ -63,6 +63,10 @@ static void hashtable_read_lock(HASHTABLE *table);
|
|||||||
static void hashtable_read_unlock(HASHTABLE *table);
|
static void hashtable_read_unlock(HASHTABLE *table);
|
||||||
static void hashtable_write_lock(HASHTABLE *table);
|
static void hashtable_write_lock(HASHTABLE *table);
|
||||||
static void hashtable_write_unlock(HASHTABLE *table);
|
static void hashtable_write_unlock(HASHTABLE *table);
|
||||||
|
static HASHTABLE *hashtable_alloc_real(HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special null function used as default memory allfunctions in the hashtable
|
* Special null function used as default memory allfunctions in the hashtable
|
||||||
@ -89,14 +93,42 @@ nullfn(void *data)
|
|||||||
HASHTABLE *
|
HASHTABLE *
|
||||||
hashtable_alloc(int size, int (*hashfn)(), int (*cmpfn)())
|
hashtable_alloc(int size, int (*hashfn)(), int (*cmpfn)())
|
||||||
{
|
{
|
||||||
HASHTABLE *rval;
|
return hashtable_alloc_real(NULL, size, hashfn, cmpfn);
|
||||||
|
}
|
||||||
|
|
||||||
if ((rval = malloc(sizeof(HASHTABLE))) == NULL)
|
HASHTABLE* hashtable_alloc_flat(
|
||||||
return NULL;
|
HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)())
|
||||||
|
{
|
||||||
|
return hashtable_alloc_real(target, size, hashfn, cmpfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HASHTABLE *
|
||||||
|
hashtable_alloc_real(
|
||||||
|
HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)())
|
||||||
|
{
|
||||||
|
HASHTABLE *rval;
|
||||||
|
|
||||||
|
if (target == NULL)
|
||||||
|
{
|
||||||
|
if ((rval = malloc(sizeof(HASHTABLE))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
rval->ht_isflat = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rval = target;
|
||||||
|
rval->ht_isflat = true;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
rval->ht_chk_top = CHK_NUM_HASHTABLE;
|
rval->ht_chk_top = CHK_NUM_HASHTABLE;
|
||||||
rval->ht_chk_tail = CHK_NUM_HASHTABLE;
|
rval->ht_chk_tail = CHK_NUM_HASHTABLE;
|
||||||
#endif
|
#endif
|
||||||
rval->hashsize = size;
|
rval->hashsize = size;
|
||||||
rval->hashfn = hashfn;
|
rval->hashfn = hashfn;
|
||||||
@ -143,7 +175,11 @@ HASHENTRIES *entry, *ptr;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(table->entries);
|
free(table->entries);
|
||||||
free(table);
|
|
||||||
|
if (!table->ht_isflat)
|
||||||
|
{
|
||||||
|
free(table);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -281,6 +281,7 @@ MODULES *mod = registered;
|
|||||||
* @param dlhandle The handle returned by dlopen
|
* @param dlhandle The handle returned by dlopen
|
||||||
* @param version The version string returned by the module
|
* @param version The version string returned by the module
|
||||||
* @param modobj The module object
|
* @param modobj The module object
|
||||||
|
* @param mod_info The module information
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
register_module(const char *module, const char *type, void *dlhandle, char *version, void *modobj, MODULE_INFO *mod_info)
|
register_module(const char *module, const char *type, void *dlhandle, char *version, void *modobj, MODULE_INFO *mod_info)
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
* Encrypt a password for storing in the MaxScale.cnf file
|
* Encrypt a password for storing in the MaxScale.cnf file
|
||||||
*
|
*
|
||||||
* @param argc Argument count
|
* @param argc Argument count
|
||||||
* @param arv Argument vector
|
* @param argv Argument vector
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <buffer.h>
|
#include <buffer.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <mysql_client_server_protocol.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a GWBUF structure is a MySQL COM_QUERY packet
|
* Check if a GWBUF structure is a MySQL COM_QUERY packet
|
||||||
@ -171,3 +172,57 @@ GWBUF *addition;
|
|||||||
|
|
||||||
return orig;
|
return orig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy query string from GWBUF buffer to separate memory area.
|
||||||
|
*
|
||||||
|
* @param buf GWBUF buffer including the query
|
||||||
|
*
|
||||||
|
* @return Plaint text query if the packet type is COM_QUERY. Otherwise return
|
||||||
|
* a string including the packet type.
|
||||||
|
*/
|
||||||
|
char* modutil_get_query(
|
||||||
|
GWBUF* buf)
|
||||||
|
{
|
||||||
|
uint8_t* packet;
|
||||||
|
mysql_server_cmd_t packet_type;
|
||||||
|
size_t len;
|
||||||
|
char* query_str;
|
||||||
|
|
||||||
|
packet = GWBUF_DATA(buf);
|
||||||
|
packet_type = packet[4];
|
||||||
|
|
||||||
|
switch (packet_type) {
|
||||||
|
case MYSQL_COM_QUIT:
|
||||||
|
len = strlen("[Quit msg]")+1;
|
||||||
|
if ((query_str = (char *)malloc(len+1)) == NULL)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
memcpy(query_str, "[Quit msg]", len);
|
||||||
|
memset(&query_str[len], 0, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MYSQL_COM_QUERY:
|
||||||
|
len = MYSQL_GET_PACKET_LEN(packet)-1; /*< distract 1 for packet type byte */
|
||||||
|
if ((query_str = (char *)malloc(len+1)) == NULL)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
memcpy(query_str, &packet[5], len);
|
||||||
|
memset(&query_str[len], 0, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
len = strlen(STRPACKETTYPE(packet_type))+1;
|
||||||
|
if ((query_str = (char *)malloc(len+1)) == NULL)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
memcpy(query_str, STRPACKETTYPE(packet_type), len);
|
||||||
|
memset(&query_str[len], 0, 1);
|
||||||
|
break;
|
||||||
|
} /*< switch */
|
||||||
|
retblock:
|
||||||
|
return query_str;
|
||||||
|
}
|
@ -207,7 +207,8 @@ MONITOR *ptr;
|
|||||||
/**
|
/**
|
||||||
* Show a single monitor
|
* Show a single monitor
|
||||||
*
|
*
|
||||||
* @param dcb DCB for printing output
|
* @param dcb DCB for printing output
|
||||||
|
* @param monitor The monitor to print information regarding
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
monitorShow(DCB *dcb, MONITOR *monitor)
|
monitorShow(DCB *dcb, MONITOR *monitor)
|
||||||
@ -303,7 +304,7 @@ monitorSetInterval (MONITOR *mon, unsigned long interval)
|
|||||||
* Enable Replication Heartbeat support in monitor.
|
* Enable Replication Heartbeat support in monitor.
|
||||||
*
|
*
|
||||||
* @param mon The monitor instance
|
* @param mon The monitor instance
|
||||||
* @param interval The sampling interval in milliseconds
|
* @param replication_heartbeat The replication heartbeat
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
monitorSetReplicationHeartbeat(MONITOR *mon, int replication_heartbeat)
|
monitorSetReplicationHeartbeat(MONITOR *mon, int replication_heartbeat)
|
||||||
@ -312,28 +313,3 @@ monitorSetReplicationHeartbeat(MONITOR *mon, int replication_heartbeat)
|
|||||||
mon->module->replicationHeartbeat(mon->handle, replication_heartbeat);
|
mon->module->replicationHeartbeat(mon->handle, replication_heartbeat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over the monitors, calling a function per call
|
|
||||||
*
|
|
||||||
* @param fcn The function to call
|
|
||||||
* @param data The data to pass to each call
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
monitorIterate(void (*fcn)(MONITOR *, void *), void *data)
|
|
||||||
{
|
|
||||||
MONITOR *monitor, *next;
|
|
||||||
|
|
||||||
spinlock_acquire(&monLock);
|
|
||||||
monitor = allMonitors;
|
|
||||||
while (monitor)
|
|
||||||
{
|
|
||||||
next = monitor->next;
|
|
||||||
spinlock_release(&monLock);
|
|
||||||
(*fcn)(monitor, data);
|
|
||||||
spinlock_acquire(&monLock);
|
|
||||||
monitor = next;
|
|
||||||
}
|
|
||||||
spinlock_release(&monLock);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -252,8 +252,10 @@ poll_waitevents(void *arg)
|
|||||||
static bool process_zombies_only = false; /*< flag for all threads */
|
static bool process_zombies_only = false; /*< flag for all threads */
|
||||||
DCB *zombies = NULL;
|
DCB *zombies = NULL;
|
||||||
|
|
||||||
/* Add this thread to the bitmask of running polling threads */
|
/** Add this thread to the bitmask of running polling threads */
|
||||||
bitmask_set(&poll_mask, thread_id);
|
bitmask_set(&poll_mask, thread_id);
|
||||||
|
/** Init mysql thread context for use with a mysql handle and a parser */
|
||||||
|
mysql_thread_init();
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@ -495,6 +497,8 @@ poll_waitevents(void *arg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} /*< while(1) */
|
} /*< while(1) */
|
||||||
|
/** Release mysql thread context */
|
||||||
|
mysql_thread_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,8 +148,7 @@ server_set_unique_name(SERVER *server, char *name)
|
|||||||
* Find an existing server using the unique section name in
|
* Find an existing server using the unique section name in
|
||||||
* configuration file
|
* configuration file
|
||||||
*
|
*
|
||||||
* @param servname The Server name or address
|
* @param name The Server name defined in the header file
|
||||||
* @param port The server port
|
|
||||||
* @return The server or NULL if not found
|
* @return The server or NULL if not found
|
||||||
*/
|
*/
|
||||||
SERVER *
|
SERVER *
|
||||||
@ -563,28 +562,3 @@ SERVER_PARAM *param = server->parameters;
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over the servers, calling a function per call
|
|
||||||
*
|
|
||||||
* @param fcn The function to call
|
|
||||||
* @param data The data to pass to each call
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
serverIterate(void (*fcn)(SERVER *, void *), void *data)
|
|
||||||
{
|
|
||||||
SERVER *server, *next;
|
|
||||||
|
|
||||||
spinlock_acquire(&server_spin);
|
|
||||||
server = allServers;
|
|
||||||
while (server)
|
|
||||||
{
|
|
||||||
next = server->next;
|
|
||||||
spinlock_release(&server_spin);
|
|
||||||
(*fcn)(server, data);
|
|
||||||
spinlock_acquire(&server_spin);
|
|
||||||
server = next;
|
|
||||||
}
|
|
||||||
spinlock_release(&server_spin);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -675,6 +675,7 @@ int n = 0;
|
|||||||
"Unable to find filter '%s' for service '%s'\n",
|
"Unable to find filter '%s' for service '%s'\n",
|
||||||
trim(ptr), service->name
|
trim(ptr), service->name
|
||||||
)));
|
)));
|
||||||
|
n--;
|
||||||
}
|
}
|
||||||
flist[n] = NULL;
|
flist[n] = NULL;
|
||||||
ptr = strtok_r(NULL, "|", &brkt);
|
ptr = strtok_r(NULL, "|", &brkt);
|
||||||
@ -1008,7 +1009,7 @@ bool service_set_param_value (
|
|||||||
{
|
{
|
||||||
char* p;
|
char* p;
|
||||||
int valint;
|
int valint;
|
||||||
bool succp;
|
bool succp = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find out whether the value is numeric and ends with '%' or '\0'
|
* Find out whether the value is numeric and ends with '%' or '\0'
|
||||||
@ -1171,28 +1172,3 @@ serviceGetWeightingParameter(SERVICE *service)
|
|||||||
{
|
{
|
||||||
return service->weightby;
|
return service->weightby;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over the services, calling a function per call
|
|
||||||
*
|
|
||||||
* @param fcn The function to call
|
|
||||||
* @param data The data to pass to each call
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
serviceIterate(void (*fcn)(SERVICE *, void *), void *data)
|
|
||||||
{
|
|
||||||
SERVICE *service, *next;
|
|
||||||
|
|
||||||
spinlock_acquire(&service_spin);
|
|
||||||
service = allServices;
|
|
||||||
while (service)
|
|
||||||
{
|
|
||||||
next = service->next;
|
|
||||||
spinlock_release(&service_spin);
|
|
||||||
(*fcn)(service, data);
|
|
||||||
spinlock_acquire(&service_spin);
|
|
||||||
service = next;
|
|
||||||
}
|
|
||||||
spinlock_release(&service_spin);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -333,13 +333,15 @@ bool session_free(
|
|||||||
{
|
{
|
||||||
for (i = 0; i < session->n_filters; i++)
|
for (i = 0; i < session->n_filters; i++)
|
||||||
{
|
{
|
||||||
session->filters[i].filter->obj->closeSession(
|
if (session->filters[i].filter)
|
||||||
|
session->filters[i].filter->obj->closeSession(
|
||||||
session->filters[i].instance,
|
session->filters[i].instance,
|
||||||
session->filters[i].session);
|
session->filters[i].session);
|
||||||
}
|
}
|
||||||
for (i = 0; i < session->n_filters; i++)
|
for (i = 0; i < session->n_filters; i++)
|
||||||
{
|
{
|
||||||
session->filters[i].filter->obj->freeSession(
|
if (session->filters[i].filter)
|
||||||
|
session->filters[i].filter->obj->freeSession(
|
||||||
session->filters[i].instance,
|
session->filters[i].instance,
|
||||||
session->filters[i].session);
|
session->filters[i].session);
|
||||||
}
|
}
|
||||||
@ -653,6 +655,14 @@ int i;
|
|||||||
session->n_filters = service->n_filters;
|
session->n_filters = service->n_filters;
|
||||||
for (i = service->n_filters - 1; i >= 0; i--)
|
for (i = service->n_filters - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
if (service->filters[i] == NULL)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Service '%s' contians an unresolved filter.\n",
|
||||||
|
service->name)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if ((head = filterApply(service->filters[i], session,
|
if ((head = filterApply(service->filters[i], session,
|
||||||
&session->head)) == NULL)
|
&session->head)) == NULL)
|
||||||
{
|
{
|
||||||
@ -757,28 +767,3 @@ session_getUser(SESSION *session)
|
|||||||
{
|
{
|
||||||
return (session && session->client) ? session->client->user : NULL;
|
return (session && session->client) ? session->client->user : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over the sessions, calling a function per call
|
|
||||||
*
|
|
||||||
* @param fcn The function to call
|
|
||||||
* @param data The data to pass to each call
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
sessionIterate(void (*fcn)(SESSION *, void *), void *data)
|
|
||||||
{
|
|
||||||
SESSION *session, *next;
|
|
||||||
|
|
||||||
spinlock_acquire(&session_spin);
|
|
||||||
session = allSessions;
|
|
||||||
while (session)
|
|
||||||
{
|
|
||||||
next = session->next;
|
|
||||||
spinlock_release(&session_spin);
|
|
||||||
(*fcn)(session, data);
|
|
||||||
spinlock_acquire(&session_spin);
|
|
||||||
session = next;
|
|
||||||
}
|
|
||||||
spinlock_release(&session_spin);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -8,7 +8,20 @@ include ../../../makefile.inc
|
|||||||
include ../../../test.inc
|
include ../../../test.inc
|
||||||
|
|
||||||
CC=cc
|
CC=cc
|
||||||
TESTLOG := $(shell pwd)/testhash.log
|
TESTLOG := $(shell pwd)/testcore.log
|
||||||
|
|
||||||
|
LOGPATH := $(ROOT_PATH)/log_manager
|
||||||
|
UTILSPATH := $(ROOT_PATH)/utils
|
||||||
|
|
||||||
|
LDFLAGS=-rdynamic -L$(LOGPATH) \
|
||||||
|
-Wl,-rpath,$(DEST)/lib \
|
||||||
|
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) \
|
||||||
|
-Wl,-rpath,$(EMBEDDED_LIB)
|
||||||
|
|
||||||
|
LIBS= -lz -lm -lcrypt -lcrypto -ldl -laio -lrt -pthread -llog_manager \
|
||||||
|
-L../../inih/extra -linih -lssl -lstdc++
|
||||||
|
|
||||||
|
TESTS=testhash testspinlock testfilter testadminusers
|
||||||
|
|
||||||
cleantests:
|
cleantests:
|
||||||
- $(DEL) *.o
|
- $(DEL) *.o
|
||||||
@ -20,19 +33,41 @@ testall:
|
|||||||
$(MAKE) DEBUG=Y buildtests
|
$(MAKE) DEBUG=Y buildtests
|
||||||
$(MAKE) runtests
|
$(MAKE) runtests
|
||||||
|
|
||||||
buildtests :
|
buildtests : $(TESTS)
|
||||||
|
|
||||||
|
testhash: testhash.c
|
||||||
$(CC) $(CFLAGS) \
|
$(CC) $(CFLAGS) \
|
||||||
-I$(ROOT_PATH)/server/include \
|
-I$(ROOT_PATH)/server/include \
|
||||||
-I$(ROOT_PATH)/utils \
|
-I$(ROOT_PATH)/utils \
|
||||||
testhash.c ../hashtable.o ../atomic.o ../spinlock.o -o testhash
|
testhash.c ../hashtable.o ../atomic.o ../spinlock.o -o testhash
|
||||||
|
|
||||||
runtests:
|
testspinlock: testspinlock.c
|
||||||
|
$(CC) $(CFLAGS) \
|
||||||
|
-I$(ROOT_PATH)/server/include \
|
||||||
|
-I$(ROOT_PATH)/utils \
|
||||||
|
testspinlock.c ../spinlock.o ../atomic.o ../thread.o -o testspinlock
|
||||||
|
|
||||||
|
testfilter: testfilter.c libcore.a
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) \
|
||||||
|
-I$(ROOT_PATH)/server/include \
|
||||||
|
-I$(ROOT_PATH)/utils \
|
||||||
|
testfilter.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testfilter
|
||||||
|
|
||||||
|
testadminusers: testadminusers.c libcore.a
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) \
|
||||||
|
-I$(ROOT_PATH)/server/include \
|
||||||
|
-I$(ROOT_PATH)/utils \
|
||||||
|
testadminusers.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testadminusers
|
||||||
|
|
||||||
|
libcore.a: ../*.o
|
||||||
|
ar rv libcore.a ../*.o
|
||||||
|
|
||||||
|
runtests: $(TESTS)
|
||||||
@echo "" > $(TESTLOG)
|
@echo "" > $(TESTLOG)
|
||||||
@echo "-------------------------------" >> $(TESTLOG)
|
@echo "-------------------------------" >> $(TESTLOG)
|
||||||
@echo $(shell date) >> $(TESTLOG)
|
@echo $(shell date) >> $(TESTLOG)
|
||||||
@echo "Test MaxScale core" >> $(TESTLOG)
|
@echo "Test MaxScale core" >> $(TESTLOG)
|
||||||
@echo "-------------------------------" >> $(TESTLOG)
|
@echo "-------------------------------" >> $(TESTLOG)
|
||||||
@-./testhash 2>> $(TESTLOG)
|
$(foreach var,$(TESTS),./runtest.sh $(var) $(TESTLOG);)
|
||||||
if [ "$$?" == "0" ];then echo "MaxScale core PASSED">> $(TESTLOG);else echo "MaxScale core FAILED">> $(TESTLOG);fi
|
|
||||||
@echo "" >> $(TESTLOG)
|
@echo "" >> $(TESTLOG)
|
||||||
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
|
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
|
||||||
|
10
server/core/test/runtest.sh
Executable file
10
server/core/test/runtest.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
test=$1
|
||||||
|
log=$2
|
||||||
|
echo Running test $test >> $log
|
||||||
|
./$test 2>> $log
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo $test " " FAILED >> $log
|
||||||
|
else
|
||||||
|
echo $test " " PASSED >> $log
|
||||||
|
fi
|
278
server/core/test/testadminusers.c
Normal file
278
server/core/test/testadminusers.c
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 20-08-2014 Mark Riddoch Initial implementation
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <adminusers.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test1 default user
|
||||||
|
*
|
||||||
|
* Test that the username password admin/skysql is accepted if no users
|
||||||
|
* have been created and that no other users are accepted
|
||||||
|
*
|
||||||
|
* WARNING: $MAXSCALE_HOME/etc/passwd must be removed before this test is run
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test1()
|
||||||
|
{
|
||||||
|
if (admin_verify("admin", "skysql") == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 1.1 (default user) failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (admin_verify("bad", "user"))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 1.2 (wrong user) failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test2 creating users
|
||||||
|
*
|
||||||
|
* Create a user
|
||||||
|
* Try to create a duplicate user - expects a failure
|
||||||
|
* Remove that user - expected to fail as one user must always remain
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test2()
|
||||||
|
{
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if ((err = admin_add_user("user0", "passwd0")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 2.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (admin_add_user("user0", "passwd0") == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 2.2 (add user) failed, du;plicate.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deleting the last user is forbidden so we expect this to fail */
|
||||||
|
if ((err = admin_remove_user("user0", "passwd0")) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 2.3 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test3 search/verify users
|
||||||
|
*
|
||||||
|
* Create a user
|
||||||
|
* Search for that user
|
||||||
|
* Search for a non-existant user
|
||||||
|
* Remove the user
|
||||||
|
* Search for the user that was removed
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test3()
|
||||||
|
{
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if ((err = admin_add_user("user1", "passwd1")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 3.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admin_search_user("user1") == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 3.2 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (admin_search_user("user2") != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 3.3 (search user) failed, unexpeted user found.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = admin_remove_user("user1", "passwd1")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 3.4 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admin_search_user("user1"))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 3.5 (search user) failed - user was deleted.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test4 verify users
|
||||||
|
*
|
||||||
|
* Create a numebr of users
|
||||||
|
* search for each user in turn
|
||||||
|
* verify each user in turn (password verification)
|
||||||
|
* Verify each user in turn with incorrect password
|
||||||
|
* Randomly verify each user
|
||||||
|
* Remove each user
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test4()
|
||||||
|
{
|
||||||
|
char *err, user[40], passwd[40];
|
||||||
|
int i, n_users = 50;
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "passwd%d", i);
|
||||||
|
if ((err = admin_add_user(user, passwd)) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 4.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
if (admin_search_user(user) == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 4.2 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "passwd%d", i);
|
||||||
|
if (admin_verify(user, passwd) == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 4.3 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "badpasswd%d", i);
|
||||||
|
if (admin_verify(user, passwd) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 4.4 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srand(time(0));
|
||||||
|
for (i = 1; i < 1000; i++)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
j = rand() % n_users;
|
||||||
|
if (j == 0)
|
||||||
|
j = 1;
|
||||||
|
sprintf(user, "user%d", j);
|
||||||
|
sprintf(passwd, "passwd%d", j);
|
||||||
|
if (admin_verify(user, passwd) == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 4.5 (random) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "passwd%d", i);
|
||||||
|
if ((err = admin_remove_user(user, passwd)) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 4.6 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test5 remove first user
|
||||||
|
*
|
||||||
|
* Create a user so that user0 may be removed
|
||||||
|
* Remove the first user created (user0)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test5()
|
||||||
|
{
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if ((err = admin_add_user("user", "passwd")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 5.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((err = admin_remove_user("user0", "passwd0")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 5.2 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
result += test1();
|
||||||
|
result += test2();
|
||||||
|
result += test3();
|
||||||
|
result += test4();
|
||||||
|
result += test5();
|
||||||
|
|
||||||
|
exit(result);
|
||||||
|
}
|
||||||
|
|
155
server/core/test/testfilter.c
Normal file
155
server/core/test/testfilter.c
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 19-08-2014 Mark Riddoch Initial implementation
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <filter.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test1 Filter creation, finding and deletion
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test1()
|
||||||
|
{
|
||||||
|
FILTER_DEF *f1, *f2;
|
||||||
|
|
||||||
|
if ((f1 = filter_alloc("test1", "module")) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "filter_alloc: test 1 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((f2 = filter_find("test1")) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "filter_find: test 2 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
filter_free(f1);
|
||||||
|
if ((f2 = filter_find("test1")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "filter_find: test 3 failed delete.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passive tests for filter_add_option and filter_add_parameter
|
||||||
|
*
|
||||||
|
* These tests add options and parameters to a filter, the only failure
|
||||||
|
* is related hard crashes, such as SIGSEGV etc. as there are no good hooks
|
||||||
|
* to check the creation of parameters and options currently.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test2()
|
||||||
|
{
|
||||||
|
FILTER_DEF *f1;
|
||||||
|
|
||||||
|
if ((f1 = filter_alloc("test1", "module")) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "filter_alloc: test 1 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
filterAddOption(f1, "option1");
|
||||||
|
filterAddOption(f1, "option2");
|
||||||
|
filterAddOption(f1, "option3");
|
||||||
|
filterAddParameter(f1, "name1", "value1");
|
||||||
|
filterAddParameter(f1, "name2", "value2");
|
||||||
|
filterAddParameter(f1, "name3", "value3");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test3 Filter creation, finding and deletion soak test
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test3()
|
||||||
|
{
|
||||||
|
FILTER_DEF *f1;
|
||||||
|
char name[40];
|
||||||
|
int i, n_filters = 1000;
|
||||||
|
|
||||||
|
for (i = 0; i < n_filters; i++)
|
||||||
|
{
|
||||||
|
sprintf(name, "filter%d", i);
|
||||||
|
if ((f1 = filter_alloc(name, "module")) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"filter_alloc: test 3 failed with %s.\n", name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < n_filters; i++)
|
||||||
|
{
|
||||||
|
sprintf(name, "filter%d", i);
|
||||||
|
if ((f1 = filter_find(name)) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "filter_find: test 3 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < n_filters; i++)
|
||||||
|
{
|
||||||
|
sprintf(name, "filter%d", i);
|
||||||
|
if ((f1 = filter_find(name)) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "filter_find: test 3 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
filter_free(f1);
|
||||||
|
if ((f1 = filter_find(name)) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"filter_find: test 3 failed - found deleted filter.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
result += test1();
|
||||||
|
result += test2();
|
||||||
|
result += test3();
|
||||||
|
|
||||||
|
exit(result);
|
||||||
|
}
|
||||||
|
|
132
server/core/test/testspinlock.c
Normal file
132
server/core/test/testspinlock.c
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 18/08-2014 Mark Riddoch Initial implementation
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <spinlock.h>
|
||||||
|
#include <thread.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test1 spinlock_acquire_nowait tests
|
||||||
|
*
|
||||||
|
* Test that spinlock_acquire_nowait returns false if the spinlock
|
||||||
|
* is already taken.
|
||||||
|
*
|
||||||
|
* Test that spinlock_acquire_nowait returns true if the spinlock
|
||||||
|
* is not taken.
|
||||||
|
*
|
||||||
|
* Test that spinlock_acquire_nowait does hold the spinlock.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test1()
|
||||||
|
{
|
||||||
|
SPINLOCK lck;
|
||||||
|
|
||||||
|
spinlock_init(&lck);
|
||||||
|
spinlock_acquire(&lck);
|
||||||
|
if (spinlock_acquire_nowait(&lck))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "spinlock_acquire_nowait: test 1 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
spinlock_release(&lck);
|
||||||
|
if (!spinlock_acquire_nowait(&lck))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "spinlock_acquire_nowait: test 2 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (spinlock_acquire_nowait(&lck))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "spinlock_acquire_nowait: test 3 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
spinlock_release(&lck);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acquire_time;
|
||||||
|
|
||||||
|
static void
|
||||||
|
test2_helper(void *data)
|
||||||
|
{
|
||||||
|
SPINLOCK *lck = (SPINLOCK *)data;
|
||||||
|
unsigned long t1 = time(0);
|
||||||
|
|
||||||
|
spinlock_acquire(lck);
|
||||||
|
acquire_time = time(0) - t1;
|
||||||
|
spinlock_release(lck);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that spinlock correctly blocks another thread whilst the spinlock
|
||||||
|
* is held.
|
||||||
|
*
|
||||||
|
* Take out a lock.
|
||||||
|
* Start a second thread to take the same lock
|
||||||
|
* sleep for 10 seconds
|
||||||
|
* release lock
|
||||||
|
* verify that second thread took at least 8 seconds to obtain the lock
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test2()
|
||||||
|
{
|
||||||
|
SPINLOCK lck;
|
||||||
|
void *handle;
|
||||||
|
|
||||||
|
acquire_time = 0;
|
||||||
|
spinlock_init(&lck);
|
||||||
|
spinlock_acquire(&lck);
|
||||||
|
handle = thread_start(test2_helper, (void *)&lck);
|
||||||
|
sleep(10);
|
||||||
|
spinlock_release(&lck);
|
||||||
|
thread_wait(handle);
|
||||||
|
|
||||||
|
if (acquire_time < 8)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "spinlock: test 1 failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
result += test1();
|
||||||
|
result += test2();
|
||||||
|
|
||||||
|
exit(result);
|
||||||
|
}
|
||||||
|
|
@ -204,7 +204,7 @@ void gw_sha1_2_str(const uint8_t *in, int in_len, const uint8_t *in2, int in2_le
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @node Gets errno corresponding to latest socket error
|
* node Gets errno corresponding to latest socket error
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* @param fd - in, use
|
* @param fd - in, use
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
|
#include <dcb.h>
|
||||||
|
|
||||||
#define ADMIN_SALT "MS"
|
#define ADMIN_SALT "MS"
|
||||||
|
|
||||||
extern int admin_verify(char *, char *);
|
extern int admin_verify(char *, char *);
|
||||||
|
@ -43,11 +43,14 @@
|
|||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
#include <spinlock.h>
|
#include <string.h>
|
||||||
#include <skygw_debug.h>
|
#include <skygw_debug.h>
|
||||||
#include <hint.h>
|
#include <hint.h>
|
||||||
|
#include <spinlock.h>
|
||||||
|
|
||||||
|
|
||||||
|
EXTERN_C_BLOCK_BEGIN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffer properties - used to store properties related to the buffer
|
* Buffer properties - used to store properties related to the buffer
|
||||||
* contents. This may be added at any point during the processing of the
|
* contents. This may be added at any point during the processing of the
|
||||||
@ -89,6 +92,35 @@ typedef struct {
|
|||||||
int refcount; /*< Reference count on the buffer */
|
int refcount; /*< Reference count on the buffer */
|
||||||
} SHARED_BUF;
|
} SHARED_BUF;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GWBUF_INFO_NONE = 0x0,
|
||||||
|
GWBUF_INFO_PARSED = 0x1
|
||||||
|
} gwbuf_info_t;
|
||||||
|
|
||||||
|
#define GWBUF_IS_PARSED(b) (b->gwbuf_info & GWBUF_INFO_PARSED)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure for cleaning up memory allocations of structures which are
|
||||||
|
* referred to by GWBUF and deallocated in gwbuf_free but GWBUF doesn't
|
||||||
|
* know what they are.
|
||||||
|
* All functions on the list are executed before freeing memory of GWBUF struct.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GWBUF_PARSING_INFO
|
||||||
|
} bufobj_id_t;
|
||||||
|
|
||||||
|
typedef struct buffer_object_st buffer_object_t;
|
||||||
|
|
||||||
|
struct buffer_object_st {
|
||||||
|
bufobj_id_t bo_id;
|
||||||
|
void* bo_data;
|
||||||
|
void (*bo_donefun_fp)(void *);
|
||||||
|
buffer_object_t* bo_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer structure used by the descriptor control blocks.
|
* The buffer structure used by the descriptor control blocks.
|
||||||
*
|
*
|
||||||
@ -98,14 +130,15 @@ typedef struct {
|
|||||||
* be copied within the gateway.
|
* be copied within the gateway.
|
||||||
*/
|
*/
|
||||||
typedef struct gwbuf {
|
typedef struct gwbuf {
|
||||||
|
SPINLOCK gwbuf_lock;
|
||||||
struct gwbuf *next; /*< Next buffer in a linked chain of buffers */
|
struct gwbuf *next; /*< Next buffer in a linked chain of buffers */
|
||||||
void *start; /*< Start of the valid data */
|
void *start; /*< Start of the valid data */
|
||||||
void *end; /*< First byte after the valid data */
|
void *end; /*< First byte after the valid data */
|
||||||
SHARED_BUF *sbuf; /*< The shared buffer with the real data */
|
SHARED_BUF *sbuf; /*< The shared buffer with the real data */
|
||||||
int command;/*< The command type for the queue */
|
buffer_object_t *gwbuf_bufobj; /*< List of objects referred to by GWBUF */
|
||||||
|
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 */
|
HINT *hint; /*< Hint data for this buffer */
|
||||||
SPINLOCK lock;
|
|
||||||
BUF_PROPERTY *properties; /*< Buffer properties */
|
BUF_PROPERTY *properties; /*< Buffer properties */
|
||||||
} GWBUF;
|
} GWBUF;
|
||||||
|
|
||||||
@ -144,4 +177,14 @@ extern int gwbuf_add_property(GWBUF *buf, char *name, char *value);
|
|||||||
extern char *gwbuf_get_property(GWBUF *buf, char *name);
|
extern char *gwbuf_get_property(GWBUF *buf, char *name);
|
||||||
extern GWBUF *gwbuf_make_contiguous(GWBUF *);
|
extern GWBUF *gwbuf_make_contiguous(GWBUF *);
|
||||||
extern int gwbuf_add_hint(GWBUF *, HINT *);
|
extern int gwbuf_add_hint(GWBUF *, HINT *);
|
||||||
|
|
||||||
|
void gwbuf_add_buffer_object(GWBUF* buf,
|
||||||
|
bufobj_id_t id,
|
||||||
|
void* data,
|
||||||
|
void (*donefun_fp)(void *));
|
||||||
|
void* gwbuf_get_buffer_object_data(GWBUF* buf, bufobj_id_t id);
|
||||||
|
|
||||||
|
EXTERN_C_BLOCK_END
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -145,9 +145,9 @@ typedef enum {
|
|||||||
DCB_STATE_POLLING, /*< Waiting in the poll loop */
|
DCB_STATE_POLLING, /*< Waiting in the poll loop */
|
||||||
DCB_STATE_LISTENING, /*< The DCB is for a listening socket */
|
DCB_STATE_LISTENING, /*< The DCB is for a listening socket */
|
||||||
DCB_STATE_DISCONNECTED, /*< The socket is now closed */
|
DCB_STATE_DISCONNECTED, /*< The socket is now closed */
|
||||||
DCB_STATE_FREED, /*< Memory freed */
|
|
||||||
DCB_STATE_NOPOLLING, /*< Removed from poll mask */
|
DCB_STATE_NOPOLLING, /*< Removed from poll mask */
|
||||||
DCB_STATE_ZOMBIE /*< DCB is no longer active, waiting to free it */
|
DCB_STATE_ZOMBIE, /*< DCB is no longer active, waiting to free it */
|
||||||
|
DCB_STATE_FREED /*< Memory freed */
|
||||||
} dcb_state_t;
|
} dcb_state_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -61,7 +61,7 @@ typedef struct {
|
|||||||
* filter pipline
|
* filter pipline
|
||||||
* routeQuery Called on each query that requires
|
* routeQuery Called on each query that requires
|
||||||
* routing
|
* routing
|
||||||
* clientReply
|
* clientReply Called for each reply packet
|
||||||
* diagnostics Called to force the filter to print
|
* diagnostics Called to force the filter to print
|
||||||
* diagnostic output
|
* diagnostic output
|
||||||
*
|
*
|
||||||
@ -88,21 +88,21 @@ typedef struct filter_object {
|
|||||||
*/
|
*/
|
||||||
#define FILTER_VERSION {1, 1, 0}
|
#define FILTER_VERSION {1, 1, 0}
|
||||||
/**
|
/**
|
||||||
* The definition of a filter form the configuration file.
|
* The definition of a filter from the configuration file.
|
||||||
* This is basically the link between a plugin to load and the
|
* This is basically the link between a plugin to load and the
|
||||||
* optons to pass to that plugin.
|
* optons to pass to that plugin.
|
||||||
*/
|
*/
|
||||||
typedef struct filter_def {
|
typedef struct filter_def {
|
||||||
char *name; /*< The Filter name */
|
char *name; /**< The Filter name */
|
||||||
char *module; /*< The module to load */
|
char *module; /**< The module to load */
|
||||||
char **options; /*< The options set for this filter */
|
char **options; /**< The options set for this filter */
|
||||||
FILTER_PARAMETER
|
FILTER_PARAMETER
|
||||||
**parameters; /*< The filter parameters */
|
**parameters; /**< The filter parameters */
|
||||||
FILTER filter;
|
FILTER filter; /**< The runtime filter */
|
||||||
FILTER_OBJECT *obj;
|
FILTER_OBJECT *obj; /**< The "MODULE_OBJECT" for the filter */
|
||||||
SPINLOCK spin;
|
SPINLOCK spin; /**< Spinlock to protect the filter definition */
|
||||||
struct filter_def
|
struct filter_def
|
||||||
*next; /*< Next filter in the chain of all filters */
|
*next; /**< Next filter in the chain of all filters */
|
||||||
} FILTER_DEF;
|
} FILTER_DEF;
|
||||||
|
|
||||||
FILTER_DEF *filter_alloc(char *, char *);
|
FILTER_DEF *filter_alloc(char *, char *);
|
||||||
|
@ -84,12 +84,17 @@ typedef struct hashtable {
|
|||||||
SPINLOCK spin; /**< Internal spinlock for the hashtable */
|
SPINLOCK spin; /**< Internal spinlock for the hashtable */
|
||||||
int n_readers; /**< Number of clients reading the table */
|
int n_readers; /**< Number of clients reading the table */
|
||||||
int writelock; /**< The table is locked by a writer */
|
int writelock; /**< The table is locked by a writer */
|
||||||
|
bool ht_isflat; /**< Indicates whether hashtable is in stack or heap */
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
skygw_chk_t ht_chk_tail;
|
skygw_chk_t ht_chk_tail;
|
||||||
#endif
|
#endif
|
||||||
} HASHTABLE;
|
} HASHTABLE;
|
||||||
|
|
||||||
extern HASHTABLE *hashtable_alloc(int, int (*hashfn)(), int (*cmpfn)());
|
extern HASHTABLE *hashtable_alloc(int, int (*hashfn)(), int (*cmpfn)());
|
||||||
|
HASHTABLE *hashtable_alloc_flat(HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)());
|
||||||
/**< Allocate a hashtable */
|
/**< Allocate a hashtable */
|
||||||
extern void hashtable_memory_fns(HASHTABLE *, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN);
|
extern void hashtable_memory_fns(HASHTABLE *, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN);
|
||||||
/**< Provide an interface to control key/value memory
|
/**< Provide an interface to control key/value memory
|
||||||
|
@ -36,4 +36,6 @@ extern int modutil_is_SQL(GWBUF *);
|
|||||||
extern int modutil_extract_SQL(GWBUF *, char **, int *);
|
extern int modutil_extract_SQL(GWBUF *, char **, int *);
|
||||||
extern int modutil_MySQL_Query(GWBUF *, char **, int *, int *);
|
extern int modutil_MySQL_Query(GWBUF *, char **, int *, int *);
|
||||||
extern GWBUF *modutil_replace_SQL(GWBUF *, char *);
|
extern GWBUF *modutil_replace_SQL(GWBUF *, char *);
|
||||||
|
char* modutil_get_query(GWBUF* buf);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -110,5 +110,4 @@ extern void monitorList(DCB *);
|
|||||||
extern void monitorSetId(MONITOR *, unsigned long);
|
extern void monitorSetId(MONITOR *, unsigned long);
|
||||||
extern void monitorSetInterval (MONITOR *, unsigned long);
|
extern void monitorSetInterval (MONITOR *, unsigned long);
|
||||||
extern void monitorSetReplicationHeartbeat(MONITOR *, int);
|
extern void monitorSetReplicationHeartbeat(MONITOR *, int);
|
||||||
extern void monitorIterate(void (*fcn)(MONITOR *, void *), void *data);
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -169,5 +169,4 @@ extern void serverAddParameter(SERVER *, char *, char *);
|
|||||||
extern char *serverGetParameter(SERVER *, char *);
|
extern char *serverGetParameter(SERVER *, char *);
|
||||||
extern void server_update(SERVER *, char *, char *, char *);
|
extern void server_update(SERVER *, char *, char *, char *);
|
||||||
extern void server_set_unique_name(SERVER *, char *);
|
extern void server_set_unique_name(SERVER *, char *);
|
||||||
extern void serverIterate(void (*fcn)(SERVER *, void *), void *data);
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -161,7 +161,6 @@ extern void serviceSetFilters(SERVICE *, char *);
|
|||||||
extern int serviceEnableRootUser(SERVICE *, int );
|
extern int serviceEnableRootUser(SERVICE *, int );
|
||||||
extern void serviceWeightBy(SERVICE *, char *);
|
extern void serviceWeightBy(SERVICE *, char *);
|
||||||
extern char *serviceGetWeightingParameter(SERVICE *);
|
extern char *serviceGetWeightingParameter(SERVICE *);
|
||||||
extern void serviceIterate(void (*fcn)(SERVICE *, void *), void *data);
|
|
||||||
extern void service_update(SERVICE *, char *, char *, char *);
|
extern void service_update(SERVICE *, char *, char *, char *);
|
||||||
extern int service_refresh_users(SERVICE *);
|
extern int service_refresh_users(SERVICE *);
|
||||||
extern void printService(SERVICE *);
|
extern void printService(SERVICE *);
|
||||||
|
@ -157,7 +157,6 @@ void dprintAllSessions(struct dcb *);
|
|||||||
void dprintSession(struct dcb *, SESSION *);
|
void dprintSession(struct dcb *, SESSION *);
|
||||||
void dListSessions(struct dcb *);
|
void dListSessions(struct dcb *);
|
||||||
char *session_state(int);
|
char *session_state(int);
|
||||||
void sessionIterate(void (*fcn)(SESSION *, void *), void *data);
|
|
||||||
bool session_link_dcb(SESSION *, struct dcb *);
|
bool session_link_dcb(SESSION *, struct dcb *);
|
||||||
SESSION* get_session_by_router_ses(void* rses);
|
SESSION* get_session_by_router_ses(void* rses);
|
||||||
#endif
|
#endif
|
||||||
|
@ -81,7 +81,7 @@ tags:
|
|||||||
(cd hint; touch depend.mk; make tags)
|
(cd hint; touch depend.mk; make tags)
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@rm -f depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
(cd hint; touch depend.mk; make depend)
|
(cd hint; touch depend.mk; make depend)
|
||||||
|
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @file qlafilter.c - Quary Log All Filter
|
||||||
|
* @verbatim
|
||||||
|
*
|
||||||
* QLA Filter - Query Log All. A primitive query logging filter, simply
|
* QLA Filter - Query Log All. A primitive query logging filter, simply
|
||||||
* used to verify the filter mechanism for downstream filters. All queries
|
* used to verify the filter mechanism for downstream filters. All queries
|
||||||
* that are passed through the filter will be written to file.
|
* that are passed through the filter will be written to file.
|
||||||
@ -33,6 +36,7 @@
|
|||||||
* 11/06/2014 Mark Riddoch Addition of source and match parameters
|
* 11/06/2014 Mark Riddoch Addition of source and match parameters
|
||||||
* 19/06/2014 Mark Riddoch Addition of user parameter
|
* 19/06/2014 Mark Riddoch Addition of user parameter
|
||||||
*
|
*
|
||||||
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -154,6 +158,7 @@ GetModuleObject()
|
|||||||
* within MaxScale.
|
* within MaxScale.
|
||||||
*
|
*
|
||||||
* @param options The options for this filter
|
* @param options The options for this filter
|
||||||
|
* @param params The array of name/value pair parameters for the filter
|
||||||
*
|
*
|
||||||
* @return The instance data for this new instance
|
* @return The instance data for this new instance
|
||||||
*/
|
*/
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
extern int lm_enabled_logfiles_bitmask;
|
extern int lm_enabled_logfiles_bitmask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* regexfilter.c - a very simple regular expression rewrite filter.
|
* @file regexfilter.c - a very simple regular expression rewrite filter.
|
||||||
|
* @verbatim
|
||||||
*
|
*
|
||||||
* A simple regular expression query rewrite filter.
|
* A simple regular expression query rewrite filter.
|
||||||
* Two parameters should be defined in the filter configuration
|
* Two parameters should be defined in the filter configuration
|
||||||
@ -39,6 +40,7 @@ extern int lm_enabled_logfiles_bitmask;
|
|||||||
*
|
*
|
||||||
* Date Who Description
|
* Date Who Description
|
||||||
* 19/06/2014 Mark Riddoch Addition of source and user parameters
|
* 19/06/2014 Mark Riddoch Addition of source and user parameters
|
||||||
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
|
|
||||||
MODULE_INFO info = {
|
MODULE_INFO info = {
|
||||||
@ -132,6 +134,7 @@ GetModuleObject()
|
|||||||
* within MaxScale.
|
* within MaxScale.
|
||||||
*
|
*
|
||||||
* @param options The options for this filter
|
* @param options The options for this filter
|
||||||
|
* @param params The array of name/value pair parameters for the filter
|
||||||
*
|
*
|
||||||
* @return The instance data for this new instance
|
* @return The instance data for this new instance
|
||||||
*/
|
*/
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @file tee.c A filter that splits the processing pipeline in two
|
* @file tee.c A filter that splits the processing pipeline in two
|
||||||
|
* @verbatim
|
||||||
*
|
*
|
||||||
* Conditionally duplicate requests and send the duplicates to another service
|
* Conditionally duplicate requests and send the duplicates to another service
|
||||||
* within MaxScale.
|
* within MaxScale.
|
||||||
@ -41,6 +42,7 @@
|
|||||||
* 20/06/2014 Mark Riddoch Initial implementation
|
* 20/06/2014 Mark Riddoch Initial implementation
|
||||||
* 24/06/2014 Mark Riddoch Addition of support for multi-packet queries
|
* 24/06/2014 Mark Riddoch Addition of support for multi-packet queries
|
||||||
*
|
*
|
||||||
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -162,6 +164,7 @@ GetModuleObject()
|
|||||||
* within MaxScale.
|
* within MaxScale.
|
||||||
*
|
*
|
||||||
* @param options The options for this filter
|
* @param options The options for this filter
|
||||||
|
* @param params The array of name/value pair parameters for the filter
|
||||||
*
|
*
|
||||||
* @return The instance data for this new instance
|
* @return The instance data for this new instance
|
||||||
*/
|
*/
|
||||||
|
@ -396,8 +396,9 @@ void print_help()
|
|||||||
{
|
{
|
||||||
|
|
||||||
printf("\nFilter Test Harness\n\n"
|
printf("\nFilter Test Harness\n\n"
|
||||||
"List of commands:\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n"
|
"List of commands:\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n "
|
||||||
"%-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n"
|
"%-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n %-32s%s\n "
|
||||||
|
"%-32s%s\n %-32s%s\n"
|
||||||
,"help","Prints this help message."
|
,"help","Prints this help message."
|
||||||
,"run","Feeds the contents of the buffer to the filter chain."
|
,"run","Feeds the contents of the buffer to the filter chain."
|
||||||
,"add <filter name>","Loads a filter and appeds it to the end of the chain."
|
,"add <filter name>","Loads a filter and appeds it to the end of the chain."
|
||||||
@ -407,6 +408,8 @@ void print_help()
|
|||||||
,"config <file name>","Loads filter configurations from a file."
|
,"config <file name>","Loads filter configurations from a file."
|
||||||
,"in <file name>","Source file for the SQL statements."
|
,"in <file name>","Source file for the SQL statements."
|
||||||
,"out <file name>","Destination file for the SQL statements. Defaults to stdout if no parameters were passed."
|
,"out <file name>","Destination file for the SQL statements. Defaults to stdout if no parameters were passed."
|
||||||
|
,"threads <number>","Sets the amount of threads to use"
|
||||||
|
,"sessions <number>","How many sessions to create for each filter. This clears all loaded filters."
|
||||||
,"quiet","Print only error messages."
|
,"quiet","Print only error messages."
|
||||||
,"verbose","Print everything."
|
,"verbose","Print everything."
|
||||||
,"exit","Exit the program"
|
,"exit","Exit the program"
|
||||||
@ -490,9 +493,14 @@ FILTER_PARAMETER** read_params(int* paramc)
|
|||||||
int routeQuery(void* ins, void* session, GWBUF* queue)
|
int routeQuery(void* ins, void* session, GWBUF* queue)
|
||||||
{
|
{
|
||||||
|
|
||||||
int buffsz = (int)(queue->end - (queue->start + 5));
|
unsigned int buffsz = 0;
|
||||||
|
unsigned char* ptr = (void*)queue->start;
|
||||||
char *qstr;
|
char *qstr;
|
||||||
|
|
||||||
|
buffsz += *ptr++;
|
||||||
|
buffsz += *ptr++ << 8;
|
||||||
|
buffsz += *ptr++ << 16;
|
||||||
|
|
||||||
if(queue->hint){
|
if(queue->hint){
|
||||||
buffsz += 40;
|
buffsz += 40;
|
||||||
if(queue->hint->data){
|
if(queue->hint->data){
|
||||||
@ -506,7 +514,7 @@ int routeQuery(void* ins, void* session, GWBUF* queue)
|
|||||||
qstr = calloc(buffsz,sizeof(char));
|
qstr = calloc(buffsz,sizeof(char));
|
||||||
|
|
||||||
if(qstr){
|
if(qstr){
|
||||||
memcpy(qstr,queue->start + 5,(int)(queue->end - 1 - (queue->start + 5)));
|
memcpy(qstr,queue->start + 5,buffsz - 1);
|
||||||
if(queue->hint){
|
if(queue->hint){
|
||||||
char *ptr = qstr + (int)(queue->end - 1 - (queue->start + 5));
|
char *ptr = qstr + (int)(queue->end - 1 - (queue->start + 5));
|
||||||
|
|
||||||
@ -621,9 +629,9 @@ void manual_query()
|
|||||||
gwbuf_set_type(instance.buffer[0],GWBUF_TYPE_MYSQL);
|
gwbuf_set_type(instance.buffer[0],GWBUF_TYPE_MYSQL);
|
||||||
memcpy(instance.buffer[0]->sbuf->data + 5,query,qlen);
|
memcpy(instance.buffer[0]->sbuf->data + 5,query,qlen);
|
||||||
|
|
||||||
instance.buffer[0]->sbuf->data[0] = (qlen>>0&1)|(qlen>>1&1) << 1;
|
instance.buffer[0]->sbuf->data[0] = (qlen);
|
||||||
instance.buffer[0]->sbuf->data[1] = (qlen>>2&1)|(qlen>>3&1) << 1;
|
instance.buffer[0]->sbuf->data[1] = (qlen << 8);
|
||||||
instance.buffer[0]->sbuf->data[2] = (qlen>>4&1)|(qlen>>5&1) << 1;
|
instance.buffer[0]->sbuf->data[2] = (qlen << 16);
|
||||||
instance.buffer[0]->sbuf->data[3] = 0x00;
|
instance.buffer[0]->sbuf->data[3] = 0x00;
|
||||||
instance.buffer[0]->sbuf->data[4] = 0x03;
|
instance.buffer[0]->sbuf->data[4] = 0x03;
|
||||||
|
|
||||||
@ -706,9 +714,9 @@ int load_query()
|
|||||||
memcpy(tmpbff[i]->sbuf->data + 5,query_list[i],strnlen(query_list[i],buff_sz));
|
memcpy(tmpbff[i]->sbuf->data + 5,query_list[i],strnlen(query_list[i],buff_sz));
|
||||||
|
|
||||||
qlen = strnlen(query_list[i],buff_sz);
|
qlen = strnlen(query_list[i],buff_sz);
|
||||||
tmpbff[i]->sbuf->data[0] = (qlen>>0&1)|(qlen>>1&1) << 1;
|
tmpbff[i]->sbuf->data[0] = qlen;
|
||||||
tmpbff[i]->sbuf->data[1] = (qlen>>2&1)|(qlen>>3&1) << 1;
|
tmpbff[i]->sbuf->data[1] = (qlen << 8);
|
||||||
tmpbff[i]->sbuf->data[2] = (qlen>>4&1)|(qlen>>5&1) << 1;
|
tmpbff[i]->sbuf->data[2] = (qlen << 16);
|
||||||
tmpbff[i]->sbuf->data[3] = 0x00;
|
tmpbff[i]->sbuf->data[3] = 0x00;
|
||||||
tmpbff[i]->sbuf->data[4] = 0x03;
|
tmpbff[i]->sbuf->data[4] = 0x03;
|
||||||
|
|
||||||
|
@ -21,13 +21,15 @@
|
|||||||
#include <modutil.h>
|
#include <modutil.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* testfilter.c - a very simple test filter.
|
* @file testfilter.c - a very simple test filter.
|
||||||
|
* @verbatim
|
||||||
*
|
*
|
||||||
* This filter is a very simple example used to test the filter API,
|
* This filter is a very simple example used to test the filter API,
|
||||||
* it merely counts the number of statements that flow through the
|
* it merely counts the number of statements that flow through the
|
||||||
* filter pipeline.
|
* filter pipeline.
|
||||||
*
|
*
|
||||||
* Reporting is done via the diagnostics print routine.
|
* Reporting is done via the diagnostics print routine.
|
||||||
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
|
|
||||||
MODULE_INFO info = {
|
MODULE_INFO info = {
|
||||||
@ -114,6 +116,7 @@ GetModuleObject()
|
|||||||
* within MaxScale.
|
* within MaxScale.
|
||||||
*
|
*
|
||||||
* @param options The options for this filter
|
* @param options The options for this filter
|
||||||
|
* @param params The array of name/value pair parameters for the filter
|
||||||
*
|
*
|
||||||
* @return The instance data for this new instance
|
* @return The instance data for this new instance
|
||||||
*/
|
*/
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @file topfilter.c - Top N Longest Running Queries
|
||||||
|
* @verbatim
|
||||||
|
*
|
||||||
* TOPN Filter - Query Log All. A primitive query logging filter, simply
|
* TOPN Filter - Query Log All. A primitive query logging filter, simply
|
||||||
* used to verify the filter mechanism for downstream filters. All queries
|
* used to verify the filter mechanism for downstream filters. All queries
|
||||||
* that are passed through the filter will be written to file.
|
* that are passed through the filter will be written to file.
|
||||||
@ -30,6 +33,8 @@
|
|||||||
*
|
*
|
||||||
* Date Who Description
|
* Date Who Description
|
||||||
* 18/06/2014 Mark Riddoch Addition of source and user filters
|
* 18/06/2014 Mark Riddoch Addition of source and user filters
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -172,6 +177,7 @@ GetModuleObject()
|
|||||||
* within MaxScale.
|
* within MaxScale.
|
||||||
*
|
*
|
||||||
* @param options The options for this filter
|
* @param options The options for this filter
|
||||||
|
* @param params The array of name/value pair parameters for the filter
|
||||||
*
|
*
|
||||||
* @return The instance data for this new instance
|
* @return The instance data for this new instance
|
||||||
*/
|
*/
|
||||||
|
@ -48,20 +48,18 @@
|
|||||||
#define HTTPD_FIELD_MAXLEN 8192
|
#define HTTPD_FIELD_MAXLEN 8192
|
||||||
#define HTTPD_REQUESTLINE_MAXLEN 8192
|
#define HTTPD_REQUESTLINE_MAXLEN 8192
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
METHOD_UNKNOWN = 0,
|
|
||||||
METHOD_POST,
|
|
||||||
METHOD_PUT,
|
|
||||||
METHOD_GET,
|
|
||||||
METHOD_HEAD
|
|
||||||
} HTTP_METHOD;
|
|
||||||
/**
|
/**
|
||||||
* HTTPD session specific data
|
* HTTPD session specific data
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
typedef struct httpd_session {
|
typedef struct httpd_session {
|
||||||
HTTP_METHOD method;
|
char user[HTTPD_USER_MAXLEN]; /*< username for authentication*/
|
||||||
GWBUF *saved;
|
char *cookies; /*< all input cookies */
|
||||||
int request_len;
|
char hostname[HTTPD_HOSTNAME_MAXLEN]; /*< The hostname */
|
||||||
char *url;
|
char useragent[HTTPD_USERAGENT_MAXLEN]; /*< The useragent */
|
||||||
|
char method[HTTPD_METHOD_MAXLEN]; /*< The HTTPD Method */
|
||||||
|
char *url; /*< the URL in the request */
|
||||||
|
char *path_info; /*< the Pathinfo, starts with /, is the extra path segments after the document name */
|
||||||
|
char *query_string; /*< the Query string, starts with ?, after path_info and document name */
|
||||||
|
int headers_received; /*< All the headers has been received, if 1 */
|
||||||
} HTTPD_session;
|
} HTTPD_session;
|
||||||
|
@ -102,6 +102,12 @@ typedef enum {
|
|||||||
MYSQL_IDLE
|
MYSQL_IDLE
|
||||||
} mysql_auth_state_t;
|
} mysql_auth_state_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MYSQL_PROTOCOL_ALLOC,
|
||||||
|
MYSQL_PROTOCOL_ACTIVE,
|
||||||
|
MYSQL_PROTOCOL_DONE
|
||||||
|
} mysql_protocol_state_t;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MySQL session specific data
|
* MySQL session specific data
|
||||||
@ -270,6 +276,7 @@ typedef struct {
|
|||||||
server_command_t protocol_command; /*< session command list */
|
server_command_t protocol_command; /*< session command list */
|
||||||
server_command_t* protocol_cmd_history; /*< session command history */
|
server_command_t* protocol_cmd_history; /*< session command history */
|
||||||
mysql_auth_state_t protocol_auth_state; /*< Authentication status */
|
mysql_auth_state_t protocol_auth_state; /*< Authentication status */
|
||||||
|
mysql_protocol_state_t protocol_state; /*< Protocol struct status */
|
||||||
uint8_t scramble[MYSQL_SCRAMBLE_LEN]; /*< server scramble,
|
uint8_t scramble[MYSQL_SCRAMBLE_LEN]; /*< server scramble,
|
||||||
* created or received */
|
* created or received */
|
||||||
uint32_t server_capabilities; /*< server capabilities,
|
uint32_t server_capabilities; /*< server capabilities,
|
||||||
|
@ -92,7 +92,8 @@ typedef enum rses_property_type_t {
|
|||||||
RSES_PROP_TYPE_UNDEFINED=-1,
|
RSES_PROP_TYPE_UNDEFINED=-1,
|
||||||
RSES_PROP_TYPE_SESCMD=0,
|
RSES_PROP_TYPE_SESCMD=0,
|
||||||
RSES_PROP_TYPE_FIRST = RSES_PROP_TYPE_SESCMD,
|
RSES_PROP_TYPE_FIRST = RSES_PROP_TYPE_SESCMD,
|
||||||
RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_SESCMD,
|
RSES_PROP_TYPE_TMPTABLES,
|
||||||
|
RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_TMPTABLES,
|
||||||
RSES_PROP_TYPE_COUNT=RSES_PROP_TYPE_LAST+1
|
RSES_PROP_TYPE_COUNT=RSES_PROP_TYPE_LAST+1
|
||||||
} rses_property_type_t;
|
} rses_property_type_t;
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ struct rses_property_st {
|
|||||||
rses_property_type_t rses_prop_type;
|
rses_property_type_t rses_prop_type;
|
||||||
union rses_prop_data {
|
union rses_prop_data {
|
||||||
mysql_sescmd_t sescmd;
|
mysql_sescmd_t sescmd;
|
||||||
void* placeholder; /*< to be removed due new type */
|
HASHTABLE* temp_tables;
|
||||||
} rses_prop_data;
|
} rses_prop_data;
|
||||||
rses_property_t* rses_prop_next; /*< next property of same type */
|
rses_property_t* rses_prop_next; /*< next property of same type */
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
# 28/07/14 Massimiliano Pinto new monitor ndbcluster added
|
# 28/07/14 Massimiliano Pinto new monitor ndbcluster added
|
||||||
|
|
||||||
include ../../../build_gateway.inc
|
include ../../../build_gateway.inc
|
||||||
|
include ../../../makefile.inc
|
||||||
|
|
||||||
LOGPATH := $(ROOT_PATH)/log_manager
|
LOGPATH := $(ROOT_PATH)/log_manager
|
||||||
UTILSPATH := $(ROOT_PATH)/utils
|
UTILSPATH := $(ROOT_PATH)/utils
|
||||||
|
|
||||||
@ -61,13 +63,13 @@ libndbclustermon.so: $(NDBCLUSTEROBJ)
|
|||||||
$(CC) $(CFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) $(MODULES)
|
$(DEL) $(OBJ) $(MODULES)
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
ctags $(SRCS) $(HDRS)
|
ctags $(SRCS) $(HDRS)
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@rm -f depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
|
|
||||||
install: $(MODULES)
|
install: $(MODULES)
|
||||||
|
@ -76,7 +76,7 @@ libmaxscaled.so: $(MAXSCALEDOBJ)
|
|||||||
$(CC) $(CFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) $(MODULES)
|
$(DEL) $(OBJ) $(MODULES)
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
ctags $(SRCS) $(HDRS)
|
ctags $(SRCS) $(HDRS)
|
||||||
@ -85,7 +85,7 @@ install: $(MODULES)
|
|||||||
install -D $(MODULES) $(DEST)/modules
|
install -D $(MODULES) $(DEST)/modules
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
rm -f depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
|
|
||||||
include depend.mk
|
include depend.mk
|
||||||
|
@ -33,8 +33,6 @@
|
|||||||
* Date Who Description
|
* Date Who Description
|
||||||
* 08/07/2013 Massimiliano Pinto Initial version
|
* 08/07/2013 Massimiliano Pinto Initial version
|
||||||
* 09/07/2013 Massimiliano Pinto Added /show?dcb|session for all dcbs|sessions
|
* 09/07/2013 Massimiliano Pinto Added /show?dcb|session for all dcbs|sessions
|
||||||
* 11/07/2014 Mark Riddoch Recoded as more generic protocol module
|
|
||||||
* removing hardcoded example
|
|
||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
@ -42,10 +40,6 @@
|
|||||||
#include <httpd.h>
|
#include <httpd.h>
|
||||||
#include <gw.h>
|
#include <gw.h>
|
||||||
#include <modinfo.h>
|
#include <modinfo.h>
|
||||||
#include <skygw_utils.h>
|
|
||||||
#include <log_manager.h>
|
|
||||||
|
|
||||||
extern int lm_enabled_logfiles_bitmask;
|
|
||||||
|
|
||||||
MODULE_INFO info = {
|
MODULE_INFO info = {
|
||||||
MODULE_API_PROTOCOL,
|
MODULE_API_PROTOCOL,
|
||||||
@ -54,6 +48,7 @@ MODULE_INFO info = {
|
|||||||
"An experimental HTTPD implementation for use in admnistration"
|
"An experimental HTTPD implementation for use in admnistration"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define ISspace(x) isspace((int)(x))
|
||||||
#define HTTP_SERVER_STRING "Gateway(c) v.1.0.0"
|
#define HTTP_SERVER_STRING "Gateway(c) v.1.0.0"
|
||||||
static char *version_str = "V1.0.1";
|
static char *version_str = "V1.0.1";
|
||||||
|
|
||||||
@ -65,8 +60,8 @@ static int httpd_hangup(DCB *dcb);
|
|||||||
static int httpd_accept(DCB *dcb);
|
static int httpd_accept(DCB *dcb);
|
||||||
static int httpd_close(DCB *dcb);
|
static int httpd_close(DCB *dcb);
|
||||||
static int httpd_listen(DCB *dcb, char *config);
|
static int httpd_listen(DCB *dcb, char *config);
|
||||||
static char *httpd_nextline(GWBUF *buf, char *ptr);
|
static int httpd_get_line(int sock, char *buf, int size);
|
||||||
static void httpd_process_header(GWBUF *buf, char *sol, HTTPD_session *client_data);
|
static void httpd_send_headers(DCB *dcb, int final);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "module object" for the httpd protocol module.
|
* The "module object" for the httpd protocol module.
|
||||||
@ -126,99 +121,132 @@ GetModuleObject()
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
httpd_read_event(DCB *dcb)
|
httpd_read_event(DCB* dcb)
|
||||||
{
|
{
|
||||||
SESSION *session = dcb->session;
|
//SESSION *session = dcb->session;
|
||||||
GWBUF *buf = NULL;
|
//ROUTER_OBJECT *router = session->service->router;
|
||||||
char *ptr, *sol;
|
//ROUTER *router_instance = session->service->router_instance;
|
||||||
HTTPD_session *client_data = NULL;
|
//void *rsession = session->router_session;
|
||||||
int n;
|
|
||||||
|
|
||||||
// Read all the available data
|
int numchars = 1;
|
||||||
if ((n = dcb_read(dcb, &buf)) != -1)
|
char buf[HTTPD_REQUESTLINE_MAXLEN-1] = "";
|
||||||
{
|
char *query_string = NULL;
|
||||||
client_data = dcb->data;
|
char method[HTTPD_METHOD_MAXLEN-1] = "";
|
||||||
|
char url[HTTPD_SMALL_BUFFER] = "";
|
||||||
|
int cgi = 0;
|
||||||
|
size_t i, j;
|
||||||
|
int headers_read = 0;
|
||||||
|
HTTPD_session *client_data = NULL;
|
||||||
|
|
||||||
if (client_data->saved)
|
client_data = dcb->data;
|
||||||
{
|
|
||||||
buf = gwbuf_append(client_data->saved, buf);
|
|
||||||
client_data->saved = NULL;
|
|
||||||
}
|
|
||||||
buf = gwbuf_make_contiguous(buf);
|
|
||||||
|
|
||||||
ptr = GWBUF_DATA(buf);
|
|
||||||
|
|
||||||
if (strncasecmp(ptr, "POST", 4))
|
|
||||||
{
|
|
||||||
client_data->method = METHOD_POST;
|
|
||||||
gwbuf_add_property(buf, "Method", "POST");
|
|
||||||
ptr = ptr + 4;
|
|
||||||
}
|
|
||||||
else if (strncasecmp(ptr, "PUT", 3))
|
|
||||||
{
|
|
||||||
client_data->method = METHOD_PUT;
|
|
||||||
gwbuf_add_property(buf, "Method", "PUT");
|
|
||||||
ptr = ptr + 3;
|
|
||||||
}
|
|
||||||
else if (strncasecmp(ptr, "GET", 3))
|
|
||||||
{
|
|
||||||
client_data->method = METHOD_GET;
|
|
||||||
gwbuf_add_property(buf, "Method", "GET");
|
|
||||||
ptr = ptr + 3;
|
|
||||||
}
|
|
||||||
else if (strncasecmp(ptr, "HEAD", 4))
|
|
||||||
{
|
|
||||||
client_data->method = METHOD_HEAD;
|
|
||||||
gwbuf_add_property(buf, "Method", "HEAD");
|
|
||||||
ptr = ptr + 4;
|
|
||||||
}
|
|
||||||
while (ptr < (char *)(buf->end) && isspace(*ptr))
|
|
||||||
ptr++;
|
|
||||||
sol = ptr;
|
|
||||||
while (ptr < (char *)(buf->end) && isspace(*ptr) == 0)
|
|
||||||
ptr++;
|
|
||||||
client_data->url = strndup(sol, ptr - sol);
|
|
||||||
gwbuf_add_property(buf, "URL", client_data->url);
|
|
||||||
while ((sol = httpd_nextline(buf, ptr)) != NULL &&
|
|
||||||
*sol != '\n' && *sol != '\r')
|
|
||||||
{
|
|
||||||
httpd_process_header(buf, sol, client_data);
|
|
||||||
ptr = sol;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We have read all the headers, or run out of data to
|
|
||||||
* examine.
|
|
||||||
*/
|
|
||||||
if (sol == NULL)
|
|
||||||
{
|
|
||||||
client_data->saved = buf;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (((char *)(buf->end) - sol)
|
|
||||||
< client_data->request_len)
|
|
||||||
{
|
|
||||||
client_data->saved = buf;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGIF(LT, (skygw_log_write(
|
|
||||||
LOGFILE_TRACE,
|
|
||||||
"HTTPD: request %s.\n", client_data->url)));
|
|
||||||
SESSION_ROUTE_QUERY(session, buf);
|
|
||||||
if (client_data->url)
|
|
||||||
{
|
|
||||||
free(client_data->url);
|
|
||||||
client_data->url = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the request line
|
||||||
|
* METHOD URL HTTP_VER\r\n
|
||||||
|
*/
|
||||||
|
|
||||||
|
numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));
|
||||||
|
|
||||||
|
i = 0; j = 0;
|
||||||
|
while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) {
|
||||||
|
method[i] = buf[j];
|
||||||
|
i++; j++;
|
||||||
}
|
}
|
||||||
|
method[i] = '\0';
|
||||||
|
|
||||||
|
strcpy(client_data->method, method);
|
||||||
|
|
||||||
|
/* check allowed http methods */
|
||||||
|
if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) {
|
||||||
|
//httpd_unimplemented(dcb->fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp(method, "POST") == 0)
|
||||||
|
cgi = 1;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
while (ISspace(buf[j]) && (j < sizeof(buf))) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) {
|
||||||
|
url[i] = buf[j];
|
||||||
|
i++; j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
url[i] = '\0';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query string if availble
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (strcasecmp(method, "GET") == 0) {
|
||||||
|
query_string = url;
|
||||||
|
while ((*query_string != '?') && (*query_string != '\0'))
|
||||||
|
query_string++;
|
||||||
|
if (*query_string == '?') {
|
||||||
|
cgi = 1;
|
||||||
|
*query_string = '\0';
|
||||||
|
query_string++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the request headers
|
||||||
|
*/
|
||||||
|
|
||||||
|
while ((numchars > 0) && strcmp("\n", buf)) {
|
||||||
|
char *value = NULL;
|
||||||
|
char *end = NULL;
|
||||||
|
numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));
|
||||||
|
if ( (value = strchr(buf, ':'))) {
|
||||||
|
*value = '\0';
|
||||||
|
value++;
|
||||||
|
end = &value[strlen(value) -1];
|
||||||
|
*end = '\0';
|
||||||
|
|
||||||
|
if (strncasecmp(buf, "Hostname", 6) == 0) {
|
||||||
|
strcpy(client_data->hostname, value);
|
||||||
|
}
|
||||||
|
if (strncasecmp(buf, "useragent", 9) == 0) {
|
||||||
|
strcpy(client_data->useragent, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numchars) {
|
||||||
|
headers_read = 1;
|
||||||
|
memcpy(&client_data->headers_received, &headers_read, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now begins the server reply
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* send all the basic headers and close with \r\n */
|
||||||
|
httpd_send_headers(dcb, 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToDO: launch proper content handling based on the requested URI, later REST interface
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
dcb_printf(dcb, "Welcome to HTTPD Gateway (c) %s\n\n", version_str);
|
||||||
|
|
||||||
|
if (strcmp(url, "/show") == 0) {
|
||||||
|
if (strlen(query_string)) {
|
||||||
|
if (strcmp(query_string, "dcb") == 0)
|
||||||
|
dprintAllDCBs(dcb);
|
||||||
|
if (strcmp(query_string, "session") == 0)
|
||||||
|
dprintAllSessions(dcb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* force the client connecton close */
|
||||||
|
dcb_close(dcb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,18 +287,6 @@ httpd_write(DCB *dcb, GWBUF *queue)
|
|||||||
static int
|
static int
|
||||||
httpd_error(DCB *dcb)
|
httpd_error(DCB *dcb)
|
||||||
{
|
{
|
||||||
HTTPD_session *client_data = NULL;
|
|
||||||
if (dcb->data)
|
|
||||||
{
|
|
||||||
client_data = dcb->data;
|
|
||||||
if (client_data->url)
|
|
||||||
{
|
|
||||||
free(client_data->url);
|
|
||||||
client_data->url = NULL;
|
|
||||||
}
|
|
||||||
free(dcb->data);
|
|
||||||
dcb->data = NULL;
|
|
||||||
}
|
|
||||||
dcb_close(dcb);
|
dcb_close(dcb);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -302,7 +318,7 @@ int n_connect = 0;
|
|||||||
{
|
{
|
||||||
int so = -1;
|
int so = -1;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
socklen_t addrlen = 0;
|
socklen_t addrlen;
|
||||||
DCB *client = NULL;
|
DCB *client = NULL;
|
||||||
HTTPD_session *client_data = NULL;
|
HTTPD_session *client_data = NULL;
|
||||||
|
|
||||||
@ -317,11 +333,10 @@ int n_connect = 0;
|
|||||||
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
||||||
|
|
||||||
/* we don't need the session */
|
/* we don't need the session */
|
||||||
client->session = session_alloc(dcb->session->service, client);
|
client->session = NULL;
|
||||||
|
|
||||||
/* create the session data for HTTPD */
|
/* create the session data for HTTPD */
|
||||||
client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session));
|
client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session));
|
||||||
memset(client_data, 0, sizeof(HTTPD_session));
|
|
||||||
client->data = client_data;
|
client->data = client_data;
|
||||||
|
|
||||||
if (poll_add_dcb(client) == -1)
|
if (poll_add_dcb(client) == -1)
|
||||||
@ -410,84 +425,51 @@ int rc;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the start of the next line int the buffer.
|
* HTTPD get line from client
|
||||||
*
|
|
||||||
* @param buf The GWBUF chain
|
|
||||||
* @param ptr Start point within the buffer
|
|
||||||
*
|
|
||||||
* @return the start of the next line or NULL if there are no more lines
|
|
||||||
*/
|
*/
|
||||||
static char *
|
static int httpd_get_line(int sock, char *buf, int size) {
|
||||||
httpd_nextline(GWBUF *buf, char *ptr)
|
int i = 0;
|
||||||
{
|
char c = '\0';
|
||||||
while (ptr < (char *)(buf->end) && *ptr != '\n' && *ptr != '\r')
|
int n;
|
||||||
ptr++;
|
|
||||||
if (ptr >= (char *)(buf->end))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Skip prcisely one CR/LF */
|
while ((i < size - 1) && (c != '\n')) {
|
||||||
if (*ptr == '\r')
|
n = recv(sock, &c, 1, 0);
|
||||||
ptr++;
|
/* DEBUG printf("%02X\n", c); */
|
||||||
if (*ptr == '\n')
|
if (n > 0) {
|
||||||
ptr++;
|
if (c == '\r') {
|
||||||
return ptr;
|
n = recv(sock, &c, 1, MSG_PEEK);
|
||||||
}
|
/* DEBUG printf("%02X\n", c); */
|
||||||
|
if ((n > 0) && (c == '\n'))
|
||||||
/**
|
recv(sock, &c, 1, 0);
|
||||||
* The headers to extract from the HTTP request and add as properties to the
|
else
|
||||||
* GWBUF structure.
|
c = '\n';
|
||||||
*/
|
|
||||||
static char *headers[] = {
|
|
||||||
"Content-Type",
|
|
||||||
"User-Agent",
|
|
||||||
"From",
|
|
||||||
"Date",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a single header line
|
|
||||||
*
|
|
||||||
* @param buf The GWBUF that contains the request
|
|
||||||
* @param sol The current start of line
|
|
||||||
* @param client_data The client data structure for this request
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
httpd_process_header(GWBUF *buf, char *sol, HTTPD_session *client_data)
|
|
||||||
{
|
|
||||||
char *ptr = sol;
|
|
||||||
char cbuf[300];
|
|
||||||
int len, i;
|
|
||||||
|
|
||||||
/* Find the end of the line */
|
|
||||||
while (ptr < (char *)(buf->end) && *ptr != '\n' && *ptr != '\r')
|
|
||||||
ptr++;
|
|
||||||
|
|
||||||
if (strncmp(sol, "Content-Length:", strlen("Content-Length:")) == 0)
|
|
||||||
{
|
|
||||||
char *p1 = sol + strlen("Content-Length:");
|
|
||||||
while (isspace(*p1))
|
|
||||||
p1++;
|
|
||||||
len = ptr - p1;
|
|
||||||
strncpy(cbuf, p1, len);
|
|
||||||
cbuf[len] = 0;
|
|
||||||
client_data->request_len = atoi(cbuf);
|
|
||||||
gwbuf_add_property(buf, "Content-Length", cbuf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 0; headers[i]; i++)
|
|
||||||
{
|
|
||||||
if (strncmp(sol, headers[i], strlen(headers[i])) == 0)
|
|
||||||
{
|
|
||||||
char *p1 = sol + strlen(headers[i]) + 1;
|
|
||||||
while (isspace(*p1))
|
|
||||||
p1++;
|
|
||||||
len = ptr - p1;
|
|
||||||
strncpy(cbuf, p1, len);
|
|
||||||
cbuf[len] = 0;
|
|
||||||
gwbuf_add_property(buf, headers[i], cbuf);
|
|
||||||
}
|
}
|
||||||
}
|
buf[i] = c;
|
||||||
|
i++;
|
||||||
|
} else
|
||||||
|
c = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[i] = '\0';
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTPD send basic headers with 200 OK
|
||||||
|
*/
|
||||||
|
static void httpd_send_headers(DCB *dcb, int final)
|
||||||
|
{
|
||||||
|
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/plain\r\n", date, HTTP_SERVER_STRING);
|
||||||
|
|
||||||
|
/* close the headers */
|
||||||
|
if (final) {
|
||||||
|
dcb_printf(dcb, "\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -761,12 +761,13 @@ return_rc:
|
|||||||
*/
|
*/
|
||||||
static int gw_error_backend_event(DCB *dcb)
|
static int gw_error_backend_event(DCB *dcb)
|
||||||
{
|
{
|
||||||
SESSION* session;
|
SESSION* session;
|
||||||
void* rsession;
|
void* rsession;
|
||||||
ROUTER_OBJECT* router;
|
ROUTER_OBJECT* router;
|
||||||
ROUTER* router_instance;
|
ROUTER* router_instance;
|
||||||
GWBUF* errbuf;
|
GWBUF* errbuf;
|
||||||
bool succp;
|
bool succp;
|
||||||
|
session_state_t ses_state;
|
||||||
|
|
||||||
CHK_DCB(dcb);
|
CHK_DCB(dcb);
|
||||||
session = dcb->session;
|
session = dcb->session;
|
||||||
@ -775,11 +776,6 @@ static int gw_error_backend_event(DCB *dcb)
|
|||||||
router = session->service->router;
|
router = session->service->router;
|
||||||
router_instance = session->service->router_instance;
|
router_instance = session->service->router_instance;
|
||||||
|
|
||||||
#if defined(SS_DEBUG)
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
|
||||||
LOGFILE_ERROR,
|
|
||||||
"Backend error event handling.")));
|
|
||||||
#endif
|
|
||||||
/**
|
/**
|
||||||
* Avoid running redundant error handling procedure.
|
* Avoid running redundant error handling procedure.
|
||||||
* dcb_close is already called for the DCB. Thus, either connection is
|
* dcb_close is already called for the DCB. Thus, either connection is
|
||||||
@ -795,6 +791,34 @@ static int gw_error_backend_event(DCB *dcb)
|
|||||||
0,
|
0,
|
||||||
"Lost connection to backend server.");
|
"Lost connection to backend server.");
|
||||||
|
|
||||||
|
spinlock_acquire(&session->ses_lock);
|
||||||
|
ses_state = session->state;
|
||||||
|
spinlock_release(&session->ses_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session might be initialized when DCB already is in the poll set.
|
||||||
|
* Thus hangup can occur in the middle of session initialization.
|
||||||
|
* Only complete and successfully initialized sessions allow for
|
||||||
|
* calling error handler.
|
||||||
|
*/
|
||||||
|
while (ses_state == SESSION_STATE_READY)
|
||||||
|
{
|
||||||
|
spinlock_acquire(&session->ses_lock);
|
||||||
|
ses_state = session->state;
|
||||||
|
spinlock_release(&session->ses_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ses_state != SESSION_STATE_ROUTER_READY)
|
||||||
|
{
|
||||||
|
gwbuf_free(errbuf);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Backend error event handling.")));
|
||||||
|
#endif
|
||||||
router->handleError(router_instance,
|
router->handleError(router_instance,
|
||||||
rsession,
|
rsession,
|
||||||
errbuf,
|
errbuf,
|
||||||
@ -810,6 +834,7 @@ static int gw_error_backend_event(DCB *dcb)
|
|||||||
}
|
}
|
||||||
dcb_close(dcb);
|
dcb_close(dcb);
|
||||||
|
|
||||||
|
retblock:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -923,12 +948,13 @@ return_fd:
|
|||||||
static int
|
static int
|
||||||
gw_backend_hangup(DCB *dcb)
|
gw_backend_hangup(DCB *dcb)
|
||||||
{
|
{
|
||||||
SESSION* session;
|
SESSION* session;
|
||||||
void* rsession;
|
void* rsession;
|
||||||
ROUTER_OBJECT* router;
|
ROUTER_OBJECT* router;
|
||||||
ROUTER* router_instance;
|
ROUTER* router_instance;
|
||||||
bool succp;
|
bool succp;
|
||||||
GWBUF* errbuf;
|
GWBUF* errbuf;
|
||||||
|
session_state_t ses_state;
|
||||||
|
|
||||||
CHK_DCB(dcb);
|
CHK_DCB(dcb);
|
||||||
session = dcb->session;
|
session = dcb->session;
|
||||||
@ -938,18 +964,39 @@ gw_backend_hangup(DCB *dcb)
|
|||||||
router = session->service->router;
|
router = session->service->router;
|
||||||
router_instance = session->service->router_instance;
|
router_instance = session->service->router_instance;
|
||||||
|
|
||||||
|
errbuf = mysql_create_custom_error(
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
"Lost connection to backend server.");
|
||||||
|
|
||||||
|
spinlock_acquire(&session->ses_lock);
|
||||||
|
ses_state = session->state;
|
||||||
|
spinlock_release(&session->ses_lock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session might be initialized when DCB already is in the poll set.
|
||||||
|
* Thus hangup can occur in the middle of session initialization.
|
||||||
|
* Only complete and successfully initialized sessions allow for
|
||||||
|
* calling error handler.
|
||||||
|
*/
|
||||||
|
while (ses_state == SESSION_STATE_READY)
|
||||||
|
{
|
||||||
|
spinlock_acquire(&session->ses_lock);
|
||||||
|
ses_state = session->state;
|
||||||
|
spinlock_release(&session->ses_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ses_state != SESSION_STATE_ROUTER_READY)
|
||||||
|
{
|
||||||
|
gwbuf_free(errbuf);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
"Backend hangup error handling.")));
|
"Backend hangup error handling.")));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
errbuf = mysql_create_custom_error(
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
"Lost connection to backend server.");
|
|
||||||
|
|
||||||
router->handleError(router_instance,
|
router->handleError(router_instance,
|
||||||
rsession,
|
rsession,
|
||||||
errbuf,
|
errbuf,
|
||||||
@ -958,7 +1005,8 @@ gw_backend_hangup(DCB *dcb)
|
|||||||
&succp);
|
&succp);
|
||||||
|
|
||||||
/** There are not required backends available, close session. */
|
/** There are not required backends available, close session. */
|
||||||
if (!succp) {
|
if (!succp)
|
||||||
|
{
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
@ -971,7 +1019,8 @@ gw_backend_hangup(DCB *dcb)
|
|||||||
}
|
}
|
||||||
dcb_close(dcb);
|
dcb_close(dcb);
|
||||||
|
|
||||||
return 1;
|
retblock:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,7 +65,7 @@ static int gw_client_hangup_event(DCB *dcb);
|
|||||||
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
|
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
|
||||||
int MySQLSendHandshake(DCB* dcb);
|
int MySQLSendHandshake(DCB* dcb);
|
||||||
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
|
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
|
||||||
static int route_by_statement(SESSION *, GWBUF *);
|
static int route_by_statement(SESSION *, GWBUF **);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The "module object" for the mysqld client protocol module.
|
* The "module object" for the mysqld client protocol module.
|
||||||
@ -783,7 +783,7 @@ int gw_read_client_event(
|
|||||||
* Feed each statement completely and separately
|
* Feed each statement completely and separately
|
||||||
* to router.
|
* to router.
|
||||||
*/
|
*/
|
||||||
rc = route_by_statement(session, read_buffer);
|
rc = route_by_statement(session, &read_buffer);
|
||||||
|
|
||||||
if (read_buffer != NULL)
|
if (read_buffer != NULL)
|
||||||
{
|
{
|
||||||
@ -1300,12 +1300,19 @@ static int gw_error_client_event(
|
|||||||
STRDCBSTATE(dcb->state),
|
STRDCBSTATE(dcb->state),
|
||||||
(session != NULL ? session : NULL))));
|
(session != NULL ? session : NULL))));
|
||||||
|
|
||||||
|
if (session != NULL && session->state == SESSION_STATE_STOPPING)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
"Client error event handling.")));
|
"Client error event handling.")));
|
||||||
#endif
|
#endif
|
||||||
dcb_close(dcb);
|
dcb_close(dcb);
|
||||||
|
|
||||||
|
retblock:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1380,12 +1387,19 @@ gw_client_hangup_event(DCB *dcb)
|
|||||||
{
|
{
|
||||||
CHK_SESSION(session);
|
CHK_SESSION(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (session != NULL && session->state == SESSION_STATE_STOPPING)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
"Client hangup error handling.")));
|
"Client hangup error handling.")));
|
||||||
#endif
|
#endif
|
||||||
dcb_close(dcb);
|
dcb_close(dcb);
|
||||||
|
|
||||||
|
retblock:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1399,14 +1413,16 @@ gw_client_hangup_event(DCB *dcb)
|
|||||||
* Return 1 in success. If the last packet is incomplete return success but
|
* Return 1 in success. If the last packet is incomplete return success but
|
||||||
* leave incomplete packet to readbuf.
|
* leave incomplete packet to readbuf.
|
||||||
*/
|
*/
|
||||||
static int route_by_statement(SESSION *session, GWBUF *readbuf)
|
static int route_by_statement(
|
||||||
|
SESSION* session,
|
||||||
|
GWBUF** p_readbuf)
|
||||||
{
|
{
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
GWBUF* packetbuf;
|
GWBUF* packetbuf;
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
GWBUF* tmpbuf;
|
GWBUF* tmpbuf;
|
||||||
|
|
||||||
tmpbuf = readbuf;
|
tmpbuf = *p_readbuf;
|
||||||
while (tmpbuf != NULL)
|
while (tmpbuf != NULL)
|
||||||
{
|
{
|
||||||
ss_dassert(GWBUF_IS_TYPE_MYSQL(tmpbuf));
|
ss_dassert(GWBUF_IS_TYPE_MYSQL(tmpbuf));
|
||||||
@ -1415,15 +1431,14 @@ static int route_by_statement(SESSION *session, GWBUF *readbuf)
|
|||||||
#endif
|
#endif
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
ss_dassert(GWBUF_IS_TYPE_MYSQL(readbuf));
|
ss_dassert(GWBUF_IS_TYPE_MYSQL((*p_readbuf)));
|
||||||
|
|
||||||
packetbuf = gw_MySQL_get_next_packet(&readbuf);
|
packetbuf = gw_MySQL_get_next_packet(p_readbuf);
|
||||||
|
|
||||||
ss_dassert(GWBUF_IS_TYPE_MYSQL(packetbuf));
|
|
||||||
|
|
||||||
if (packetbuf != NULL)
|
if (packetbuf != NULL)
|
||||||
{
|
{
|
||||||
CHK_GWBUF(packetbuf);
|
CHK_GWBUF(packetbuf);
|
||||||
|
ss_dassert(GWBUF_IS_TYPE_MYSQL(packetbuf));
|
||||||
/**
|
/**
|
||||||
* This means that buffer includes exactly one MySQL
|
* This means that buffer includes exactly one MySQL
|
||||||
* statement.
|
* statement.
|
||||||
@ -1446,7 +1461,7 @@ static int route_by_statement(SESSION *session, GWBUF *readbuf)
|
|||||||
goto return_rc;
|
goto return_rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (readbuf != NULL);
|
while (*p_readbuf != NULL);
|
||||||
|
|
||||||
return_rc:
|
return_rc:
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -79,6 +79,7 @@ MySQLProtocol* mysql_protocol_init(
|
|||||||
strerror(eno))));
|
strerror(eno))));
|
||||||
goto return_p;
|
goto return_p;
|
||||||
}
|
}
|
||||||
|
p->protocol_state = MYSQL_PROTOCOL_ALLOC;
|
||||||
p->protocol_auth_state = MYSQL_ALLOC;
|
p->protocol_auth_state = MYSQL_ALLOC;
|
||||||
p->protocol_command.scom_cmd = MYSQL_COM_UNDEFINED;
|
p->protocol_command.scom_cmd = MYSQL_COM_UNDEFINED;
|
||||||
p->protocol_command.scom_nresponse_packets = 0;
|
p->protocol_command.scom_nresponse_packets = 0;
|
||||||
@ -90,6 +91,7 @@ MySQLProtocol* mysql_protocol_init(
|
|||||||
/*< Assign fd with protocol */
|
/*< Assign fd with protocol */
|
||||||
p->fd = fd;
|
p->fd = fd;
|
||||||
p->owner_dcb = dcb;
|
p->owner_dcb = dcb;
|
||||||
|
p->protocol_state = MYSQL_PROTOCOL_ACTIVE;
|
||||||
CHK_PROTOCOL(p);
|
CHK_PROTOCOL(p);
|
||||||
return_p:
|
return_p:
|
||||||
return p;
|
return p;
|
||||||
@ -107,15 +109,30 @@ return_p:
|
|||||||
void mysql_protocol_done (
|
void mysql_protocol_done (
|
||||||
DCB* dcb)
|
DCB* dcb)
|
||||||
{
|
{
|
||||||
server_command_t* scmd = ((MySQLProtocol *)dcb->protocol)->protocol_cmd_history;
|
MySQLProtocol* p;
|
||||||
|
server_command_t* scmd;
|
||||||
server_command_t* scmd2;
|
server_command_t* scmd2;
|
||||||
|
|
||||||
|
p = (MySQLProtocol *)dcb->protocol;
|
||||||
|
|
||||||
|
spinlock_acquire(&p->protocol_lock);
|
||||||
|
|
||||||
|
if (p->protocol_state != MYSQL_PROTOCOL_ACTIVE)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
scmd = p->protocol_cmd_history;
|
||||||
|
|
||||||
while (scmd != NULL)
|
while (scmd != NULL)
|
||||||
{
|
{
|
||||||
scmd2 = scmd->scom_next;
|
scmd2 = scmd->scom_next;
|
||||||
free(scmd);
|
free(scmd);
|
||||||
scmd = scmd2;
|
scmd = scmd2;
|
||||||
}
|
}
|
||||||
|
p->protocol_state = MYSQL_PROTOCOL_DONE;
|
||||||
|
|
||||||
|
retblock:
|
||||||
|
spinlock_release(&p->protocol_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1497,6 +1514,9 @@ GWBUF* gw_MySQL_get_next_packet(
|
|||||||
size_t packetlen;
|
size_t packetlen;
|
||||||
size_t totalbuflen;
|
size_t totalbuflen;
|
||||||
uint8_t* data;
|
uint8_t* data;
|
||||||
|
size_t nbytes_copied = 0;
|
||||||
|
uint8_t* target;
|
||||||
|
|
||||||
readbuf = *p_readbuf;
|
readbuf = *p_readbuf;
|
||||||
|
|
||||||
if (readbuf == NULL)
|
if (readbuf == NULL)
|
||||||
@ -1523,42 +1543,27 @@ GWBUF* gw_MySQL_get_next_packet(
|
|||||||
packetbuf = NULL;
|
packetbuf = NULL;
|
||||||
goto return_packetbuf;
|
goto return_packetbuf;
|
||||||
}
|
}
|
||||||
/** there is one complete packet in the buffer */
|
|
||||||
if (packetlen == buflen)
|
packetbuf = gwbuf_alloc(packetlen);
|
||||||
{
|
target = GWBUF_DATA(packetbuf);
|
||||||
packetbuf = gwbuf_clone_portion(readbuf, 0, packetlen);
|
packetbuf->gwbuf_type = readbuf->gwbuf_type; /*< Copy the type too */
|
||||||
*p_readbuf = gwbuf_consume(readbuf, packetlen);
|
|
||||||
goto return_packetbuf;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Packet spans multiple buffers.
|
* Copy first MySQL packet to packetbuf and leave posible other
|
||||||
* Allocate buffer for complete packet
|
* packets to read buffer.
|
||||||
* copy packet parts into it and consume copied bytes
|
|
||||||
*/
|
*/
|
||||||
else if (packetlen > buflen)
|
while (nbytes_copied < packetlen && totalbuflen > 0)
|
||||||
{
|
{
|
||||||
size_t nbytes_copied = 0;
|
uint8_t* src = GWBUF_DATA((*p_readbuf));
|
||||||
uint8_t* target;
|
size_t bytestocopy;
|
||||||
|
|
||||||
packetbuf = gwbuf_alloc(packetlen);
|
bytestocopy = MIN(buflen,packetlen-nbytes_copied);
|
||||||
target = GWBUF_DATA(packetbuf);
|
|
||||||
|
|
||||||
while (nbytes_copied < packetlen)
|
memcpy(target+nbytes_copied, src, bytestocopy);
|
||||||
{
|
*p_readbuf = gwbuf_consume((*p_readbuf), bytestocopy);
|
||||||
uint8_t* src = GWBUF_DATA(readbuf);
|
totalbuflen = gwbuf_length((*p_readbuf));
|
||||||
size_t buflen = GWBUF_LENGTH(readbuf);
|
nbytes_copied += bytestocopy;
|
||||||
|
|
||||||
memcpy(target+nbytes_copied, src, buflen);
|
|
||||||
*p_readbuf = gwbuf_consume(readbuf, buflen);
|
|
||||||
nbytes_copied += buflen;
|
|
||||||
}
|
|
||||||
ss_dassert(nbytes_copied == packetlen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
packetbuf = gwbuf_clone_portion(readbuf, 0, packetlen);
|
|
||||||
*p_readbuf = gwbuf_consume(readbuf, packetlen);
|
|
||||||
}
|
}
|
||||||
|
ss_dassert(buflen == 0 || nbytes_copied == packetlen);
|
||||||
|
|
||||||
return_packetbuf:
|
return_packetbuf:
|
||||||
return packetbuf;
|
return packetbuf;
|
||||||
@ -1625,11 +1630,16 @@ void protocol_archive_srv_command(
|
|||||||
MySQLProtocol* p)
|
MySQLProtocol* p)
|
||||||
{
|
{
|
||||||
server_command_t* s1;
|
server_command_t* s1;
|
||||||
server_command_t** s2;
|
server_command_t* h1;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
|
||||||
spinlock_acquire(&p->protocol_lock);
|
spinlock_acquire(&p->protocol_lock);
|
||||||
|
|
||||||
|
if (p->protocol_state != MYSQL_PROTOCOL_ACTIVE)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
s1 = &p->protocol_command;
|
s1 = &p->protocol_command;
|
||||||
|
|
||||||
LOGIF(LT, (skygw_log_write(
|
LOGIF(LT, (skygw_log_write(
|
||||||
@ -1639,17 +1649,18 @@ void protocol_archive_srv_command(
|
|||||||
p->owner_dcb->fd)));
|
p->owner_dcb->fd)));
|
||||||
|
|
||||||
/** Copy to history list */
|
/** Copy to history list */
|
||||||
s2 = &p->protocol_cmd_history;
|
if ((h1 = p->protocol_cmd_history) == NULL)
|
||||||
|
|
||||||
if (*s2 != NULL)
|
|
||||||
{
|
{
|
||||||
while ((*s2)->scom_next != NULL)
|
p->protocol_cmd_history = server_command_copy(s1);
|
||||||
{
|
}
|
||||||
*s2 = (*s2)->scom_next;
|
else
|
||||||
len += 1;
|
{
|
||||||
}
|
while (h1->scom_next != NULL)
|
||||||
|
{
|
||||||
|
h1 = h1->scom_next;
|
||||||
|
}
|
||||||
|
h1->scom_next = server_command_copy(s1);
|
||||||
}
|
}
|
||||||
*s2 = server_command_copy(s1);
|
|
||||||
|
|
||||||
/** Keep history limits, remove oldest */
|
/** Keep history limits, remove oldest */
|
||||||
if (len > MAX_CMD_HISTORY)
|
if (len > MAX_CMD_HISTORY)
|
||||||
@ -1669,6 +1680,8 @@ void protocol_archive_srv_command(
|
|||||||
p->protocol_command = *(s1->scom_next);
|
p->protocol_command = *(s1->scom_next);
|
||||||
free(s1->scom_next);
|
free(s1->scom_next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retblock:
|
||||||
spinlock_release(&p->protocol_lock);
|
spinlock_release(&p->protocol_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1685,6 +1698,10 @@ void protocol_add_srv_command(
|
|||||||
|
|
||||||
spinlock_acquire(&p->protocol_lock);
|
spinlock_acquire(&p->protocol_lock);
|
||||||
|
|
||||||
|
if (p->protocol_state != MYSQL_PROTOCOL_ACTIVE)
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
/** this is the only server command in protocol */
|
/** this is the only server command in protocol */
|
||||||
if (p->protocol_command.scom_cmd == MYSQL_COM_UNDEFINED)
|
if (p->protocol_command.scom_cmd == MYSQL_COM_UNDEFINED)
|
||||||
{
|
{
|
||||||
@ -1717,6 +1734,7 @@ void protocol_add_srv_command(
|
|||||||
c = c->scom_next;
|
c = c->scom_next;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
retblock:
|
||||||
spinlock_release(&p->protocol_lock);
|
spinlock_release(&p->protocol_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,13 +44,10 @@ DEBUGCLISRCS=debugcli.c debugcmd.c
|
|||||||
DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o)
|
DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o)
|
||||||
CLISRCS=cli.c debugcmd.c
|
CLISRCS=cli.c debugcmd.c
|
||||||
CLIOBJ=$(CLISRCS:.c=.o)
|
CLIOBJ=$(CLISRCS:.c=.o)
|
||||||
WEBSRCS=webserver.o
|
|
||||||
WEBOBJ=$(WEBSRCS:.c=.o)
|
|
||||||
SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) cli.c
|
SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) cli.c
|
||||||
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= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so \
|
MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so
|
||||||
libwebserver.so
|
|
||||||
|
|
||||||
|
|
||||||
all: $(MODULES)
|
all: $(MODULES)
|
||||||
@ -67,9 +64,6 @@ libdebugcli.so: $(DEBUGCLIOBJ)
|
|||||||
libcli.so: $(CLIOBJ)
|
libcli.so: $(CLIOBJ)
|
||||||
$(CC) $(LDFLAGS) $(CLIOBJ) $(LIBS) -o $@
|
$(CC) $(LDFLAGS) $(CLIOBJ) $(LIBS) -o $@
|
||||||
|
|
||||||
libwebserver.so: $(WEBOBJ)
|
|
||||||
$(CC) $(LDFLAGS) $(WEBOBJ) $(LIBS) -o $@
|
|
||||||
|
|
||||||
libreadwritesplit.so:
|
libreadwritesplit.so:
|
||||||
# (cd readwritesplit; touch depend.mk ; make; cp $@ ..)
|
# (cd readwritesplit; touch depend.mk ; make; cp $@ ..)
|
||||||
|
|
||||||
@ -77,7 +71,7 @@ libreadwritesplit.so:
|
|||||||
$(CC) $(CFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) $(MODULES)
|
$(DEL) $(OBJ) $(MODULES)
|
||||||
(cd readwritesplit; touch depend.mk; make clean)
|
(cd readwritesplit; touch depend.mk; make clean)
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
@ -85,7 +79,7 @@ tags:
|
|||||||
(cd readwritesplit; make tags)
|
(cd readwritesplit; make tags)
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@rm -f depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
(cd readwritesplit; touch depend.mk ; make depend)
|
(cd readwritesplit; touch depend.mk ; make depend)
|
||||||
|
|
||||||
|
@ -50,13 +50,13 @@ libreadwritesplit.so: $(OBJ)
|
|||||||
$(CC) $(CFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJ) $(MODULES)
|
$(DEL) $(OBJ) $(MODULES)
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
ctags $(SRCS) $(HDRS)
|
ctags $(SRCS) $(HDRS)
|
||||||
|
|
||||||
depend:
|
depend:
|
||||||
@rm -f depend.mk
|
@$(DEL) depend.mk
|
||||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||||
|
|
||||||
install: $(MODULES)
|
install: $(MODULES)
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <dcb.h>
|
#include <dcb.h>
|
||||||
#include <spinlock.h>
|
#include <spinlock.h>
|
||||||
#include <modinfo.h>
|
#include <modinfo.h>
|
||||||
|
#include <modutil.h>
|
||||||
#include <mysql_client_server_protocol.h>
|
#include <mysql_client_server_protocol.h>
|
||||||
|
|
||||||
MODULE_INFO info = {
|
MODULE_INFO info = {
|
||||||
@ -280,6 +281,47 @@ static bool have_enough_servers(
|
|||||||
static SPINLOCK instlock;
|
static SPINLOCK instlock;
|
||||||
static ROUTER_INSTANCE* instances;
|
static ROUTER_INSTANCE* instances;
|
||||||
|
|
||||||
|
static int hashkeyfun(void* key);
|
||||||
|
static int hashcmpfun (void *, void *);
|
||||||
|
|
||||||
|
static int hashkeyfun(
|
||||||
|
void* key)
|
||||||
|
{
|
||||||
|
if(key == NULL){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsigned int hash = 0,c = 0;
|
||||||
|
char* ptr = (char*)key;
|
||||||
|
while((c = *ptr++)){
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
}
|
||||||
|
return *(int *)key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hashcmpfun(
|
||||||
|
void* v1,
|
||||||
|
void* v2)
|
||||||
|
{
|
||||||
|
char* i1 = (char*) v1;
|
||||||
|
char* i2 = (char*) v2;
|
||||||
|
|
||||||
|
return strcmp(i1,i2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* hstrdup(void* fval)
|
||||||
|
{
|
||||||
|
char* str = (char*)fval;
|
||||||
|
return strdup(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void* hfree(void* fval)
|
||||||
|
{
|
||||||
|
free (fval);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the mandatory version entry point
|
* Implementation of the mandatory version entry point
|
||||||
*
|
*
|
||||||
@ -956,76 +998,74 @@ static bool get_dcb(
|
|||||||
/** get root master from available 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 (name != NULL) /*< Choose backend by name from a hint */
|
||||||
|
{
|
||||||
|
ss_dassert(btype != BE_MASTER); /*< Master dominates and no name should be passed with it */
|
||||||
|
|
||||||
|
for (i=0; i<rses->rses_nbackends; i++)
|
||||||
|
{
|
||||||
|
BACKEND* b = backend_ref[i].bref_backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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])) &&
|
||||||
|
(strncasecmp(
|
||||||
|
name,
|
||||||
|
b->backend_server->unique_name,
|
||||||
|
PATH_MAX) == 0) &&
|
||||||
|
master_host != NULL &&
|
||||||
|
(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)
|
||||||
|
{
|
||||||
|
goto return_succp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
* To become chosen:
|
||||||
|
* backend must be in use,
|
||||||
/**
|
* root master node must be found,
|
||||||
* To become chosen:
|
* backend is not allowed to be the master,
|
||||||
* backend must be in use, name must match,
|
* backend's role can be either slave or relay
|
||||||
* root master node must be found,
|
* server and it must have least connections
|
||||||
* backend's role must be either slave, relay
|
* at the moment.
|
||||||
* server, or master.
|
*/
|
||||||
*/
|
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
||||||
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
master_host != NULL &&
|
||||||
(strncasecmp(
|
b->backend_server != master_host->backend_server &&
|
||||||
name,
|
(max_rlag == MAX_RLAG_UNDEFINED ||
|
||||||
b->backend_server->unique_name,
|
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||||
MIN(strlen(b->backend_server->unique_name), PATH_MAX)) == 0) &&
|
b->backend_server->rlag <= max_rlag)) &&
|
||||||
master_host != NULL &&
|
(SERVER_IS_SLAVE(b->backend_server) ||
|
||||||
#if 0
|
SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||||
(max_rlag == MAX_RLAG_UNDEFINED ||
|
(smallest_nconn == -1 ||
|
||||||
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
b->backend_conn_count < smallest_nconn))
|
||||||
b->backend_server->rlag <= max_rlag)) &&
|
{
|
||||||
#endif
|
*p_dcb = backend_ref[i].bref_dcb;
|
||||||
(SERVER_IS_SLAVE(b->backend_server) ||
|
smallest_nconn = b->backend_conn_count;
|
||||||
SERVER_IS_RELAY_SERVER(b->backend_server) ||
|
succp = true;
|
||||||
SERVER_IS_MASTER(b->backend_server)))
|
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
||||||
{
|
}
|
||||||
*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 ||
|
|
||||||
b->backend_conn_count < smallest_nconn))
|
|
||||||
{
|
|
||||||
*p_dcb = backend_ref[i].bref_dcb;
|
|
||||||
smallest_nconn = b->backend_conn_count;
|
|
||||||
succp = true;
|
|
||||||
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!succp) /*< No valid slave was found, search master next */
|
if (!succp) /*< No valid slave was found, search master next */
|
||||||
{
|
{
|
||||||
@ -1065,7 +1105,7 @@ return_succp:
|
|||||||
* Examine the query type, transaction state and routing hints. Find out the
|
* Examine the query type, transaction state and routing hints. Find out the
|
||||||
* target for query routing.
|
* target for query routing.
|
||||||
*
|
*
|
||||||
* @param qtype Type of query
|
* @param qtype Type of query
|
||||||
* @param trx_active Is transacation active or not
|
* @param trx_active Is transacation active or not
|
||||||
* @param hint Pointer to list of hints attached to the query buffer
|
* @param hint Pointer to list of hints attached to the query buffer
|
||||||
*
|
*
|
||||||
@ -1086,10 +1126,22 @@ static route_target_t get_route_target (
|
|||||||
/** hints don't affect on routing */
|
/** hints don't affect on routing */
|
||||||
target = TARGET_ALL;
|
target = TARGET_ALL;
|
||||||
}
|
}
|
||||||
else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) && !trx_active)
|
/**
|
||||||
|
* Read-only statements to slave or to master can be re-routed after
|
||||||
|
* the hints
|
||||||
|
*/
|
||||||
|
else if ((QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) ||
|
||||||
|
QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_READ)) &&
|
||||||
|
!trx_active)
|
||||||
{
|
{
|
||||||
target = TARGET_SLAVE;
|
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ))
|
||||||
|
{
|
||||||
|
target = TARGET_SLAVE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = TARGET_MASTER;
|
||||||
|
}
|
||||||
/** process routing hints */
|
/** process routing hints */
|
||||||
while (hint != NULL)
|
while (hint != NULL)
|
||||||
{
|
{
|
||||||
@ -1103,8 +1155,15 @@ static route_target_t get_route_target (
|
|||||||
}
|
}
|
||||||
else if (hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
else if (hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
||||||
{
|
{
|
||||||
target |= TARGET_NAMED_SERVER; /*< add */
|
/**
|
||||||
}
|
* Searching for a named server. If it can't be
|
||||||
|
* found, the oroginal target is chosen.
|
||||||
|
*/
|
||||||
|
target |= TARGET_NAMED_SERVER;
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Hint: route to named server : ")));
|
||||||
|
}
|
||||||
else if (hint->type == HINT_ROUTE_TO_UPTODATE_SERVER)
|
else if (hint->type == HINT_ROUTE_TO_UPTODATE_SERVER)
|
||||||
{
|
{
|
||||||
/** not implemented */
|
/** not implemented */
|
||||||
@ -1140,6 +1199,7 @@ static route_target_t get_route_target (
|
|||||||
}
|
}
|
||||||
else if (hint->type == HINT_ROUTE_TO_SLAVE)
|
else if (hint->type == HINT_ROUTE_TO_SLAVE)
|
||||||
{
|
{
|
||||||
|
target = TARGET_SLAVE;
|
||||||
LOGIF(LT, (skygw_log_write(
|
LOGIF(LT, (skygw_log_write(
|
||||||
LOGFILE_TRACE,
|
LOGFILE_TRACE,
|
||||||
"Hint: route to slave.")));
|
"Hint: route to slave.")));
|
||||||
@ -1191,15 +1251,27 @@ static int routeQuery(
|
|||||||
char* startpos;
|
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;
|
||||||
|
int tsize = 0;
|
||||||
|
int klen = 0;
|
||||||
|
int i = 0;
|
||||||
DCB* master_dcb = NULL;
|
DCB* master_dcb = NULL;
|
||||||
DCB* target_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;
|
||||||
|
rses_property_t* rses_prop_tmp;
|
||||||
bool rses_is_closed = false;
|
bool rses_is_closed = false;
|
||||||
|
bool target_tmp_table = false;
|
||||||
|
char* dbname;
|
||||||
|
char* hkey;
|
||||||
|
char** tbl;
|
||||||
|
HASHTABLE* h;
|
||||||
|
MYSQL_session* data;
|
||||||
size_t len;
|
size_t len;
|
||||||
MYSQL* mysql = NULL;
|
|
||||||
route_target_t route_target;
|
route_target_t route_target;
|
||||||
|
bool succp = false;
|
||||||
|
int rlag_max = MAX_RLAG_UNDEFINED;
|
||||||
|
backend_type_t btype; /*< target backend type */
|
||||||
|
|
||||||
CHK_CLIENT_RSES(router_cli_ses);
|
CHK_CLIENT_RSES(router_cli_ses);
|
||||||
|
|
||||||
@ -1213,6 +1285,7 @@ static int routeQuery(
|
|||||||
|
|
||||||
packet = GWBUF_DATA(querybuf);
|
packet = GWBUF_DATA(querybuf);
|
||||||
packet_type = packet[4];
|
packet_type = packet[4];
|
||||||
|
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
||||||
|
|
||||||
if (rses_is_closed)
|
if (rses_is_closed)
|
||||||
{
|
{
|
||||||
@ -1222,6 +1295,8 @@ static int routeQuery(
|
|||||||
*/
|
*/
|
||||||
if (packet_type != MYSQL_COM_QUIT)
|
if (packet_type != MYSQL_COM_QUIT)
|
||||||
{
|
{
|
||||||
|
char* query_str = modutil_get_query(querybuf);
|
||||||
|
|
||||||
LOGIF(LE,
|
LOGIF(LE,
|
||||||
(skygw_log_write_flush(
|
(skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
@ -1229,19 +1304,22 @@ static int routeQuery(
|
|||||||
"backend server. %s.",
|
"backend server. %s.",
|
||||||
STRPACKETTYPE(packet_type),
|
STRPACKETTYPE(packet_type),
|
||||||
STRQTYPE(qtype),
|
STRQTYPE(qtype),
|
||||||
(querystr == NULL ? "(empty)" : querystr),
|
(query_str == NULL ? "(empty)" : query_str),
|
||||||
(rses_is_closed ? "Router was closed" :
|
(rses_is_closed ? "Router was closed" :
|
||||||
"Router has no backend servers where to "
|
"Router has no backend servers where to "
|
||||||
"route to"))));
|
"route to"))));
|
||||||
|
free(querybuf);
|
||||||
}
|
}
|
||||||
goto return_ret;
|
goto retblock;
|
||||||
}
|
}
|
||||||
inst->stats.n_queries++;
|
inst->stats.n_queries++;
|
||||||
startpos = (char *)&packet[5];
|
|
||||||
|
|
||||||
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
|
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
|
||||||
CHK_DCB(master_dcb);
|
CHK_DCB(master_dcb);
|
||||||
|
|
||||||
|
data = (MYSQL_session*)master_dcb->session->data;
|
||||||
|
dbname = data->db;
|
||||||
|
|
||||||
switch(packet_type) {
|
switch(packet_type) {
|
||||||
case MYSQL_COM_QUIT: /*< 1 QUIT will close all sessions */
|
case MYSQL_COM_QUIT: /*< 1 QUIT will close all sessions */
|
||||||
case MYSQL_COM_INIT_DB: /*< 2 DDL must go to the master */
|
case MYSQL_COM_INIT_DB: /*< 2 DDL must go to the master */
|
||||||
@ -1261,44 +1339,16 @@ static int routeQuery(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MYSQL_COM_QUERY:
|
case MYSQL_COM_QUERY:
|
||||||
plainsqlbuf = gwbuf_clone_transform(querybuf,
|
qtype = query_classifier_get_type(querybuf);
|
||||||
GWBUF_TYPE_PLAINSQL);
|
|
||||||
len = GWBUF_LENGTH(plainsqlbuf);
|
|
||||||
/** unnecessary if buffer includes additional terminating null */
|
|
||||||
querystr = (char *)malloc(len+1);
|
|
||||||
memcpy(querystr, startpos, len);
|
|
||||||
memset(&querystr[len], 0, 1);
|
|
||||||
/**
|
|
||||||
* Use mysql handle to query information from parse tree.
|
|
||||||
* call skygw_query_classifier_free before exit!
|
|
||||||
*/
|
|
||||||
qtype = skygw_query_classifier_get_type(querystr, 0, &mysql);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MYSQL_COM_STMT_PREPARE:
|
case MYSQL_COM_STMT_PREPARE:
|
||||||
plainsqlbuf = gwbuf_clone_transform(querybuf,
|
qtype = query_classifier_get_type(querybuf);
|
||||||
GWBUF_TYPE_PLAINSQL);
|
|
||||||
len = GWBUF_LENGTH(plainsqlbuf);
|
|
||||||
/** unnecessary if buffer includes additional terminating null */
|
|
||||||
querystr = (char *)malloc(len+1);
|
|
||||||
memcpy(querystr, startpos, len);
|
|
||||||
memset(&querystr[len], 0, 1);
|
|
||||||
qtype = skygw_query_classifier_get_type(querystr, 0, &mysql);
|
|
||||||
qtype |= QUERY_TYPE_PREPARE_STMT;
|
qtype |= QUERY_TYPE_PREPARE_STMT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MYSQL_COM_STMT_EXECUTE:
|
case MYSQL_COM_STMT_EXECUTE:
|
||||||
/** Parsing is not needed for this type of packet */
|
/** Parsing is not needed for this type of packet */
|
||||||
#if defined(NOT_USED)
|
|
||||||
plainsqlbuf = gwbuf_clone_transform(querybuf,
|
|
||||||
GWBUF_TYPE_PLAINSQL);
|
|
||||||
len = GWBUF_LENGTH(plainsqlbuf);
|
|
||||||
/** unnecessary if buffer includes additional terminating null */
|
|
||||||
querystr = (char *)malloc(len+1);
|
|
||||||
memcpy(querystr, startpos, len);
|
|
||||||
memset(&querystr[len], 0, 1);
|
|
||||||
qtype = skygw_query_classifier_get_type(querystr, 0, &mysql);
|
|
||||||
#endif
|
|
||||||
qtype = QUERY_TYPE_EXEC_STMT;
|
qtype = QUERY_TYPE_EXEC_STMT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1314,6 +1364,48 @@ static int routeQuery(
|
|||||||
break;
|
break;
|
||||||
} /**< switch by packet type */
|
} /**< switch by packet type */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the query targets a temporary table
|
||||||
|
*/
|
||||||
|
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ))
|
||||||
|
{
|
||||||
|
tbl = skygw_get_table_names(querybuf,&tsize);
|
||||||
|
|
||||||
|
if (tsize > 0)
|
||||||
|
{
|
||||||
|
/** Query targets at least one table */
|
||||||
|
for(i = 0; i<tsize && !target_tmp_table && tbl[i]; i++)
|
||||||
|
{
|
||||||
|
klen = strlen(dbname) + strlen(tbl[i]) + 2;
|
||||||
|
hkey = calloc(klen,sizeof(char));
|
||||||
|
strcpy(hkey,dbname);
|
||||||
|
strcat(hkey,".");
|
||||||
|
strcat(hkey,tbl[0]);
|
||||||
|
|
||||||
|
if (rses_prop_tmp &&
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables)
|
||||||
|
{
|
||||||
|
|
||||||
|
if( (target_tmp_table =
|
||||||
|
(bool)hashtable_fetch(rses_prop_tmp->rses_prop_data.temp_tables,(void *)hkey)))
|
||||||
|
{
|
||||||
|
/**Query target is a temporary table*/
|
||||||
|
qtype = QUERY_TYPE_READ_TMP_TABLE;
|
||||||
|
LOGIF(LT,
|
||||||
|
(skygw_log_write(LOGFILE_TRACE,
|
||||||
|
"Query targets a temporary table: %s",hkey)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(hkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i<tsize; i++)
|
||||||
|
{
|
||||||
|
free(tbl[i]);
|
||||||
|
}
|
||||||
|
free(tbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* If autocommit is disabled or transaction is explicitly started
|
* If autocommit is disabled or transaction is explicitly started
|
||||||
* transaction becomes active and master gets all statements until
|
* transaction becomes active and master gets all statements until
|
||||||
@ -1350,6 +1442,118 @@ static int routeQuery(
|
|||||||
router_cli_ses->rses_autocommit_enabled = true;
|
router_cli_ses->rses_autocommit_enabled = true;
|
||||||
router_cli_ses->rses_transaction_active = false;
|
router_cli_ses->rses_transaction_active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If query is of type QUERY_TYPE_CREATE_TMP_TABLE then find out
|
||||||
|
* the database and table name, create a hashvalue and
|
||||||
|
* add it to the router client session's property. If property
|
||||||
|
* doesn't exist then create it first. If the query is DROP TABLE...
|
||||||
|
* then see if it targets a temporary table and remove it from the hashtable
|
||||||
|
* if it does.
|
||||||
|
*/
|
||||||
|
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_CREATE_TMP_TABLE))
|
||||||
|
{
|
||||||
|
bool is_temp = true;
|
||||||
|
char* tblname = NULL;
|
||||||
|
|
||||||
|
tblname = skygw_get_created_table_name(querybuf);
|
||||||
|
|
||||||
|
if (tblname && strlen(tblname) > 0)
|
||||||
|
{
|
||||||
|
klen = strlen(dbname) + strlen(tblname) + 2;
|
||||||
|
hkey = calloc(klen,sizeof(char));
|
||||||
|
strcpy(hkey,dbname);
|
||||||
|
strcat(hkey,".");
|
||||||
|
strcat(hkey,tblname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hkey = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rses_prop_tmp == NULL)
|
||||||
|
{
|
||||||
|
if((rses_prop_tmp =
|
||||||
|
(rses_property_t*)calloc(1,sizeof(rses_property_t))))
|
||||||
|
{
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
rses_prop_tmp->rses_prop_chk_top = CHK_NUM_ROUTER_PROPERTY;
|
||||||
|
rses_prop_tmp->rses_prop_chk_tail = CHK_NUM_ROUTER_PROPERTY;
|
||||||
|
#endif
|
||||||
|
rses_prop_tmp->rses_prop_rsession = router_cli_ses;
|
||||||
|
rses_prop_tmp->rses_prop_refcount = 1;
|
||||||
|
rses_prop_tmp->rses_prop_next = NULL;
|
||||||
|
rses_prop_tmp->rses_prop_type = RSES_PROP_TYPE_TMPTABLES;
|
||||||
|
router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES] = rses_prop_tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rses_prop_tmp->rses_prop_data.temp_tables == NULL)
|
||||||
|
{
|
||||||
|
h = hashtable_alloc(7, hashkeyfun, hashcmpfun);
|
||||||
|
hashtable_memory_fns(h,hstrdup,NULL,hfree,NULL);
|
||||||
|
if (h != NULL)
|
||||||
|
{
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hkey &&
|
||||||
|
hashtable_add(rses_prop_tmp->rses_prop_data.temp_tables,
|
||||||
|
(void *)hkey,
|
||||||
|
(void *)is_temp) == 0) /*< Conflict in hash table */
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Temporary table conflict in hashtable: %s",
|
||||||
|
hkey)));
|
||||||
|
}
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
{
|
||||||
|
bool retkey =
|
||||||
|
hashtable_fetch(
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables,
|
||||||
|
hkey);
|
||||||
|
if (retkey)
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Temporary table added: %s",
|
||||||
|
hkey)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
free(hkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if DROP TABLE... targets a temporary table */
|
||||||
|
if (is_drop_table_query(querybuf))
|
||||||
|
{
|
||||||
|
tbl = skygw_get_table_names(querybuf,&tsize);
|
||||||
|
|
||||||
|
for(i = 0; i<tsize; i++)
|
||||||
|
{
|
||||||
|
klen = strlen(dbname) + strlen(tbl[i]) + 2;
|
||||||
|
hkey = calloc(klen,sizeof(char));
|
||||||
|
strcpy(hkey,dbname);
|
||||||
|
strcat(hkey,".");
|
||||||
|
strcat(hkey,tbl[i]);
|
||||||
|
|
||||||
|
if (rses_prop_tmp &&
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables)
|
||||||
|
{
|
||||||
|
if (hashtable_delete(rses_prop_tmp->rses_prop_data.temp_tables,
|
||||||
|
(void *)hkey))
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE,
|
||||||
|
"Temporary table dropped: %s",hkey)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(tbl[i]);
|
||||||
|
free(hkey);
|
||||||
|
}
|
||||||
|
free(tbl);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Find out where to route the query. Result may not be clear; it is
|
* 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
|
* possible to have a hint for routing to a named server which can
|
||||||
@ -1357,161 +1561,193 @@ static int routeQuery(
|
|||||||
* If query would otherwise be routed to slave then the hint determines
|
* If query would otherwise be routed to slave then the hint determines
|
||||||
* actual target server if it exists.
|
* actual target server if it exists.
|
||||||
*
|
*
|
||||||
* route_target is a bitfield and may include multiple values.
|
* route_target is a bitfield and may include :
|
||||||
|
* TARGET_ALL
|
||||||
|
* - route to all connected backend servers
|
||||||
|
* TARGET_SLAVE[|TARGET_NAMED_SERVER|TARGET_RLAG_MAX]
|
||||||
|
* - route primarily according to hints, then to slave and if those
|
||||||
|
* failed, eventually to master
|
||||||
|
* TARGET_MASTER[|TARGET_NAMED_SERVER|TARGET_RLAG_MAX]
|
||||||
|
* - route primarily according to the hints and if they failed,
|
||||||
|
* eventually to master
|
||||||
*/
|
*/
|
||||||
route_target = get_route_target(qtype,
|
route_target = get_route_target(qtype,
|
||||||
router_cli_ses->rses_transaction_active,
|
router_cli_ses->rses_transaction_active,
|
||||||
querybuf->hint);
|
querybuf->hint);
|
||||||
|
|
||||||
if (TARGET_IS_ALL(route_target))
|
if (TARGET_IS_ALL(route_target))
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* It is not sure if the session command in question requires
|
||||||
|
* response. Statement is examined in route_session_write.
|
||||||
|
* Router locking is done inside the function.
|
||||||
|
*/
|
||||||
|
succp = route_session_write(router_cli_ses,
|
||||||
|
gwbuf_clone(querybuf),
|
||||||
|
inst,
|
||||||
|
packet_type,
|
||||||
|
qtype);
|
||||||
|
|
||||||
|
if (succp)
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lock router session */
|
||||||
|
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||||
|
{
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* There is a hint which either names the target backend or
|
||||||
|
* hint which sets maximum allowed replication lag for the
|
||||||
|
* backend.
|
||||||
|
*/
|
||||||
|
if (TARGET_IS_NAMED_SERVER(route_target) ||
|
||||||
|
TARGET_IS_RLAG_MAX(route_target))
|
||||||
|
{
|
||||||
|
HINT* hint;
|
||||||
|
char* named_server = NULL;
|
||||||
|
|
||||||
|
hint = querybuf->hint;
|
||||||
|
|
||||||
|
while (hint != NULL)
|
||||||
|
{
|
||||||
|
if (hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the name of searched
|
||||||
|
* backend 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)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set max. acceptable
|
||||||
|
* replication lag
|
||||||
|
* value for backend srv
|
||||||
|
*/
|
||||||
|
rlag_max = val;
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Hint: "
|
||||||
|
"max_slave_replication_lag=%d",
|
||||||
|
rlag_max)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hint = hint->next;
|
||||||
|
} /*< while */
|
||||||
|
|
||||||
|
if (rlag_max == MAX_RLAG_UNDEFINED) /*< no rlag max hint, use config */
|
||||||
|
{
|
||||||
|
rlag_max = rses_get_max_replication_lag(router_cli_ses);
|
||||||
|
}
|
||||||
|
btype = BE_UNDEFINED; /*< target may be master or slave */
|
||||||
|
/**
|
||||||
|
* Search backend server by name or replication lag.
|
||||||
|
* If it fails, then try to find valid slave or master.
|
||||||
|
*/
|
||||||
|
succp = get_dcb(&target_dcb,
|
||||||
|
router_cli_ses,
|
||||||
|
btype,
|
||||||
|
named_server,
|
||||||
|
rlag_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!succp && TARGET_IS_SLAVE(route_target))
|
||||||
|
{
|
||||||
|
btype = BE_SLAVE;
|
||||||
|
|
||||||
|
if (rlag_max == MAX_RLAG_UNDEFINED) /*< no rlag max hint, use config */
|
||||||
|
{
|
||||||
|
rlag_max = rses_get_max_replication_lag(router_cli_ses);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Search suitable backend server, get DCB in target_dcb
|
||||||
|
*/
|
||||||
|
succp = get_dcb(&target_dcb,
|
||||||
|
router_cli_ses,
|
||||||
|
BE_SLAVE,
|
||||||
|
NULL,
|
||||||
|
rlag_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!succp && TARGET_IS_MASTER(route_target))
|
||||||
|
{
|
||||||
|
if (master_dcb == NULL)
|
||||||
|
{
|
||||||
|
succp = get_dcb(&master_dcb,
|
||||||
|
router_cli_ses,
|
||||||
|
BE_MASTER,
|
||||||
|
NULL,
|
||||||
|
MAX_RLAG_UNDEFINED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
succp = true;
|
||||||
|
}
|
||||||
|
target_dcb = master_dcb;
|
||||||
|
}
|
||||||
|
ss_dassert(succp);
|
||||||
|
|
||||||
|
|
||||||
|
if (succp) /*< Have DCB of the target backend */
|
||||||
|
{
|
||||||
|
if ((ret = target_dcb->func.write(target_dcb, gwbuf_clone(querybuf))) == 1)
|
||||||
|
{
|
||||||
|
backend_ref_t* bref;
|
||||||
|
|
||||||
|
atomic_add(&inst->stats.n_slave, 1);
|
||||||
|
/**
|
||||||
|
* Add one query response waiter to backend reference
|
||||||
|
*/
|
||||||
|
bref = get_bref_from_dcb(router_cli_ses, target_dcb);
|
||||||
|
bref_set_state(bref, BREF_QUERY_ACTIVE);
|
||||||
|
bref_set_state(bref, BREF_WAITING_RESULT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error : Routing query \"%s\" failed.",
|
||||||
|
querystr)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rses_end_locked_router_action(router_cli_ses);
|
||||||
|
retblock:
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
{
|
{
|
||||||
/**
|
char* canonical_query_str;
|
||||||
* It is not sure if the session command in question requires
|
|
||||||
* response. Statement is examined in route_session_write.
|
|
||||||
*/
|
|
||||||
bool succp = route_session_write(
|
|
||||||
router_cli_ses,
|
|
||||||
querybuf,
|
|
||||||
inst,
|
|
||||||
packet_type,
|
|
||||||
qtype);
|
|
||||||
|
|
||||||
if (succp)
|
canonical_query_str = skygw_get_canonical(querybuf);
|
||||||
|
|
||||||
|
if (canonical_query_str != NULL)
|
||||||
{
|
{
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
goto return_ret;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Handle routing to master and to slave
|
|
||||||
*/
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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,
|
||||||
"Transaction is active, routing to Master.")));
|
"Canonical version: %s",
|
||||||
|
canonical_query_str)));
|
||||||
|
free(canonical_query_str);
|
||||||
}
|
}
|
||||||
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s", STRQTYPE(qtype))));
|
|
||||||
|
|
||||||
/** Lock router session */
|
|
||||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
|
||||||
{
|
|
||||||
goto return_ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TARGET_IS_SLAVE(route_target))
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
atomic_add(&inst->stats.n_slave, 1);
|
|
||||||
/**
|
|
||||||
* Add one query response waiter to backend reference
|
|
||||||
*/
|
|
||||||
bref = get_bref_from_dcb(router_cli_ses, target_dcb);
|
|
||||||
bref_set_state(bref, BREF_QUERY_ACTIVE);
|
|
||||||
bref_set_state(bref, BREF_WAITING_RESULT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
|
||||||
LOGFILE_ERROR,
|
|
||||||
"Error : Routing query \"%s\" failed.",
|
|
||||||
querystr)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rses_end_locked_router_action(router_cli_ses);
|
|
||||||
}
|
|
||||||
|
|
||||||
return_ret:
|
|
||||||
if (plainsqlbuf != NULL)
|
|
||||||
{
|
|
||||||
gwbuf_free(plainsqlbuf);
|
|
||||||
}
|
|
||||||
if (querystr != NULL)
|
|
||||||
{
|
|
||||||
free(querystr);
|
|
||||||
}
|
|
||||||
if (mysql != NULL)
|
|
||||||
{
|
|
||||||
skygw_query_classifier_free(mysql);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
gwbuf_free(querybuf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1725,8 +1961,10 @@ static void clientReply (
|
|||||||
(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);
|
||||||
char* cmdstr = (char *)malloc(len+1);
|
char* cmdstr = (char *)malloc(len+1);
|
||||||
|
/** data+termination character == len */
|
||||||
|
snprintf(cmdstr, len, "%s", &buf[5]);
|
||||||
|
|
||||||
snprintf(cmdstr, len+1, "%s", &buf[5]);
|
ss_dassert(len+4 == GWBUF_LENGTH(scur->scmd_cur_cmd->my_sescmd_buf));
|
||||||
|
|
||||||
LOGIF(LE, (skygw_log_write_flush(
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
LOGFILE_ERROR,
|
LOGFILE_ERROR,
|
||||||
@ -2457,6 +2695,11 @@ static void rses_property_done(
|
|||||||
case RSES_PROP_TYPE_SESCMD:
|
case RSES_PROP_TYPE_SESCMD:
|
||||||
mysql_sescmd_done(&prop->rses_prop_data.sescmd);
|
mysql_sescmd_done(&prop->rses_prop_data.sescmd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RSES_PROP_TYPE_TMPTABLES:
|
||||||
|
hashtable_free(prop->rses_prop_data.temp_tables);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOGIF(LD, (skygw_log_write(
|
LOGIF(LD, (skygw_log_write(
|
||||||
LOGFILE_DEBUG,
|
LOGFILE_DEBUG,
|
||||||
@ -2856,8 +3099,23 @@ static bool execute_sescmd_in_backend(
|
|||||||
sescmd_cursor_clone_querybuf(scur));
|
sescmd_cursor_clone_querybuf(scur));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MYSQL_COM_QUERY:
|
case MYSQL_COM_INIT_DB:
|
||||||
case MYSQL_COM_INIT_DB:
|
{
|
||||||
|
/**
|
||||||
|
* Record database name and store to session.
|
||||||
|
*/
|
||||||
|
GWBUF* tmpbuf;
|
||||||
|
MYSQL_session* data;
|
||||||
|
unsigned int qlen;
|
||||||
|
|
||||||
|
data = dcb->session->data;
|
||||||
|
tmpbuf = scur->scmd_cur_cmd->my_sescmd_buf;
|
||||||
|
qlen = MYSQL_GET_PACKET_LEN((unsigned char*)tmpbuf->start);
|
||||||
|
memset(data->db,0,MYSQL_DATABASE_MAXLEN+1);
|
||||||
|
strncpy(data->db,tmpbuf->start+5,qlen - 1);
|
||||||
|
}
|
||||||
|
/** Fallthrough */
|
||||||
|
case MYSQL_COM_QUERY:
|
||||||
default:
|
default:
|
||||||
/**
|
/**
|
||||||
* Mark session command buffer, it triggers writing
|
* Mark session command buffer, it triggers writing
|
||||||
@ -3831,4 +4089,3 @@ static BACKEND *get_root_master(backend_ref_t *servers, int router_nservers) {
|
|||||||
}
|
}
|
||||||
return master_host;
|
return master_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,3 +230,65 @@ if [ "$a" != "$TRETVAL" ]; then
|
|||||||
else
|
else
|
||||||
echo "$TINPUT PASSED">>$TLOG ;
|
echo "$TINPUT PASSED">>$TLOG ;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
TINPUT=test_temporary_table.sql
|
||||||
|
a=`$RUNCMD < ./$TINPUT`
|
||||||
|
TRETVAL=1
|
||||||
|
if [ "$a" != "$TRETVAL" ]; then
|
||||||
|
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
|
||||||
|
else
|
||||||
|
echo "$TINPUT PASSED">>$TLOG ;
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "-----------------------------------" >> $TLOG
|
||||||
|
echo "Session variables: Stress Test 1" >> $TLOG
|
||||||
|
echo "-----------------------------------" >> $TLOG
|
||||||
|
|
||||||
|
RUNCMD=mysql\ --host=$THOST\ -P$TPORT\ -u$TUSER\ -p$TPWD\ --unbuffered=true\ --disable-reconnect\ -q\ -r
|
||||||
|
TINPUT=test_sescmd2.sql
|
||||||
|
for ((i = 0;i<1000;i++))
|
||||||
|
do
|
||||||
|
if [[ $(( i % 50 )) -eq 0 ]]
|
||||||
|
then
|
||||||
|
printf "."
|
||||||
|
fi
|
||||||
|
a=`$RUNCMD < $TINPUT 2>&1`
|
||||||
|
if [[ "`echo "$a"|grep -i 'error'`" != "" ]]
|
||||||
|
then
|
||||||
|
err=`echo "$a" | grep -i error`
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$err" == "" ]]
|
||||||
|
then
|
||||||
|
echo "TEST PASSED" >> $TLOG
|
||||||
|
else
|
||||||
|
echo "$err" >> $TLOG
|
||||||
|
echo "Test FAILED at iteration $((i+1))" >> $TLOG
|
||||||
|
fi
|
||||||
|
echo "-----------------------------------" >> $TLOG
|
||||||
|
echo "Session variables: Stress Test 2" >> $TLOG
|
||||||
|
echo "-----------------------------------" >> $TLOG
|
||||||
|
echo ""
|
||||||
|
err=""
|
||||||
|
TINPUT=test_sescmd3.sql
|
||||||
|
for ((j = 0;j<1000;j++))
|
||||||
|
do
|
||||||
|
if [[ $(( j % 50 )) -eq 0 ]]
|
||||||
|
then
|
||||||
|
printf "."
|
||||||
|
fi
|
||||||
|
b=`$RUNCMD < $TINPUT 2>&1`
|
||||||
|
if [[ "`echo "$b"|grep -i 'null|error'`" != "" ]]
|
||||||
|
then
|
||||||
|
err=`echo "$b" | grep -i null|error`
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$err" == "" ]]
|
||||||
|
then
|
||||||
|
echo "TEST PASSED" >> $TLOG
|
||||||
|
else
|
||||||
|
echo "Test FAILED at iteration $((j+1))" >> $TLOG
|
||||||
|
fi
|
||||||
|
echo "" >> $TLOG
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
-------------------------------
|
|
||||||
Tue Aug 5 13:31:41 EEST 2014
|
|
||||||
Test Read/Write split router - hint routing
|
|
||||||
-------------------------------
|
|
||||||
-- maxscale route to master FAILED, return value 3003 when 3000 was expected
|
|
||||||
-- maxscale route to server server1 FAILED, return value 3003 when 3000 was expected
|
|
||||||
-- maxscale route to server server2 FAILED, return value 3003 when 3001 was expected
|
|
||||||
-- maxscale route to server server3 FAILED, return value 3003 when 3002 was expected
|
|
||||||
-- maxscale route to server server4 PASSED
|
|
||||||
-- maxscale max_slave_replication_lag = 100 FAILED, return value 3003 when was expected
|
|
||||||
-- maxscale max_slave_replication_lag= 100 FAILED, return value 3003 when was expected
|
|
||||||
-- maxscale max_slave_replication_lag =100 FAILED, return value 3003 when was expected
|
|
||||||
-- maxscale max_slave_replication_lag=100 FAILED, return value 3003 when was expected
|
|
||||||
|
|
20
server/modules/routing/readwritesplit/test/test_sescmd2.sql
Normal file
20
server/modules/routing/readwritesplit/test/test_sescmd2.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40101 SET NAMES utf8 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
16
server/modules/routing/readwritesplit/test/test_sescmd3.sql
Normal file
16
server/modules/routing/readwritesplit/test/test_sescmd3.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
select @OLD_SQL_NOTES;
|
||||||
|
select @OLD_CHARACTER_SET_CLIENT;
|
||||||
|
select @OLD_CHARACTER_SET_RESULTS;
|
||||||
|
select @OLD_COLLATION_CONNECTION;
|
||||||
|
select @OLD_TIME_ZONE;
|
||||||
|
select @OLD_UNIQUE_CHECKS;
|
||||||
|
select @OLD_FOREIGN_KEY_CHECKS;
|
||||||
|
select @OLD_SQL_MODE;
|
@ -0,0 +1,5 @@
|
|||||||
|
use test;
|
||||||
|
drop table if exists t1;
|
||||||
|
create temporary table t1 (id integer);
|
||||||
|
insert into t1 values(1);
|
||||||
|
select id from t1;
|
@ -3,6 +3,7 @@ include ../makefile.inc
|
|||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CPP = g++
|
CPP = g++
|
||||||
|
UTILS_PATH := $(ROOT_PATH)/utils
|
||||||
|
|
||||||
makeall: clean all
|
makeall: clean all
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ clean:
|
|||||||
- $(DEL) *~
|
- $(DEL) *~
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(CPP) -c $(CFLAGS) \
|
$(CPP) -c $(CFLAGS) -I$(UTILS_PATH) \
|
||||||
-fPIC skygw_utils.cc -o skygw_utils.o
|
-fPIC skygw_utils.cc -o skygw_utils.o
|
||||||
|
|
||||||
cleantests:
|
cleantests:
|
||||||
|
@ -123,7 +123,8 @@ typedef enum skygw_chk_t {
|
|||||||
CHK_NUM_SESCMD_CUR,
|
CHK_NUM_SESCMD_CUR,
|
||||||
CHK_NUM_BACKEND,
|
CHK_NUM_BACKEND,
|
||||||
CHK_NUM_BACKEND_REF,
|
CHK_NUM_BACKEND_REF,
|
||||||
CHK_NUM_PREP_STMT
|
CHK_NUM_PREP_STMT,
|
||||||
|
CHK_NUM_PINFO
|
||||||
} skygw_chk_t;
|
} skygw_chk_t;
|
||||||
|
|
||||||
# define STRBOOL(b) ((b) ? "true" : "false")
|
# define STRBOOL(b) ((b) ? "true" : "false")
|
||||||
@ -489,6 +490,13 @@ typedef enum skygw_chk_t {
|
|||||||
"Prepared statement struct has invalid check fields"); \
|
"Prepared statement struct has invalid check fields"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CHK_PARSING_INFO(p) { \
|
||||||
|
ss_info_dassert((p)->pi_chk_top == CHK_NUM_PINFO && \
|
||||||
|
(p)->pi_chk_tail == CHK_NUM_PINFO, \
|
||||||
|
"Parsing info struct has invalid check fields"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
bool conn_open[10240];
|
bool conn_open[10240];
|
||||||
|
@ -44,4 +44,6 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_ERROR_MSG PATH_MAX
|
||||||
|
|
||||||
#endif /* SKYGW_TYPES_H */
|
#endif /* SKYGW_TYPES_H */
|
||||||
|
@ -23,9 +23,10 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <regex.h>
|
||||||
#include "skygw_debug.h"
|
#include "skygw_debug.h"
|
||||||
#include "skygw_types.h"
|
#include <skygw_types.h>
|
||||||
#include "skygw_utils.h"
|
#include "skygw_utils.h"
|
||||||
|
|
||||||
const char* timestamp_formatstr = "%04d %02d/%02d %02d:%02d:%02d ";
|
const char* timestamp_formatstr = "%04d %02d/%02d %02d:%02d:%02d ";
|
||||||
@ -1863,3 +1864,104 @@ void skygw_file_done(
|
|||||||
free(file);
|
free(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the given needle - user-provided literal - and replace it with
|
||||||
|
* replacement string. Separate user-provided literals from matching table names
|
||||||
|
* etc. by searching only substrings preceded by non-letter and non-number.
|
||||||
|
*
|
||||||
|
* @param haystack Plain text query string, not to be freed
|
||||||
|
* @param needle Substring to be searched, not to be freed
|
||||||
|
* @param replacement Replacement text, not to be freed
|
||||||
|
*
|
||||||
|
* @return newly allocated string where needle is replaced
|
||||||
|
*/
|
||||||
|
char* replace_literal(
|
||||||
|
char* haystack,
|
||||||
|
const char* needle,
|
||||||
|
const char* replacement)
|
||||||
|
{
|
||||||
|
const char* prefix = "[ ='\",\\(]"; /*< ' ','=','(',''',''"',',' are allowed before needle */
|
||||||
|
const char* suffix = "([^[:alnum:]]|$)"; /*< alpha-num chars aren't allowed after the needle */
|
||||||
|
char* search_re;
|
||||||
|
char* newstr;
|
||||||
|
regex_t re;
|
||||||
|
regmatch_t match;
|
||||||
|
int rc;
|
||||||
|
size_t rlen = strlen(replacement);
|
||||||
|
size_t nlen = strlen(needle);
|
||||||
|
size_t hlen = strlen(haystack);
|
||||||
|
|
||||||
|
search_re = (char *)malloc(strlen(prefix)+nlen+strlen(suffix)+1);
|
||||||
|
|
||||||
|
if (search_re == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Regex memory allocation failed : %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
newstr = haystack;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(search_re, "%s%s%s", prefix, needle, suffix);
|
||||||
|
/** Allocate memory for new string +1 for terminating byte */
|
||||||
|
newstr = (char *)malloc(hlen-nlen+rlen+1);
|
||||||
|
|
||||||
|
if (newstr == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Regex memory allocation failed : %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
free(search_re);
|
||||||
|
free(newstr);
|
||||||
|
newstr = haystack;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = regcomp(&re, search_re, REG_EXTENDED|REG_ICASE);
|
||||||
|
ss_dassert(rc == 0);
|
||||||
|
|
||||||
|
if (rc != 0)
|
||||||
|
{
|
||||||
|
char error_message[MAX_ERROR_MSG];
|
||||||
|
regerror (rc, &re, error_message, MAX_ERROR_MSG);
|
||||||
|
fprintf(stderr,
|
||||||
|
"Regex error compiling '%s': %s\n",
|
||||||
|
search_re,
|
||||||
|
error_message);
|
||||||
|
free(search_re);
|
||||||
|
free(newstr);
|
||||||
|
newstr = haystack;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
rc = regexec(&re, haystack, 1, &match, 0);
|
||||||
|
|
||||||
|
if (rc != 0)
|
||||||
|
{
|
||||||
|
free(search_re);
|
||||||
|
free(newstr);
|
||||||
|
regfree(&re);
|
||||||
|
newstr = haystack;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
memcpy(newstr, haystack, match.rm_so+1);
|
||||||
|
memcpy(newstr+match.rm_so+1, replacement, rlen);
|
||||||
|
/** +1 is terminating byte */
|
||||||
|
memcpy(newstr+match.rm_so+1+rlen, haystack+match.rm_so+1+nlen, hlen-(match.rm_so+1)-nlen+1);
|
||||||
|
|
||||||
|
regfree(&re);
|
||||||
|
free(haystack);
|
||||||
|
free(search_re);
|
||||||
|
retblock:
|
||||||
|
return newstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,4 +192,12 @@ int skygw_rwlock_init(skygw_rwlock_t** rwlock);
|
|||||||
|
|
||||||
int atomic_add(int *variable, int value);
|
int atomic_add(int *variable, int value);
|
||||||
|
|
||||||
|
EXTERN_C_BLOCK_BEGIN
|
||||||
|
|
||||||
|
char* replace_literal(char* haystack,
|
||||||
|
const char* needle,
|
||||||
|
const char* replacement);
|
||||||
|
|
||||||
|
EXTERN_C_BLOCK_END
|
||||||
|
|
||||||
#endif /* SKYGW_UTILS_H */
|
#endif /* SKYGW_UTILS_H */
|
||||||
|
Reference in New Issue
Block a user