Hint parsing

This commit is contained in:
Mark Riddoch 2014-07-25 16:33:49 +01:00
parent 764ceac105
commit 2dea68b9b3
12 changed files with 1142 additions and 9 deletions

View File

@ -64,7 +64,6 @@ maxadmin: $(OBJ)
clean:
rm -f $(OBJ) maxadmin
- rm *.so
tags:
ctags $(SRCS) $(HDRS)

View File

@ -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)

View File

@ -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
View 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);
}

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

View 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

View 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;
}

View 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;
}

View 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