MXS-2302: Remove unused parsing code
Removed all code that is no longer used. The hint stack mechanism is still in use but can, and should, be replaced with std::vector.
This commit is contained in:
@ -13,649 +13,17 @@
|
||||
|
||||
#define MXS_MODULE_NAME "hintfilter"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <maxscale/filter.hh>
|
||||
#include <maxscale/modinfo.h>
|
||||
#include <maxscale/modutil.hh>
|
||||
#include <maxscale/buffer.hh>
|
||||
|
||||
#include "mysqlhint.hh"
|
||||
#include <maxscale/alloc.h>
|
||||
|
||||
/**
|
||||
* hintparser.c - Find any comment in the SQL packet and look for MAXSCALE
|
||||
* hints in that comment.
|
||||
* Code for parsing SQL comments and processing them into MaxScale hints
|
||||
*/
|
||||
|
||||
/**
|
||||
* The keywords in the hint syntax
|
||||
*/
|
||||
static struct
|
||||
{
|
||||
const 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 },
|
||||
{"last", TOK_LAST },
|
||||
{NULL, static_cast<TOKEN_VALUE>(0)}
|
||||
};
|
||||
|
||||
static HINT_TOKEN* hint_next_token(GWBUF** buf, char** ptr);
|
||||
static void hint_pop(HINT_SESSION*);
|
||||
static HINT* lookup_named_hint(HINT_SESSION*, const char*);
|
||||
static void create_named_hint(HINT_SESSION*, const char*, HINT*);
|
||||
static void hint_push(HINT_SESSION*, HINT*);
|
||||
static const char* token_get_keyword(HINT_TOKEN* token);
|
||||
static void token_free(HINT_TOKEN* token);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HM_EXECUTE,
|
||||
HM_START,
|
||||
HM_PREPARE
|
||||
} HINT_MODE;
|
||||
|
||||
void token_free(HINT_TOKEN* token)
|
||||
{
|
||||
if (token->value != NULL)
|
||||
{
|
||||
MXS_FREE(token->value);
|
||||
}
|
||||
MXS_FREE(token);
|
||||
}
|
||||
|
||||
static const char* token_get_keyword(HINT_TOKEN* token)
|
||||
{
|
||||
switch (token->token)
|
||||
{
|
||||
case TOK_END:
|
||||
return "End of hint";
|
||||
break;
|
||||
|
||||
case TOK_LINEBRK:
|
||||
return "End of line";
|
||||
break;
|
||||
|
||||
case TOK_STRING:
|
||||
return token->value;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
int i = 0;
|
||||
while (i < TOK_LINEBRK && keywords[i].token != token->token)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
mxb_assert(i != TOK_LINEBRK);
|
||||
|
||||
if (i == TOK_LINEBRK)
|
||||
{
|
||||
return "Unknown token";
|
||||
}
|
||||
else
|
||||
{
|
||||
return keywords[i].keyword;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the hint comments in the MySQL statement passed in request.
|
||||
* Add any hints to the buffer for later processing.
|
||||
*
|
||||
* @param session The filter session
|
||||
* @param request The MySQL request buffer
|
||||
* @return The hints parsed in this statement or active on the
|
||||
* stack
|
||||
*/
|
||||
HINT* hint_parser(HINT_SESSION* session, GWBUF* request)
|
||||
{
|
||||
char* ptr, lastch = ' ';
|
||||
int len, residual, state;
|
||||
int found, escape, quoted, squoted;
|
||||
HINT* rval = NULL;
|
||||
char* pname = nullptr;
|
||||
char* lvalue = nullptr;
|
||||
char* hintname = nullptr;
|
||||
GWBUF* buf;
|
||||
HINT_TOKEN* tok;
|
||||
HINT_MODE mode = HM_EXECUTE;
|
||||
bool multiline_comment = false;
|
||||
/* 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;
|
||||
multiline_comment = true;
|
||||
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 = (char*)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 = (char*)GWBUF_DATA(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
|
||||
tok = hint_next_token(&buf, &ptr);
|
||||
|
||||
if (tok == NULL)
|
||||
{
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
/** This is not MaxScale hint because it doesn't start with 'maxscale' */
|
||||
if (tok->token != TOK_MAXSCALE)
|
||||
{
|
||||
token_free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
token_free(tok);
|
||||
|
||||
state = HS_INIT;
|
||||
|
||||
while (((tok = hint_next_token(&buf, &ptr)) != NULL)
|
||||
&& (tok->token != TOK_END))
|
||||
{
|
||||
if (tok->token == TOK_LINEBRK)
|
||||
{
|
||||
if (multiline_comment)
|
||||
{
|
||||
// Skip token
|
||||
token_free(tok);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treat as TOK_END
|
||||
tok->token = TOK_END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case HS_INIT:
|
||||
switch (tok->token)
|
||||
{
|
||||
case TOK_ROUTE:
|
||||
state = HS_ROUTE;
|
||||
break;
|
||||
|
||||
case TOK_STRING:
|
||||
state = HS_NAME;
|
||||
lvalue = MXS_STRDUP_A(tok->value);
|
||||
break;
|
||||
|
||||
case TOK_STOP:
|
||||
/* Action: pop active hint */
|
||||
hint_pop(session);
|
||||
state = HS_INIT;
|
||||
break;
|
||||
|
||||
case TOK_START:
|
||||
hintname = NULL;
|
||||
mode = HM_START;
|
||||
state = HS_INIT;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Error: expected hint, name or STOP */
|
||||
MXS_ERROR("Syntax error in hint. Expected "
|
||||
"'route', 'stop' or hint name instead of "
|
||||
"'%s'. Hint ignored.",
|
||||
token_get_keyword(tok));
|
||||
token_free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
break;
|
||||
|
||||
case HS_ROUTE:
|
||||
if (tok->token != TOK_TO)
|
||||
{
|
||||
/* Error, expect TO */
|
||||
MXS_ERROR("Syntax error in hint. Expected "
|
||||
"'to' instead of '%s'. Hint ignored.",
|
||||
token_get_keyword(tok));
|
||||
token_free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
state = HS_ROUTE1;
|
||||
break;
|
||||
|
||||
case HS_ROUTE1:
|
||||
switch (tok->token)
|
||||
{
|
||||
case TOK_MASTER:
|
||||
rval = hint_create_route(rval,
|
||||
HINT_ROUTE_TO_MASTER,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case TOK_SLAVE:
|
||||
rval = hint_create_route(rval,
|
||||
HINT_ROUTE_TO_SLAVE,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case TOK_LAST:
|
||||
rval = hint_create_route(rval,
|
||||
HINT_ROUTE_TO_LAST_USED,
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case TOK_SERVER:
|
||||
state = HS_ROUTE_SERVER;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Error expected MASTER, SLAVE or SERVER */
|
||||
MXS_ERROR("Syntax error in hint. Expected "
|
||||
"'master', 'slave', or 'server' instead "
|
||||
"of '%s'. Hint ignored.",
|
||||
token_get_keyword(tok));
|
||||
|
||||
token_free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
break;
|
||||
|
||||
case HS_ROUTE_SERVER:
|
||||
if (tok->token == TOK_STRING)
|
||||
{
|
||||
rval = hint_create_route(rval,
|
||||
HINT_ROUTE_TO_NAMED_SERVER,
|
||||
tok->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Error: Expected server name */
|
||||
MXS_ERROR("Syntax error in hint. Expected "
|
||||
"server name instead of '%s'. Hint "
|
||||
"ignored.",
|
||||
token_get_keyword(tok));
|
||||
token_free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
break;
|
||||
|
||||
case HS_NAME:
|
||||
switch (tok->token)
|
||||
{
|
||||
case TOK_EQUAL:
|
||||
pname = lvalue;
|
||||
lvalue = NULL;
|
||||
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 */
|
||||
MXS_ERROR("Syntax error in hint. Expected "
|
||||
"'=', 'prepare', or 'start' instead of "
|
||||
"'%s'. Hint ignored.",
|
||||
token_get_keyword(tok));
|
||||
token_free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
break;
|
||||
|
||||
case HS_PVALUE:
|
||||
/* Action: pname = tok->value */
|
||||
rval = hint_create_parameter(rval, pname, tok->value);
|
||||
MXS_FREE(pname);
|
||||
pname = NULL;
|
||||
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 */
|
||||
MXS_ERROR("Syntax error in hint. Expected "
|
||||
"'route' or hint name instead of "
|
||||
"'%s'. Hint ignored.",
|
||||
token_get_keyword(tok));
|
||||
token_free(tok);
|
||||
goto retblock;
|
||||
}
|
||||
break;
|
||||
}
|
||||
token_free(tok);
|
||||
} /*< while */
|
||||
|
||||
if (tok && tok->token == TOK_END)
|
||||
{
|
||||
token_free(tok);
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case HM_START:
|
||||
/*
|
||||
* We are starting either a predefined set of hints,
|
||||
* creating a new set of hints and starting in a single
|
||||
* operation or starting an anonymous 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:
|
||||
MXS_FREE(hintname);
|
||||
|
||||
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;
|
||||
int endtag = 0;
|
||||
char inquote = '\0';
|
||||
int i, found;
|
||||
HINT_TOKEN* tok;
|
||||
|
||||
if ((tok = (HINT_TOKEN*)MXS_MALLOC(sizeof(HINT_TOKEN))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
tok->value = NULL;
|
||||
dest = word;
|
||||
while (*ptr < (char*)((*buf)->end) || (*buf)->next)
|
||||
{
|
||||
/** word ends, don't move ptr but return with read word */
|
||||
if (inword && inquote == '\0'
|
||||
&& (isspace(**ptr) || **ptr == '='))
|
||||
{
|
||||
inword = 0;
|
||||
break;
|
||||
}
|
||||
/** found '=' or '\n', move ptr and return with the char */
|
||||
else if (!inword && inquote == '\0' && ((**ptr == '=') || (**ptr == '\n')))
|
||||
{
|
||||
*dest = **ptr;
|
||||
dest++;
|
||||
(*ptr)++;
|
||||
break;
|
||||
}
|
||||
else if (**ptr == '\'' && inquote == '\'')
|
||||
{
|
||||
inquote = '\0';
|
||||
}
|
||||
else if (**ptr == '\'')
|
||||
{
|
||||
inquote = **ptr;
|
||||
}
|
||||
/** Any other character which belongs to the word, move ahead */
|
||||
|
||||
else if (**ptr == '/' && endtag)
|
||||
{
|
||||
/** Comment end tag found, rewind the pointer back and return the token */
|
||||
inword = 0;
|
||||
(*ptr)--;
|
||||
break;
|
||||
}
|
||||
else if (**ptr == '*' && !endtag)
|
||||
{
|
||||
endtag = 1;
|
||||
}
|
||||
else if (inword || (isspace(**ptr) == 0))
|
||||
{
|
||||
*dest++ = **ptr;
|
||||
inword = 1;
|
||||
}
|
||||
(*ptr)++;
|
||||
|
||||
if (*ptr > (char*)((*buf)->end) && (*buf)->next)
|
||||
{
|
||||
*buf = (*buf)->next;
|
||||
*ptr = static_cast<char*>((*buf)->start);
|
||||
}
|
||||
|
||||
if (dest - word > 98)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} /*< while */
|
||||
*dest = 0;
|
||||
|
||||
/* We now have a word in the local word, check to see if it is a
|
||||
* token we recognise.
|
||||
*/
|
||||
if (word[0] == '\0' || (word[0] == '*' && word[1] == '/'))
|
||||
{
|
||||
tok->token = TOK_END;
|
||||
return tok;
|
||||
}
|
||||
if (word[0] == '\n')
|
||||
{
|
||||
tok->token = TOK_LINEBRK;
|
||||
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 = MXS_STRDUP_A(word);
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
/**
|
||||
* hint_pop - pop the hint off the top of the stack if it is not empty
|
||||
*
|
||||
|
Reference in New Issue
Block a user