Hint parsing
This commit is contained in:
parent
764ceac105
commit
2dea68b9b3
@ -64,7 +64,6 @@ maxadmin: $(OBJ)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) maxadmin
|
||||
- rm *.so
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
@ -32,8 +32,9 @@
|
||||
# are behind SS_DEBUG macros.
|
||||
# 29/06/13 Vilho Raatikka Reverted Query classifier changes because
|
||||
# gateway needs mysql client lib, not qc.
|
||||
# 24/07/13 Mark Ridoch Addition of encryption routines
|
||||
# 30/05/14 Mark Ridoch Filter API added
|
||||
# 24/07/13 Mark Riddoch Addition of encryption routines
|
||||
# 30/05/14 Mark Riddoch Filter API added
|
||||
# 25/07/14 Mark Riddoch Addition of hints
|
||||
|
||||
include ../../build_gateway.inc
|
||||
|
||||
@ -57,7 +58,7 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \
|
||||
SRCS= atomic.c buffer.c spinlock.c gateway.c \
|
||||
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c \
|
||||
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \
|
||||
monitor.c adminusers.c secrets.c filter.c modutil.c
|
||||
monitor.c adminusers.c secrets.c filter.c modutil.c hint.c
|
||||
|
||||
HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
|
||||
../include/gw.h ../modules/include/mysql_client_server_protocol.h \
|
||||
@ -65,7 +66,7 @@ HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \
|
||||
../include/modules.h ../include/poll.h ../include/config.h \
|
||||
../include/users.h ../include/hashtable.h ../include/gwbitmask.h \
|
||||
../include/adminusers.h ../include/version.h ../include/maxscale.h \
|
||||
../include/filter.h modutil.h
|
||||
../include/filter.h modutil.h hint.h
|
||||
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
|
||||
|
@ -144,6 +144,7 @@ GWBUF *rval;
|
||||
rval->start = buf->start;
|
||||
rval->end = buf->end;
|
||||
rval->gwbuf_type = buf->gwbuf_type;
|
||||
rval->properties = NULL;
|
||||
rval->next = NULL;
|
||||
CHK_GWBUF(rval);
|
||||
return rval;
|
||||
@ -170,6 +171,7 @@ GWBUF *gwbuf_clone_portion(
|
||||
clonebuf->start = (void *)((char*)buf->start)+start_offset;
|
||||
clonebuf->end = (void *)((char *)clonebuf->start)+length;
|
||||
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */
|
||||
clonebuf->properties = NULL;
|
||||
clonebuf->next = NULL;
|
||||
CHK_GWBUF(clonebuf);
|
||||
return clonebuf;
|
||||
@ -427,3 +429,32 @@ int len;
|
||||
}
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add hint to a buffer.
|
||||
*
|
||||
* @param buf The buffer to add the hint to
|
||||
* @param hint The hint itself
|
||||
* @return Non-zero on success
|
||||
*/
|
||||
int
|
||||
gwbuf_add_hint(GWBUF *buf, HINT *hint)
|
||||
{
|
||||
HINT *ptr;
|
||||
|
||||
spinlock_acquire(&buf->lock);
|
||||
if (buf->hint)
|
||||
{
|
||||
ptr = buf->hint;
|
||||
while (ptr->next)
|
||||
ptr = ptr->next;
|
||||
ptr->next = hint;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf->hint = hint;
|
||||
}
|
||||
spinlock_release(&buf->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
136
server/core/hint.c
Normal file
136
server/core/hint.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* This file is distributed as part of MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2014
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <hint.h>
|
||||
|
||||
/**
|
||||
* @file hint.c generic support routines for hints.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 25/07/14 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Duplicate a list of hints
|
||||
*
|
||||
* @param hint The hint list to duplicate
|
||||
* @return A duplicate of the list
|
||||
*/
|
||||
HINT *
|
||||
hint_dup(HINT *hint)
|
||||
{
|
||||
HINT *nlhead = NULL, *nltail = NULL, *ptr1, *ptr2;
|
||||
|
||||
ptr1 = hint;
|
||||
while (ptr1)
|
||||
{
|
||||
if ((ptr2 = (HINT *)malloc(sizeof(HINT))) == NULL)
|
||||
return nlhead;
|
||||
ptr2->type = ptr1->type;
|
||||
if (ptr1->data)
|
||||
ptr2->data = strdup(ptr1->data);
|
||||
else
|
||||
ptr2->data = NULL;
|
||||
if (ptr1->value)
|
||||
ptr2->value = strdup(ptr1->value);
|
||||
else
|
||||
ptr2->data = NULL;
|
||||
ptr2->next = NULL;
|
||||
if (nltail)
|
||||
{
|
||||
nltail->next = ptr2;
|
||||
nltail = ptr2;
|
||||
}
|
||||
else
|
||||
{
|
||||
nlhead = ptr2;
|
||||
nltail = ptr2;
|
||||
}
|
||||
ptr1 = ptr1->next;
|
||||
}
|
||||
return nlhead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ROUTE TO type hint
|
||||
*
|
||||
* @param head The current hint list
|
||||
* @param type The HINT_TYPE
|
||||
* @param data Data may be NULL or the name of a server to route to
|
||||
* @return The result hint list
|
||||
*/
|
||||
HINT *
|
||||
hint_create_route(HINT *head, HINT_TYPE type, char *data)
|
||||
{
|
||||
HINT *hint;
|
||||
|
||||
if ((hint = (HINT *)malloc(sizeof(HINT))) == NULL)
|
||||
return head;
|
||||
hint->next = head;
|
||||
hint->type = type;
|
||||
if (data)
|
||||
hint->data = strdup(data);
|
||||
else
|
||||
hint->data = NULL;
|
||||
hint->value = NULL;
|
||||
return hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create name/value parameter hint
|
||||
*
|
||||
* @param head The current hint list
|
||||
* @param pname The parameter name
|
||||
* @param value The parameter value
|
||||
* @return The result hint list
|
||||
*/
|
||||
HINT *
|
||||
hint_create_parameter(HINT *head, char *pname, char *value)
|
||||
{
|
||||
HINT *hint;
|
||||
|
||||
if ((hint = (HINT *)malloc(sizeof(HINT))) == NULL)
|
||||
return head;
|
||||
hint->next = head;
|
||||
hint->type = HINT_PARAMETER;
|
||||
hint->data = strdup(pname);
|
||||
hint->value = strdup(value);
|
||||
return hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* free_hint - free a hint
|
||||
*
|
||||
* @param hint The hint to free
|
||||
*/
|
||||
void
|
||||
hint_free(HINT *hint)
|
||||
{
|
||||
if (hint->data)
|
||||
free(hint->data);
|
||||
if (hint->value)
|
||||
free(hint->value);
|
||||
free(hint);
|
||||
}
|
@ -45,7 +45,7 @@
|
||||
*/
|
||||
#include <spinlock.h>
|
||||
#include <skygw_debug.h>
|
||||
#include <hints.h>
|
||||
#include <hint.h>
|
||||
|
||||
|
||||
/**
|
||||
@ -143,4 +143,5 @@ extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type);
|
||||
extern int gwbuf_add_property(GWBUF *buf, char *name, char *value);
|
||||
extern char *gwbuf_get_property(GWBUF *buf, char *name);
|
||||
extern GWBUF *gwbuf_make_contiguous(GWBUF *);
|
||||
extern int gwbuf_add_hint(GWBUF *, HINT *);
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef _HINT_H
|
||||
#define _ATOMIC_H
|
||||
#define _HINT_H
|
||||
/*
|
||||
* This file is distributed as part of MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
@ -37,7 +37,8 @@ typedef enum {
|
||||
HINT_ROUTE_TO_MASTER = 1,
|
||||
HINT_ROUTE_TO_SLAVE,
|
||||
HINT_ROUTE_TO_NAMED_SERVER,
|
||||
HINT_ROUTE_TO_UPTODATE_SERVER
|
||||
HINT_ROUTE_TO_UPTODATE_SERVER,
|
||||
HINT_PARAMETER
|
||||
} HINT_TYPE;
|
||||
|
||||
/**
|
||||
@ -50,10 +51,14 @@ typedef enum {
|
||||
typedef struct hint {
|
||||
HINT_TYPE type; /*< The Type of hint */
|
||||
void *data; /*< Type sepecific data */
|
||||
void *value; /*< Parameter value for hint */
|
||||
unsigned int dsize; /*< Size of the hint data */
|
||||
struct hint *next; /*< Another hint for this buffer */
|
||||
} HINT;
|
||||
|
||||
extern HINT *hint_alloc(HINT_TYPE, void *, unsigned int);
|
||||
extern HINT *hint_create_parameter(HINT *, char *, char *);
|
||||
extern HINT *hint_create_route(HINT *, HINT_TYPE, char *);
|
||||
extern void hint_free(HINT *);
|
||||
extern HINT *hint_dup(HINT *);
|
||||
#endif
|
||||
|
@ -45,7 +45,7 @@ TEEOBJ=$(TEESRCS:.c=.o)
|
||||
SRCS=$(TESTSRCS) $(QLASRCS) $(REGEXSRCS) $(TOPNSRCS) $(TEESRCS)
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
||||
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so libtopfilter.so libtee.so
|
||||
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so libtopfilter.so libtee.so libhintfilter.so
|
||||
|
||||
|
||||
all: $(MODULES)
|
||||
@ -65,18 +65,25 @@ libtopfilter.so: $(TOPNOBJ)
|
||||
libtee.so: $(TEEOBJ)
|
||||
$(CC) $(LDFLAGS) $(TEEOBJ) $(LIBS) -o $@
|
||||
|
||||
libhintfilter.so:
|
||||
(cd hint; touch depend.mk ; make; cp $@ ..)
|
||||
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULES)
|
||||
(cd hint; make clean)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
(cd hint; make tags)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
(cd hint; make depend)
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/modules
|
||||
|
BIN
server/modules/filter/hint/.hintparser.c.swp
Normal file
BIN
server/modules/filter/hint/.hintparser.c.swp
Normal file
Binary file not shown.
70
server/modules/filter/hint/Makefile
Normal file
70
server/modules/filter/hint/Makefile
Normal file
@ -0,0 +1,70 @@
|
||||
# This file is distributed as part of MaxScale form SkySQL. It is free
|
||||
# software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation,
|
||||
# version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Copyright SkySQL Ab 2014
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 21/07/14 Mark Riddoch Initial module development
|
||||
|
||||
include ../../../../build_gateway.inc
|
||||
|
||||
LOGPATH := $(ROOT_PATH)/log_manager
|
||||
UTILSPATH := $(ROOT_PATH)/utils
|
||||
|
||||
CC=cc
|
||||
CFLAGS=-c -fPIC -I/usr/include -I../../include -I../../../include -I$(LOGPATH) \
|
||||
-I$(UTILSPATH) -Wall -g
|
||||
|
||||
include ../../../../makefile.inc
|
||||
|
||||
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
|
||||
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
|
||||
|
||||
SRCS= hintfilter.c hintparser.c
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
||||
|
||||
libhintfilter.so: $(OBJ)
|
||||
$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) libhintfilter.so
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/modules
|
||||
|
||||
cleantests:
|
||||
$(MAKE) -C test cleantests
|
||||
|
||||
buildtests:
|
||||
$(MAKE) -C test DEBUG=Y buildtests
|
||||
|
||||
runtests:
|
||||
$(MAKE) -C test runtests
|
||||
|
||||
testall:
|
||||
$(MAKE) -C test testall
|
||||
|
||||
include depend.mk
|
258
server/modules/filter/hint/hintfilter.c
Normal file
258
server/modules/filter/hint/hintfilter.c
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* This file is distributed as part of MaxScale by SkySQL. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2014
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <filter.h>
|
||||
#include <modinfo.h>
|
||||
#include <modutil.h>
|
||||
#include <mysqlhint.h>
|
||||
|
||||
/**
|
||||
* hintfilter.c - a filter to parse the MaxScale hint syntax and attach those
|
||||
* hints to the buffers that carry the requests.
|
||||
*
|
||||
*/
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_FILTER,
|
||||
MODULE_ALPHA_RELEASE,
|
||||
FILTER_VERSION,
|
||||
"A hint parsing filter"
|
||||
};
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static FILTER *createInstance(char **options, FILTER_PARAMETER **params);
|
||||
static void *newSession(FILTER *instance, SESSION *session);
|
||||
static void closeSession(FILTER *instance, void *session);
|
||||
static void freeSession(FILTER *instance, void *session);
|
||||
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
|
||||
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
|
||||
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
|
||||
|
||||
|
||||
static FILTER_OBJECT MyObject = {
|
||||
createInstance,
|
||||
newSession,
|
||||
closeSession,
|
||||
freeSession,
|
||||
setDownstream,
|
||||
NULL, // No upstream requirement
|
||||
routeQuery,
|
||||
NULL,
|
||||
diagnostic,
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
FILTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the filter for a particular service
|
||||
* within MaxScale.
|
||||
*
|
||||
* @param options The options for this filter
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static FILTER *
|
||||
createInstance(char **options, FILTER_PARAMETER **params)
|
||||
{
|
||||
HINT_INSTANCE *my_instance;
|
||||
|
||||
if ((my_instance = calloc(1, sizeof(HINT_INSTANCE))) != NULL)
|
||||
my_instance->sessions = 0;
|
||||
return (FILTER *)my_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the filter.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(FILTER *instance, SESSION *session)
|
||||
{
|
||||
HINT_INSTANCE *my_instance = (HINT_INSTANCE *)instance;
|
||||
HINT_SESSION *my_session;
|
||||
|
||||
if ((my_session = calloc(1, sizeof(HINT_SESSION))) != NULL)
|
||||
{
|
||||
my_session->query_len = 0;
|
||||
my_session->request = NULL;
|
||||
my_session->stack = NULL;
|
||||
my_session->named_hints = NULL;
|
||||
}
|
||||
|
||||
return my_session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the filter, this is the mechanism
|
||||
* by which a filter may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(FILTER *instance, void *session)
|
||||
{
|
||||
HINT_SESSION *my_session = (HINT_SESSION *)session;
|
||||
|
||||
if (my_session->request)
|
||||
gwbuf_free(my_session->request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the memory associated with this filter session.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void
|
||||
freeSession(FILTER *instance, void *session)
|
||||
{
|
||||
free(session);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the downstream component for this filter.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The session being closed
|
||||
* @param downstream The downstream filter or router
|
||||
*/
|
||||
static void
|
||||
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
|
||||
{
|
||||
HINT_SESSION *my_session = (HINT_SESSION *)session;
|
||||
|
||||
my_session->down = *downstream;
|
||||
}
|
||||
|
||||
/**
|
||||
* The routeQuery entry point. This is passed the query buffer
|
||||
* to which the filter should be applied. Once applied the
|
||||
* query shoudl normally be passed to the downstream component
|
||||
* (filter or router) in the filter chain.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The filter session
|
||||
* @param queue The query data
|
||||
*/
|
||||
static int
|
||||
routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
HINT_SESSION *my_session = (HINT_SESSION *)session;
|
||||
char *ptr;
|
||||
int rval, len, residual;
|
||||
HINT *hint;
|
||||
|
||||
if (my_session->request == NULL)
|
||||
{
|
||||
/*
|
||||
* No stored buffer, so this must be the first
|
||||
* buffer of a new request.
|
||||
*/
|
||||
if (modutil_MySQL_Query(queue, &ptr, &len, &residual) == 0)
|
||||
{
|
||||
return my_session->down.routeQuery(
|
||||
my_session->down.instance,
|
||||
my_session->down.session, queue);
|
||||
}
|
||||
my_session->request = queue;
|
||||
my_session->query_len = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
gwbuf_append(my_session->request, queue);
|
||||
}
|
||||
|
||||
if (gwbuf_length(my_session->request) < my_session->query_len)
|
||||
{
|
||||
/*
|
||||
* We have not got the entire SQL text, buffer and wait for
|
||||
* the remainder.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
/* We have the entire SQL text, parse for hints and attach to the
|
||||
* buffer at the head of the queue.
|
||||
*/
|
||||
queue = my_session->request;
|
||||
my_session->request = NULL;
|
||||
my_session->query_len = 0;
|
||||
hint = hint_parser(my_session, queue);
|
||||
queue->hint = hint;
|
||||
|
||||
/* Now process the request */
|
||||
rval = my_session->down.routeQuery(my_session->down.instance,
|
||||
my_session->down.session, queue);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
* If fsession is NULL then print diagnostics on the filter
|
||||
* instance as a whole, otherwise print diagnostics for the
|
||||
* particular session.
|
||||
*
|
||||
* @param instance The filter instance
|
||||
* @param fsession Filter session, may be NULL
|
||||
* @param dcb The DCB for diagnostic output
|
||||
*/
|
||||
static void
|
||||
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
|
||||
{
|
||||
HINT_INSTANCE *my_instance = (HINT_INSTANCE *)instance;
|
||||
HINT_SESSION *my_session = (HINT_SESSION *)fsession;
|
||||
|
||||
}
|
515
server/modules/filter/hint/hintparser.c
Normal file
515
server/modules/filter/hint/hintparser.c
Normal file
@ -0,0 +1,515 @@
|
||||
/*
|
||||
* This file is distributed as part of MaxScale by SkySQL. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2014
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <filter.h>
|
||||
#include <modinfo.h>
|
||||
#include <modutil.h>
|
||||
#include <mysqlhint.h>
|
||||
|
||||
/**
|
||||
* hintparser.c - Find any comment in the SQL packet and look for MAXSCALE
|
||||
* hints in that comment.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The keywords in the hint syntax
|
||||
*/
|
||||
struct {
|
||||
char *keyword;
|
||||
TOKEN_VALUE token;
|
||||
} keywords[] = {
|
||||
{ "maxscale", TOK_MAXSCALE },
|
||||
{ "prepare", TOK_PREPARE },
|
||||
{ "start", TOK_START },
|
||||
{ "begin", TOK_START },
|
||||
{ "stop", TOK_STOP },
|
||||
{ "end", TOK_STOP },
|
||||
{ "=", TOK_EQUAL },
|
||||
{ "route", TOK_ROUTE },
|
||||
{ "to", TOK_TO },
|
||||
{ "master", TOK_MASTER },
|
||||
{ "slave", TOK_SLAVE },
|
||||
{ "server", TOK_SERVER },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static HINT_TOKEN *hint_next_token(GWBUF **buf, char **ptr);
|
||||
static void hint_pop(HINT_SESSION *);
|
||||
static HINT *lookup_named_hint(HINT_SESSION *, char *);
|
||||
static void create_named_hint(HINT_SESSION *, char *, HINT *);
|
||||
static void hint_push(HINT_SESSION *, HINT *);
|
||||
|
||||
typedef enum { HM_EXECUTE, HM_START, HM_PREPARE } HINT_MODE;
|
||||
|
||||
/**
|
||||
* Parse the hint comments in the MySQL statement passed in request.
|
||||
* Add any hints to the buffer for later processing.
|
||||
*
|
||||
* @param session The filter session
|
||||
* @param request The MySQL request buffer
|
||||
* @return The hints parsed in this statement or active on the
|
||||
* stack
|
||||
*/
|
||||
HINT *
|
||||
hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
{
|
||||
char *ptr, lastch = ' ';
|
||||
int len, residual, state;
|
||||
int found, escape, quoted, squoted;
|
||||
HINT *rval = NULL;
|
||||
char *pname, *lvalue, *hintname = NULL;
|
||||
GWBUF *buf;
|
||||
HINT_TOKEN *tok;
|
||||
HINT_MODE mode = HM_EXECUTE;
|
||||
|
||||
/* First look for any comment in the SQL */
|
||||
modutil_MySQL_Query(request, &ptr, &len, &residual);
|
||||
buf = request;
|
||||
found = 0;
|
||||
escape = 0;
|
||||
quoted = 0;
|
||||
squoted = 0;
|
||||
do {
|
||||
while (len--)
|
||||
{
|
||||
if (*ptr == '\\')
|
||||
escape = 1;
|
||||
else if (*ptr == '\"' && quoted)
|
||||
quoted = 0;
|
||||
else if (*ptr == '\"' && quoted == 0)
|
||||
quoted = 0;
|
||||
else if (*ptr == '\'' && squoted)
|
||||
squoted = 0;
|
||||
else if (*ptr == '\"' && squoted == 0)
|
||||
squoted = 0;
|
||||
else if (quoted || squoted)
|
||||
;
|
||||
else if (escape)
|
||||
escape = 0;
|
||||
else if (*ptr == '#')
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
else if (*ptr == '/')
|
||||
lastch = '/';
|
||||
else if (*ptr == '*' && lastch == '/')
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
else if (*ptr == '-' && lastch == '-')
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
else if (*ptr == '-')
|
||||
lastch = '-';
|
||||
else
|
||||
lastch = *ptr;
|
||||
ptr++;
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
|
||||
buf = buf->next;
|
||||
if (buf)
|
||||
{
|
||||
len = GWBUF_LENGTH(buf);
|
||||
ptr = GWBUF_DATA(buf);
|
||||
}
|
||||
} while (buf);
|
||||
|
||||
if (!found) /* No comment so we need do no more */
|
||||
{
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have got here then we have a comment, ptr point to
|
||||
* the comment character if it is a '#' comment or the second
|
||||
* character of the comment if it is a -- or /* comment
|
||||
*
|
||||
* Move to the next character in the SQL.
|
||||
*/
|
||||
ptr++;
|
||||
if (ptr > (char *)(buf->end))
|
||||
{
|
||||
buf = buf->next;
|
||||
if (buf)
|
||||
ptr = GWBUF_DATA(buf);
|
||||
else
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
tok = hint_next_token(&buf, &ptr);
|
||||
if (tok->token != TOK_MAXSCALE)
|
||||
{
|
||||
free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
state = HS_INIT;
|
||||
while ((tok = hint_next_token(&buf, &ptr)) != NULL
|
||||
&& tok->token != TOK_EOL)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case HS_INIT:
|
||||
switch (tok->token)
|
||||
{
|
||||
case TOK_ROUTE:
|
||||
state = HS_ROUTE;
|
||||
break;
|
||||
case TOK_STRING:
|
||||
state = HS_NAME;
|
||||
lvalue = tok->value;
|
||||
break;
|
||||
case TOK_STOP:
|
||||
/* Action: pop active hint */
|
||||
hint_pop(session);
|
||||
state = HS_INIT;
|
||||
break;
|
||||
default:
|
||||
/* Error: expected hint, name or STOP */
|
||||
;
|
||||
}
|
||||
break;
|
||||
case HS_ROUTE:
|
||||
if (tok->token != TOK_TO)
|
||||
/* Error, expect TO */;
|
||||
state = HS_ROUTE1;
|
||||
break;
|
||||
case HS_ROUTE1:
|
||||
switch (tok->token)
|
||||
{
|
||||
case TOK_MASTER:
|
||||
rval = hint_create_route(rval,
|
||||
HINT_ROUTE_TO_MASTER, NULL);
|
||||
break;
|
||||
case TOK_SLAVE:
|
||||
rval = hint_create_route(rval,
|
||||
HINT_ROUTE_TO_SLAVE, NULL);
|
||||
break;
|
||||
case TOK_SERVER:
|
||||
state = HS_ROUTE_SERVER;
|
||||
break;
|
||||
default:
|
||||
/* Error expected MASTER, SLAVE or SERVER */
|
||||
;
|
||||
}
|
||||
break;
|
||||
case HS_ROUTE_SERVER:
|
||||
if (tok->token == TOK_STRING)
|
||||
{
|
||||
rval = hint_create_route(rval,
|
||||
HINT_ROUTE_TO_NAMED_SERVER, tok->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Error: Expected server name */
|
||||
}
|
||||
case HS_NAME:
|
||||
switch (tok->token)
|
||||
{
|
||||
case TOK_EQUAL:
|
||||
pname = lvalue;
|
||||
state = HS_PVALUE;
|
||||
break;
|
||||
case TOK_PREPARE:
|
||||
pname = lvalue;
|
||||
state = HS_PREPARE;
|
||||
break;
|
||||
case TOK_START:
|
||||
/* Action start(lvalue) */
|
||||
hintname = lvalue;
|
||||
mode = HM_START;
|
||||
state = HS_INIT;
|
||||
break;
|
||||
default:
|
||||
/* Error, token tok->value not expected */
|
||||
;
|
||||
}
|
||||
break;
|
||||
case HS_PVALUE:
|
||||
/* Action: pname = tok->value */
|
||||
rval = hint_create_parameter(rval, pname, tok->value);
|
||||
state = HS_INIT;
|
||||
break;
|
||||
case HS_PREPARE:
|
||||
mode = HM_PREPARE;
|
||||
hintname = lvalue;
|
||||
switch (tok->token)
|
||||
{
|
||||
case TOK_ROUTE:
|
||||
state = HS_ROUTE;
|
||||
break;
|
||||
case TOK_STRING:
|
||||
state = HS_NAME;
|
||||
lvalue = tok->value;
|
||||
break;
|
||||
default:
|
||||
/* Error, token tok->value not expected */
|
||||
;
|
||||
}
|
||||
break;
|
||||
}
|
||||
free(tok);
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case HM_START:
|
||||
/*
|
||||
* We are starting either a predefined set of hints,
|
||||
* creating a new set of hints and starting in a single
|
||||
* operation or starting an annonymous block of hints.
|
||||
*/
|
||||
if (hintname == NULL && rval != NULL)
|
||||
{
|
||||
/* We are starting an anonymous block of hints */
|
||||
hint_push(session, rval);
|
||||
rval = NULL;
|
||||
} else if (hintname && rval)
|
||||
{
|
||||
/* We are creating and starting a block of hints */
|
||||
if (lookup_named_hint(session, hintname) != NULL)
|
||||
{
|
||||
/* Error hint with this name already exists */
|
||||
}
|
||||
else
|
||||
{
|
||||
create_named_hint(session, hintname, rval);
|
||||
hint_push(session, hint_dup(rval));
|
||||
}
|
||||
} else if (hintname && rval == NULL)
|
||||
{
|
||||
/* We starting an already define set of named hints */
|
||||
rval = lookup_named_hint(session, hintname);
|
||||
hint_push(session, hint_dup(rval));
|
||||
rval = NULL;
|
||||
} else if (hintname == NULL && rval == NULL)
|
||||
{
|
||||
/* Error case */
|
||||
}
|
||||
break;
|
||||
case HM_PREPARE:
|
||||
/*
|
||||
* We are preparing a named set of hints. Note this does
|
||||
* not trigger the usage of these hints currently.
|
||||
*/
|
||||
if (hintname == NULL || rval == NULL)
|
||||
{
|
||||
/* Error case, name and hints must be defined */
|
||||
}
|
||||
else
|
||||
{
|
||||
create_named_hint(session, hintname, rval);
|
||||
}
|
||||
/* We are not starting the hints now, so return an empty
|
||||
* hint set.
|
||||
*/
|
||||
rval = NULL;
|
||||
break;
|
||||
case HM_EXECUTE:
|
||||
/*
|
||||
* We have a one-off hint for the statement we are
|
||||
* currently forwarding.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
retblock:
|
||||
if (rval == NULL)
|
||||
{
|
||||
/* No new hint parsed in this statement, apply the current
|
||||
* top of stack if there is one.
|
||||
*/
|
||||
if (session->stack)
|
||||
rval = hint_dup(session->stack->hint);
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next token in the inout stream
|
||||
*
|
||||
* @param buf A pointer to the buffer point, will be updated if a
|
||||
* new buffer is used.
|
||||
* @param ptr The pointer within the buffer we are processing
|
||||
* @return A HINT token
|
||||
*/
|
||||
static HINT_TOKEN *
|
||||
hint_next_token(GWBUF **buf, char **ptr)
|
||||
{
|
||||
char word[100], *dest;
|
||||
int inword = 0;
|
||||
char inquote = '\0';
|
||||
int i, found;
|
||||
HINT_TOKEN *tok;
|
||||
|
||||
if ((tok = (HINT_TOKEN *)malloc(sizeof(HINT_TOKEN))) == NULL)
|
||||
return NULL;
|
||||
tok->value = NULL;
|
||||
dest = word;
|
||||
while (*ptr <= (char *)((*buf)->end) || (*buf)->next)
|
||||
{
|
||||
if (inword && inquote == '\0' &&
|
||||
(**ptr == '=' || isspace(**ptr)))
|
||||
{
|
||||
inword = 0;
|
||||
break;
|
||||
}
|
||||
else if (**ptr == '\'' && inquote == '\'')
|
||||
inquote = '\0';
|
||||
else if (**ptr == '\'')
|
||||
inquote = **ptr;
|
||||
else if (inword || (isspace(**ptr) == 0))
|
||||
{
|
||||
*dest++ = **ptr;
|
||||
inword = 1;
|
||||
}
|
||||
(*ptr)++;
|
||||
if (*ptr > (char *)((*buf)->end) && (*buf)->next)
|
||||
{
|
||||
*buf = (*buf)->next;
|
||||
*ptr = (*buf)->start;
|
||||
}
|
||||
if (dest - word > 98)
|
||||
break;
|
||||
|
||||
}
|
||||
*dest = 0;
|
||||
|
||||
/* We now have a word in the local word, check to see if it is a
|
||||
* token we recognise.
|
||||
*/
|
||||
if (word[0] == '\0')
|
||||
{
|
||||
tok->token = TOK_EOL;
|
||||
return tok;
|
||||
}
|
||||
found = 0;
|
||||
for (i = 0; keywords[i].keyword; i++)
|
||||
{
|
||||
if (strcasecmp(word, keywords[i].keyword) == 0)
|
||||
{
|
||||
tok->token = keywords[i].token;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == 0)
|
||||
{
|
||||
tok->token = TOK_STRING;
|
||||
tok->value = strdup(word);
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hint_pop - pop the hint off the top of the stack if it is not empty
|
||||
*
|
||||
* @param session The filter session.
|
||||
*/
|
||||
void
|
||||
hint_pop(HINT_SESSION *session)
|
||||
{
|
||||
HINTSTACK *ptr;
|
||||
HINT *hint;
|
||||
|
||||
if ((ptr = session->stack) != NULL)
|
||||
{
|
||||
session->stack = ptr->next;
|
||||
while (ptr->hint)
|
||||
{
|
||||
hint = ptr->hint;
|
||||
ptr->hint = hint->next;
|
||||
hint_free(hint);
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a hint onto the stack of actie hints
|
||||
*
|
||||
* @param session The filter session
|
||||
* @param hint The hint to push, the hint ownership is retained
|
||||
* by the stack and should not be freed by the caller
|
||||
*/
|
||||
static void
|
||||
hint_push(HINT_SESSION *session, HINT *hint)
|
||||
{
|
||||
HINTSTACK *item;
|
||||
|
||||
if ((item = (HINTSTACK *)malloc(sizeof(HINTSTACK))) == NULL)
|
||||
return;
|
||||
item->hint = hint;
|
||||
item->next = session->stack;
|
||||
session->stack = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a hint block that already exists with this name
|
||||
*
|
||||
* @param session The filter session
|
||||
* @param name The name to lookup
|
||||
* @return the HINT or NULL if the name was not found.
|
||||
*/
|
||||
static HINT *
|
||||
lookup_named_hint(HINT_SESSION *session, char *name)
|
||||
{
|
||||
NAMEDHINTS *ptr = session->named_hints;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
if (strcmp(ptr->name, name) == 0)
|
||||
return ptr->hints;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a named hint block
|
||||
*
|
||||
* @param session The filter session
|
||||
* @param name The name of the block to ceate
|
||||
* @param hint The hints themselves
|
||||
*/
|
||||
static void
|
||||
create_named_hint(HINT_SESSION *session, char *name, HINT *hint)
|
||||
{
|
||||
NAMEDHINTS *block;
|
||||
|
||||
if ((block = (NAMEDHINTS *)malloc(sizeof(NAMEDHINTS))) == NULL)
|
||||
return;
|
||||
if ((block->name = strdup(name)) == NULL)
|
||||
{
|
||||
free(block);
|
||||
return;
|
||||
}
|
||||
block->hints = hint;
|
||||
block->next = session->named_hints;
|
||||
session->named_hints = block;
|
||||
}
|
110
server/modules/include/mysqlhint.h
Normal file
110
server/modules/include/mysqlhint.h
Normal file
@ -0,0 +1,110 @@
|
||||
#ifndef _MYSQLHINT_H
|
||||
#define _MYSQLHINT_H
|
||||
/*
|
||||
* This file is distributed as part of the SkySQL Gateway. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2013
|
||||
*/
|
||||
|
||||
/*
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 17-07-2014 Mark Riddoch Initial implementation
|
||||
*/
|
||||
#include <hint.h>
|
||||
|
||||
/* Parser tokens for the hint parser */
|
||||
typedef enum {
|
||||
TOK_MAXSCALE = 1,
|
||||
TOK_PREPARE,
|
||||
TOK_START,
|
||||
TOK_STOP,
|
||||
TOK_EQUAL,
|
||||
TOK_STRING,
|
||||
TOK_ROUTE,
|
||||
TOK_TO,
|
||||
TOK_MASTER,
|
||||
TOK_SLAVE,
|
||||
TOK_SERVER,
|
||||
TOK_EOL
|
||||
} TOKEN_VALUE;
|
||||
|
||||
/* The tokenising return type */
|
||||
typedef struct {
|
||||
TOKEN_VALUE token; // The token itself
|
||||
char *value; // The string version of the token
|
||||
} HINT_TOKEN;
|
||||
|
||||
/**
|
||||
* A named hint set.
|
||||
*
|
||||
* The hint "MaxScale name PREPARE ..." can be used to defined a named set
|
||||
* of hints that can be later applied.
|
||||
*/
|
||||
typedef struct namedhints {
|
||||
char *name; /*< Hintsets name */
|
||||
HINT *hints;
|
||||
struct namedhints
|
||||
*next; /*< Next named hint */
|
||||
} NAMEDHINTS;
|
||||
|
||||
/**
|
||||
* A session meaintains a stack of hints, the hints BEGIN and STOP are used
|
||||
* push hints on and off the stack. The current top of the stack is added to
|
||||
* any statement that does not explicitly define a hint for that signle
|
||||
* statement.
|
||||
*/
|
||||
typedef struct hintstack {
|
||||
HINT *hint;
|
||||
struct hintstack
|
||||
*next;
|
||||
} HINTSTACK;
|
||||
|
||||
/**
|
||||
* The hint instance structure
|
||||
*/
|
||||
typedef struct {
|
||||
int sessions;
|
||||
} HINT_INSTANCE;
|
||||
|
||||
/**
|
||||
* A hint parser session structure
|
||||
*/
|
||||
typedef struct {
|
||||
DOWNSTREAM down;
|
||||
GWBUF *request;
|
||||
int query_len;
|
||||
HINTSTACK *stack;
|
||||
NAMEDHINTS *named_hints; /* The named hints defined in this session */
|
||||
} HINT_SESSION;
|
||||
|
||||
/* Some useful macros */
|
||||
#define CURRENT_HINT(session) ((session)->stack ? \
|
||||
(session)->stack->hints : NULL)
|
||||
|
||||
/* Hint Parser State Machine */
|
||||
#define HS_INIT 0
|
||||
#define HS_ROUTE 1
|
||||
#define HS_ROUTE1 2
|
||||
#define HS_ROUTE_SERVER 3
|
||||
#define HS_NAME 4
|
||||
#define HS_PVALUE 5
|
||||
#define HS_PREPARE 6
|
||||
|
||||
|
||||
extern HINT *hint_parser(HINT_SESSION *session, GWBUF *request);
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user