Due to an include conflict between /usr/include/poll.h and maxscale/server/include/poll.h the latter was moved to maxscale/server/include/maxscale/poll.h. Once 1.4 is out, all maxscale header files will be moved to that same sub-directory. That will prevent include conflicts and also make it easy to see which include files belong to maxscale and which do not.
		
			
				
	
	
		
			430 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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 MariaDB Corporation Ab 2014
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * @file maxinfo_parse.c - Parse the limited set of SQL that the MaxScale
 | 
						|
 * information schema can use
 | 
						|
 *
 | 
						|
 * @verbatim
 | 
						|
 * Revision History
 | 
						|
 *
 | 
						|
 * Date		Who		Description
 | 
						|
 * 16/02/15	Mark Riddoch	Initial implementation
 | 
						|
 *
 | 
						|
 * @endverbatim
 | 
						|
 */
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <service.h>
 | 
						|
#include <session.h>
 | 
						|
#include <router.h>
 | 
						|
#include <modules.h>
 | 
						|
#include <modinfo.h>
 | 
						|
#include <modutil.h>
 | 
						|
#include <atomic.h>
 | 
						|
#include <spinlock.h>
 | 
						|
#include <dcb.h>
 | 
						|
#include <maxscale/poll.h>
 | 
						|
#include <maxinfo.h>
 | 
						|
#include <skygw_utils.h>
 | 
						|
#include <log_manager.h>
 | 
						|
 | 
						|
static MAXINFO_TREE *make_tree_node(MAXINFO_OPERATOR, char *, MAXINFO_TREE *, MAXINFO_TREE *);
 | 
						|
static void free_tree(MAXINFO_TREE *);
 | 
						|
static char *fetch_token(char *, int *, char **);
 | 
						|
static MAXINFO_TREE *parse_column_list(char **sql);
 | 
						|
static MAXINFO_TREE *parse_table_name(char **sql);
 | 
						|
MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr,
 | 
						|
                                     PARSE_ERROR *parse_error);
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse a SQL subset for the maxinfo plugin and return a parse tree
 | 
						|
 *
 | 
						|
 * @param sql		The SQL query
 | 
						|
 * @return	Parse tree or NULL on error
 | 
						|
 */
 | 
						|
MAXINFO_TREE *
 | 
						|
maxinfo_parse(char *sql, PARSE_ERROR *parse_error)
 | 
						|
{
 | 
						|
int		token;
 | 
						|
char		*ptr, *text;
 | 
						|
MAXINFO_TREE	*tree = NULL;
 | 
						|
MAXINFO_TREE	*col, *table;
 | 
						|
 | 
						|
	*parse_error = PARSE_NOERROR;
 | 
						|
	while ((ptr  = fetch_token(sql, &token, &text)) != NULL)
 | 
						|
	{
 | 
						|
		switch (token)
 | 
						|
		{
 | 
						|
		case	LT_SHOW:
 | 
						|
			free(text);	// not needed
 | 
						|
			ptr = fetch_token(ptr, &token, &text);
 | 
						|
			if (ptr == NULL || token != LT_STRING)
 | 
						|
			{
 | 
						|
				// Expected show "name"
 | 
						|
				*parse_error = PARSE_MALFORMED_SHOW;
 | 
						|
				return NULL;
 | 
						|
			}
 | 
						|
			tree = make_tree_node(MAXOP_SHOW, text, NULL, NULL);
 | 
						|
			if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
 | 
						|
				return tree;
 | 
						|
			else if (token == LT_LIKE)
 | 
						|
			{
 | 
						|
				if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
 | 
						|
				{
 | 
						|
					tree->right = make_tree_node(MAXOP_LIKE,
 | 
						|
							text, NULL, NULL);
 | 
						|
					return tree;
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					// Expected expression
 | 
						|
					*parse_error = PARSE_EXPECTED_LIKE;
 | 
						|
					free_tree(tree);
 | 
						|
					return NULL;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			// Malformed show
 | 
						|
			free(text);
 | 
						|
			free_tree(tree);
 | 
						|
			*parse_error = PARSE_MALFORMED_SHOW;
 | 
						|
			return NULL;
 | 
						|
#if 0
 | 
						|
		case	LT_SELECT:
 | 
						|
			free(text);	// not needed
 | 
						|
			col = parse_column_list(&ptr);
 | 
						|
			table = parse_table_name(&ptr);
 | 
						|
			return make_tree_node(MAXOP_SELECT, NULL, col, table);
 | 
						|
#endif
 | 
						|
            case LT_FLUSH:
 | 
						|
                free(text);	// not needed
 | 
						|
                ptr = fetch_token(ptr, &token, &text);
 | 
						|
                return make_tree_node(MAXOP_FLUSH, text, NULL, NULL);
 | 
						|
 | 
						|
            case LT_SHUTDOWN:
 | 
						|
                free(text);
 | 
						|
                ptr = fetch_token(ptr, &token, &text);
 | 
						|
                tree = make_tree_node(MAXOP_SHUTDOWN, text, NULL, NULL);
 | 
						|
 | 
						|
                if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
 | 
						|
                {
 | 
						|
                    /** Possibly SHUTDOWN MAXSCALE */
 | 
						|
                    return tree;
 | 
						|
                }
 | 
						|
                tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
 | 
						|
 | 
						|
                if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
 | 
						|
                {
 | 
						|
                    /** Unknown token after SHUTDOWN MONITOR|SERVICE */
 | 
						|
                    *parse_error = PARSE_SYNTAX_ERROR;
 | 
						|
                    free_tree(tree);
 | 
						|
                    return NULL;
 | 
						|
                }
 | 
						|
                return tree;
 | 
						|
 | 
						|
            case LT_RESTART:
 | 
						|
                free(text);
 | 
						|
                ptr = fetch_token(ptr, &token, &text);
 | 
						|
                tree = make_tree_node(MAXOP_RESTART, text, NULL, NULL);
 | 
						|
 | 
						|
                if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
 | 
						|
                {
 | 
						|
                    /** Missing token for RESTART MONITOR|SERVICE */
 | 
						|
                    *parse_error = PARSE_SYNTAX_ERROR;
 | 
						|
                    free_tree(tree);
 | 
						|
                    return NULL;
 | 
						|
                }
 | 
						|
                tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
 | 
						|
 | 
						|
                if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
 | 
						|
                {
 | 
						|
                    /** Unknown token after RESTART MONITOR|SERVICE */
 | 
						|
                    *parse_error = PARSE_SYNTAX_ERROR;
 | 
						|
                    free(text);
 | 
						|
                    free_tree(tree);
 | 
						|
                    return NULL;
 | 
						|
                }
 | 
						|
                return tree;
 | 
						|
 | 
						|
            case LT_SET:
 | 
						|
                free(text);	// not needed
 | 
						|
                ptr = fetch_token(ptr, &token, &text);
 | 
						|
                tree = make_tree_node(MAXOP_SET, text, NULL, NULL);
 | 
						|
                return maxinfo_parse_literals(tree, 2, ptr, parse_error);
 | 
						|
 | 
						|
            case LT_CLEAR:
 | 
						|
                free(text);	// not needed
 | 
						|
                ptr = fetch_token(ptr, &token, &text);
 | 
						|
                tree = make_tree_node(MAXOP_CLEAR, text, NULL, NULL);
 | 
						|
                return maxinfo_parse_literals(tree, 2, ptr, parse_error);
 | 
						|
                break;
 | 
						|
		default:
 | 
						|
			*parse_error = PARSE_SYNTAX_ERROR;
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	*parse_error = PARSE_SYNTAX_ERROR;
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse a column list, may be a * or a valid list of string name
 | 
						|
 * separated by a comma
 | 
						|
 *
 | 
						|
 * @param sql	Pointer to pointer to column list updated to point to the table name
 | 
						|
 * @return	A tree of column names
 | 
						|
 */
 | 
						|
static MAXINFO_TREE *
 | 
						|
parse_column_list(char **ptr)
 | 
						|
{
 | 
						|
int	token, lookahead;
 | 
						|
char	*text, *text2;
 | 
						|
MAXINFO_TREE	*tree = NULL;
 | 
						|
MAXINFO_TREE * rval = NULL;
 | 
						|
	*ptr = fetch_token(*ptr, &token, &text);
 | 
						|
	*ptr = fetch_token(*ptr, &lookahead, &text2);
 | 
						|
	switch (token)
 | 
						|
	{
 | 
						|
	case LT_STRING:
 | 
						|
		switch (lookahead)
 | 
						|
		{
 | 
						|
		case LT_COMMA:
 | 
						|
			rval = make_tree_node(MAXOP_COLUMNS, text, NULL,
 | 
						|
				parse_column_list(ptr));
 | 
						|
			break;
 | 
						|
		case LT_FROM:
 | 
						|
			rval = make_tree_node(MAXOP_COLUMNS, text, NULL,
 | 
						|
				NULL);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case LT_STAR:
 | 
						|
		if (lookahead != LT_FROM)
 | 
						|
			rval = make_tree_node(MAXOP_ALL_COLUMNS, NULL, NULL,
 | 
						|
				NULL);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
	free(text);
 | 
						|
	free(text2);
 | 
						|
	return rval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse a table name
 | 
						|
 *
 | 
						|
 * @param sql	Pointer to pointer to column list updated to point to the table name
 | 
						|
 * @return	A tree of table names
 | 
						|
 */
 | 
						|
static MAXINFO_TREE *
 | 
						|
parse_table_name(char **ptr)
 | 
						|
{
 | 
						|
int	token;
 | 
						|
char	*text;
 | 
						|
MAXINFO_TREE	*tree = NULL;
 | 
						|
 | 
						|
	*ptr = fetch_token(*ptr, &token, &text);
 | 
						|
	if  (token == LT_STRING)
 | 
						|
		return make_tree_node(MAXOP_TABLE, text, NULL, NULL);
 | 
						|
	free(text);
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Allocate and populate a parse tree node
 | 
						|
 *
 | 
						|
 * @param op	The node operator
 | 
						|
 * @param value	The node value
 | 
						|
 * @param left	The left branch of the parse tree
 | 
						|
 * @param right	The right branch of the parse tree
 | 
						|
 * @return The new parse tree node
 | 
						|
 */
 | 
						|
static MAXINFO_TREE *
 | 
						|
make_tree_node(MAXINFO_OPERATOR op, char *value, MAXINFO_TREE *left, MAXINFO_TREE *right)
 | 
						|
{
 | 
						|
MAXINFO_TREE	*node;
 | 
						|
 | 
						|
	if ((node = (MAXINFO_TREE *)malloc(sizeof(MAXINFO_TREE))) == NULL)
 | 
						|
		return NULL;
 | 
						|
	node->op = op;
 | 
						|
	node->value = value;
 | 
						|
	node->left = left;
 | 
						|
	node->right = right;
 | 
						|
 | 
						|
	return node;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Recusrsively free the storage associated with a parse tree
 | 
						|
 *
 | 
						|
 * @param tree	The parse tree to free
 | 
						|
 */
 | 
						|
static void
 | 
						|
free_tree(MAXINFO_TREE *tree)
 | 
						|
{
 | 
						|
	if (tree->left)
 | 
						|
		free_tree(tree->left);
 | 
						|
	if (tree->right)
 | 
						|
		free_tree(tree->right);
 | 
						|
	if (tree->value)
 | 
						|
		free(tree->value);
 | 
						|
	free(tree);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * The set of keywords known to the tokeniser
 | 
						|
 */
 | 
						|
static struct
 | 
						|
{
 | 
						|
    char *text;
 | 
						|
    int token;
 | 
						|
} keywords[] = {
 | 
						|
    { "show",       LT_SHOW},
 | 
						|
    { "select",     LT_SELECT},
 | 
						|
    { "from",       LT_FROM},
 | 
						|
    { "like",       LT_LIKE},
 | 
						|
    { "=",          LT_EQUAL},
 | 
						|
    { ",",          LT_COMMA},
 | 
						|
    { "*",          LT_STAR},
 | 
						|
    { "flush",      LT_FLUSH},
 | 
						|
    { "set",        LT_SET},
 | 
						|
    { "clear",      LT_CLEAR},
 | 
						|
    { "shutdown",   LT_SHUTDOWN},
 | 
						|
    { "restart",    LT_RESTART},
 | 
						|
    { NULL, 0}
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Limited SQL tokeniser. Understands a limited set of key words and
 | 
						|
 * quoted strings.
 | 
						|
 *
 | 
						|
 * @param sql	The SQL to tokenise
 | 
						|
 * @param token	The returned token
 | 
						|
 * @param text	The matching text
 | 
						|
 * @return	The next position to tokenise from
 | 
						|
 */
 | 
						|
static char *
 | 
						|
fetch_token(char *sql, int *token, char **text)
 | 
						|
{
 | 
						|
char	*s1, *s2, quote = '\0';
 | 
						|
int	i;
 | 
						|
 | 
						|
	s1 = sql;
 | 
						|
	while (*s1 && isspace(*s1))
 | 
						|
	{
 | 
						|
		s1++;
 | 
						|
	}
 | 
						|
	if (quote == '\0' && (*s1 == '\'' || *s1 == '\"'))
 | 
						|
	{
 | 
						|
		quote = *s1++;
 | 
						|
	}
 | 
						|
	if (*s1 == '/' && *(s1 + 1) == '*')
 | 
						|
	{
 | 
						|
		s1 += 2;
 | 
						|
		// Skip the comment
 | 
						|
		do {
 | 
						|
			while (*s1 && *s1 != '*')
 | 
						|
				s1++;
 | 
						|
		} while (*(s1 + 1) && *(s1 + 1) != '/');
 | 
						|
		s1 += 2;
 | 
						|
		while (*s1 && isspace(*s1))
 | 
						|
		{
 | 
						|
			s1++;
 | 
						|
		}
 | 
						|
		if (quote == '\0' && (*s1 == '\'' || *s1 == '\"'))
 | 
						|
		{
 | 
						|
			quote = *s1++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	s2 = s1;
 | 
						|
	while (*s2)
 | 
						|
	{
 | 
						|
		if (quote == '\0' && (isspace(*s2)
 | 
						|
				|| *s2 == ',' || *s2 == '='))
 | 
						|
			break;
 | 
						|
		else if (quote == *s2)
 | 
						|
		{
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		s2++;
 | 
						|
	}
 | 
						|
 | 
						|
	if (*s1 == '@' && *(s1 + 1) == '@')
 | 
						|
	{
 | 
						|
		*text = strndup(s1 + 2, (s2 - s1) - 2);
 | 
						|
		*token = LT_VARIABLE;
 | 
						|
		return s2;
 | 
						|
	}
 | 
						|
 | 
						|
	if (s1 == s2)
 | 
						|
	{
 | 
						|
		*text = NULL;
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	*text = strndup(s1, s2 - s1);
 | 
						|
	for (i = 0; keywords[i].text; i++)
 | 
						|
	{
 | 
						|
		if (strcasecmp(keywords[i].text, *text) == 0)
 | 
						|
		{
 | 
						|
			*token = keywords[i].token;
 | 
						|
			return s2;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	*token = LT_STRING;
 | 
						|
	return s2;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Parse the remaining arguments as literals.
 | 
						|
 * @param tree Previous head of the parse tree
 | 
						|
 * @param min_args Minimum required number of arguments
 | 
						|
 * @param ptr Pointer to client command
 | 
						|
 * @param parse_error Pointer to parsing error to fill
 | 
						|
 * @return Parsed tree or NULL if parsing failed
 | 
						|
 */
 | 
						|
MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr,
 | 
						|
                                      PARSE_ERROR *parse_error)
 | 
						|
{
 | 
						|
    int token;
 | 
						|
    MAXINFO_TREE* node = tree;
 | 
						|
    char *text;
 | 
						|
    for(int i = 0; i < min_args; i++)
 | 
						|
    {
 | 
						|
        if((ptr = fetch_token(ptr, &token, &text)) == NULL ||
 | 
						|
           (node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL)
 | 
						|
        {
 | 
						|
            *parse_error = PARSE_SYNTAX_ERROR;
 | 
						|
            free_tree(tree);
 | 
						|
            if(ptr)
 | 
						|
            {
 | 
						|
                free(text);
 | 
						|
            }
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        node = node->right;
 | 
						|
    }
 | 
						|
 | 
						|
    return tree;
 | 
						|
}
 |