Hint parsing
This commit is contained in:
@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user