try to add query_classifier under skygteway
This commit is contained in:
parent
525861cab3
commit
4854b56aba
43
query_classifier/makefile
Normal file
43
query_classifier/makefile
Normal file
@ -0,0 +1,43 @@
|
||||
include ../build_gateway.inc
|
||||
include ../makefile.inc
|
||||
|
||||
CC = gcc
|
||||
CPP = g++
|
||||
|
||||
QUERY_CLASSIFIER_PATH := $(shell pwd)
|
||||
|
||||
makeall: clean all
|
||||
|
||||
clean:
|
||||
- $(DEL) query_classifier.o
|
||||
- $(DEL) libquery_classifier.so
|
||||
- $(DEL) libquery_classifier.so.1.0.1
|
||||
- $(DEL) skygw_utils.o
|
||||
- $(DEL) *~
|
||||
|
||||
all: utils lib
|
||||
|
||||
utils:
|
||||
make -C $(ROOT_PATH)/utils clean all
|
||||
$(COPY) $(ROOT_PATH)/utils/skygw_utils.o ./
|
||||
|
||||
lib: libcomp liblink
|
||||
|
||||
libcomp:
|
||||
$(CPP) -c $(CFLAGS) \
|
||||
-I$(MARIADB_SRC_PATH)/libmysqld/ \
|
||||
-I$(MARIADB_SRC_PATH)/include/ \
|
||||
-I$(MARIADB_SRC_PATH)/sql \
|
||||
-I$(MARIADB_SRC_PATH)/regex/ \
|
||||
-I./ \
|
||||
-fPIC ./query_classifier.cc -o query_classifier.o
|
||||
|
||||
liblink:
|
||||
$(CPP) -shared \
|
||||
-L$(MARIADB_SRC_PATH)/libmysqld \
|
||||
-Wl,-soname,libquery_classifier.so \
|
||||
-Wl,-rpath,$(MARIADB_SRC_PATH)/libmysqld \
|
||||
-o libquery_classifier.so.1.0.1 ./query_classifier.o \
|
||||
$(LDLIBS) $(CPP_LDLIBS)
|
||||
$(DEL) ./libquery_classifier.so
|
||||
$(LINK) ./libquery_classifier.so.1.0.1 ./libquery_classifier.so
|
409
query_classifier/query_classifier.cc
Normal file
409
query_classifier/query_classifier.cc
Normal file
@ -0,0 +1,409 @@
|
||||
/**
|
||||
* @section LICENCE
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @file
|
||||
*
|
||||
*/
|
||||
|
||||
#define EMBEDDED_LIBRARY
|
||||
#define MYSQL_YACC
|
||||
#define MYSQL_LEX012
|
||||
#define MYSQL_SERVER
|
||||
#if defined(MYSQL_CLIENT)
|
||||
# undef MYSQL_CLIENT
|
||||
#endif
|
||||
|
||||
#include <query_classifier.h>
|
||||
#include "../utils/skygw_types.h"
|
||||
#include "../utils/skygw_debug.h"
|
||||
|
||||
#include <mysql.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_global.h>
|
||||
#include <my_dbug.h>
|
||||
#include <my_base.h>
|
||||
#include <sql_list.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <sql_class.h>
|
||||
#include <sql_lex.h>
|
||||
#include <embedded_priv.h>
|
||||
#include <sql_class.h>
|
||||
#include <sql_lex.h>
|
||||
#include <sql_parse.h>
|
||||
#include <errmsg.h>
|
||||
#include <client_settings.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
static THD* get_or_create_thd_for_parsing(
|
||||
MYSQL* mysql,
|
||||
char* query_str);
|
||||
|
||||
static unsigned long set_client_flags(
|
||||
MYSQL* mysql);
|
||||
|
||||
static bool create_parse_tree(
|
||||
THD* thd);
|
||||
|
||||
static skygw_query_type_t resolve_query_type(
|
||||
THD* thd);
|
||||
|
||||
/**
|
||||
* @node (write brief function description here)
|
||||
*
|
||||
* Parameters:
|
||||
* @param query_str - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param client_flag - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
skygw_query_type_t skygw_query_classifier_get_type(
|
||||
const char* query,
|
||||
unsigned long client_flags)
|
||||
{
|
||||
MYSQL* mysql;
|
||||
char* query_str;
|
||||
const char* user = "skygw";
|
||||
const char* db = "skygw";
|
||||
THD* thd;
|
||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
bool failp = FALSE;
|
||||
|
||||
ss_dfprintf(stderr, ">> skygw_query_classifier_get_type\n");
|
||||
ss_info_dassert(query != NULL, ("query_str is NULL"));
|
||||
|
||||
query_str = const_cast<char*>(query);
|
||||
|
||||
fprintf(stderr, " Query \"%s\"\n", query_str);
|
||||
|
||||
/** Get server handle */
|
||||
mysql = mysql_init(NULL);
|
||||
|
||||
if (mysql == NULL) {
|
||||
fprintf(stderr,
|
||||
"mysql_real_connect failed, %d : %s\n",
|
||||
mysql_errno(mysql),
|
||||
mysql_error(mysql));
|
||||
mysql_library_end();
|
||||
goto return_without_server;
|
||||
}
|
||||
|
||||
/** Set methods and authentication to mysql */
|
||||
mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "libmysqld_skygw");
|
||||
mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
|
||||
mysql->methods = &embedded_methods;
|
||||
mysql->user = my_strdup(user, MYF(0));
|
||||
mysql->db = my_strdup(db, MYF(0));
|
||||
mysql->passwd = NULL;
|
||||
|
||||
/** Get one or create new THD object to be use in parsing */
|
||||
thd = get_or_create_thd_for_parsing(mysql, query_str);
|
||||
|
||||
if (thd == NULL) {
|
||||
goto return_with_server_handle;
|
||||
}
|
||||
/** Create parse_tree inside thd */
|
||||
failp = create_parse_tree(thd);
|
||||
|
||||
if (failp) {
|
||||
goto return_with_thd;
|
||||
}
|
||||
qtype = resolve_query_type(thd);
|
||||
|
||||
return_with_thd:
|
||||
(*mysql->methods->free_embedded_thd)(mysql);
|
||||
mysql->thd = 0;
|
||||
return_with_server_handle:
|
||||
mysql_close(mysql);
|
||||
mysql_thread_end();
|
||||
return_without_server:
|
||||
ss_dfprintf(stderr,
|
||||
"<< skygw_query_classifier_get_type : %s\n",
|
||||
STRQTYPE(qtype));
|
||||
ss_dfflush(stderr);
|
||||
return qtype;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @node (write brief function description here)
|
||||
*
|
||||
* Parameters:
|
||||
* @param mysql - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param query_str - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
static THD* get_or_create_thd_for_parsing(
|
||||
MYSQL* mysql,
|
||||
char* query_str)
|
||||
{
|
||||
THD* thd = NULL;
|
||||
unsigned long client_flags;
|
||||
char* db = mysql->options.db;
|
||||
bool failp = FALSE;
|
||||
size_t query_len;
|
||||
|
||||
ss_dfprintf(stderr, "> get_or_create_thd_for_parsing\n");
|
||||
ss_info_dassert(mysql != NULL, ("mysql is NULL"));
|
||||
ss_info_dassert(query_str != NULL, ("query_str is NULL"));
|
||||
|
||||
query_len = strlen(query_str);
|
||||
client_flags = set_client_flags(mysql);
|
||||
|
||||
/** Get THD.
|
||||
* NOTE: Instead of creating new every time, THD instance could
|
||||
* be get from a pool of them.
|
||||
*/
|
||||
thd = (THD *)create_embedded_thd(client_flags);
|
||||
|
||||
if (thd == NULL) {
|
||||
ss_dfprintf(stderr, "Couldn't create embedded thd\n");
|
||||
goto return_thd;
|
||||
}
|
||||
mysql->thd = thd;
|
||||
init_embedded_mysql(mysql, client_flags);
|
||||
failp = check_embedded_connection(mysql, db);
|
||||
|
||||
if (failp) {
|
||||
ss_dfprintf(stderr, "Checking embedded connection failed.\n");
|
||||
goto return_err_with_thd;
|
||||
}
|
||||
thd->clear_data_list();
|
||||
|
||||
/** Check that we are calling the client functions in right order */
|
||||
if (mysql->status != MYSQL_STATUS_READY) {
|
||||
set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
|
||||
goto return_err_with_thd;
|
||||
}
|
||||
/* Clear result variables */
|
||||
thd->current_stmt= NULL;
|
||||
thd->store_globals();
|
||||
/*
|
||||
We have to call free_old_query before we start to fill mysql->fields
|
||||
for new query. In the case of embedded server we collect field data
|
||||
during query execution (not during data retrieval as it is in remote
|
||||
client). So we have to call free_old_query here
|
||||
*/
|
||||
free_old_query(mysql);
|
||||
thd->extra_length = query_len;
|
||||
thd->extra_data = query_str;
|
||||
alloc_query(thd, query_str, query_len);
|
||||
goto return_thd;
|
||||
|
||||
return_err_with_thd:
|
||||
(*mysql->methods->free_embedded_thd)(mysql);
|
||||
thd = 0;
|
||||
mysql->thd = 0;
|
||||
return_thd:
|
||||
ss_dfprintf(stderr, "< get_or_create_thd_for_parsing : %p\n", thd);
|
||||
ss_dfflush(stderr);
|
||||
return thd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @node Set client flags. This is copied from libmysqld.c:mysql_real_connect
|
||||
*
|
||||
* Parameters:
|
||||
* @param mysql - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
static unsigned long set_client_flags(
|
||||
MYSQL* mysql)
|
||||
{
|
||||
unsigned long f = 0;
|
||||
ss_dfprintf(stderr, "> set_client_flags\n");
|
||||
f |= mysql->options.client_flag;
|
||||
|
||||
/* Send client information for access check */
|
||||
f |= CLIENT_CAPABILITIES;
|
||||
|
||||
if (f & CLIENT_MULTI_STATEMENTS) {
|
||||
f |= CLIENT_MULTI_RESULTS;
|
||||
}
|
||||
/**
|
||||
* No compression in embedded as we don't send any data,
|
||||
* and no pluggable auth, as we cannot do a client-server dialog
|
||||
*/
|
||||
f &= ~(CLIENT_COMPRESS | CLIENT_PLUGIN_AUTH);
|
||||
|
||||
if (mysql->options.db != NULL) {
|
||||
f |= CLIENT_CONNECT_WITH_DB;
|
||||
}
|
||||
ss_dfprintf(stderr, "< set_client_flags : %lu\n", f);
|
||||
ss_dfflush(stderr);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
static bool create_parse_tree(
|
||||
THD* thd)
|
||||
{
|
||||
Parser_state parser_state;
|
||||
bool failp = FALSE;
|
||||
const char* virtual_db = "skygw_virtual";
|
||||
ss_dfprintf(stderr, "> create_parse_tree\n");
|
||||
|
||||
if (parser_state.init(thd, thd->query(), thd->query_length())) {
|
||||
failp = TRUE;
|
||||
goto return_here;
|
||||
}
|
||||
mysql_reset_thd_for_next_command(thd, opt_userstat_running);
|
||||
|
||||
/** Set some database to thd so that parsing won't fail because of
|
||||
* missing database. Then parse. */
|
||||
failp = thd->set_db(virtual_db, sizeof(virtual_db));
|
||||
|
||||
if (failp) {
|
||||
fprintf(stderr, "Setting database for thd failed\n");
|
||||
}
|
||||
failp = parse_sql(thd, &parser_state, NULL);
|
||||
|
||||
if (failp) {
|
||||
fprintf(stderr, "parse_sql failed\n");
|
||||
}
|
||||
return_here:
|
||||
ss_dfprintf(stderr, "< create_parse_tree : %s\n", STRBOOL(failp));
|
||||
fflush(stderr);
|
||||
return failp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @node Detect query type, read-only, write, or session update
|
||||
*
|
||||
* Parameters:
|
||||
* @param thd - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return
|
||||
*
|
||||
*
|
||||
* @details Query type is deduced by checking for certain properties
|
||||
* of them. The order is essential. Some SQL commands have multiple
|
||||
* flags set and changing the order in which flags are tested,
|
||||
* the resulting type may be different.
|
||||
*
|
||||
*/
|
||||
static skygw_query_type_t resolve_query_type(
|
||||
THD* thd)
|
||||
{
|
||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
LEX* lex;
|
||||
/**
|
||||
* By default, if sql_log_bin, that is, recording data modifications
|
||||
* to binary log, is disabled, gateway treats operations normally.
|
||||
* Effectively nothing is replicated.
|
||||
* When force_data_modify_op_replication is TRUE, gateway distributes
|
||||
* all write operations to all nodes.
|
||||
*/
|
||||
bool force_data_modify_op_replication;
|
||||
|
||||
ss_dfprintf(stderr, "> resolve_query_type\n");
|
||||
ss_info_dassert(thd != NULL, ("thd is NULL\n"));
|
||||
|
||||
force_data_modify_op_replication = FALSE;
|
||||
lex = thd->lex;
|
||||
|
||||
/** SELECT ..INTO variable|OUTFILE|DUMPFILE */
|
||||
if (lex->result != NULL) {
|
||||
qtype = QUERY_TYPE_SESSION_WRITE;
|
||||
goto return_here;
|
||||
}
|
||||
/**
|
||||
* 1:ALTER TABLE, TRUNCATE, REPAIR, OPTIMIZE, ANALYZE, CHECK.
|
||||
* 2:CREATE|ALTER|DROP|TRUNCATE|RENAME TABLE, LOAD, CREATE|DROP|ALTER DB,
|
||||
* CREATE|DROP INDEX, CREATE|DROP VIEW, CREATE|DROP TRIGGER,
|
||||
* CREATE|ALTER|DROP EVENT, UPDATE, INSERT, INSERT(SELECT),
|
||||
* DELETE, REPLACE, REPLACE(SELECT), CREATE|RENAME|DROP USER,
|
||||
* GRANT, REVOKE, OPTIMIZE, CREATE|ALTER|DROP FUNCTION|PROCEDURE,
|
||||
* CREATE SPFUNCTION, INSTALL|UNINSTALL PLUGIN
|
||||
*/
|
||||
if (is_log_table_write_query(lex->sql_command) ||
|
||||
is_update_query(lex->sql_command))
|
||||
{
|
||||
if (thd->variables.sql_log_bin == 0 &&
|
||||
force_data_modify_op_replication)
|
||||
{
|
||||
qtype = QUERY_TYPE_SESSION_WRITE;
|
||||
} else {
|
||||
qtype = QUERY_TYPE_WRITE;
|
||||
}
|
||||
|
||||
goto return_here;
|
||||
}
|
||||
|
||||
/**
|
||||
* REVOKE ALL, ASSIGN_TO_KEYCACHE,
|
||||
* PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER
|
||||
*/
|
||||
if (sql_command_flags[lex->sql_command] & CF_AUTO_COMMIT_TRANS) {
|
||||
qtype = QUERY_TYPE_SESSION_WRITE;
|
||||
goto return_here;
|
||||
}
|
||||
|
||||
/** Try to catch session modifications here */
|
||||
switch (lex->sql_command) {
|
||||
case SQLCOM_CHANGE_DB:
|
||||
case SQLCOM_SET_OPTION:
|
||||
qtype = QUERY_TYPE_SESSION_WRITE;
|
||||
break;
|
||||
|
||||
case SQLCOM_SELECT:
|
||||
qtype = QUERY_TYPE_READ;
|
||||
break;
|
||||
|
||||
case SQLCOM_CALL:
|
||||
qtype = QUERY_TYPE_WRITE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return_here:
|
||||
ss_dfprintf(stderr, "< resolve_query_type : %s\n", STRQTYPE(qtype));
|
||||
return qtype;
|
||||
}
|
45
query_classifier/query_classifier.h
Normal file
45
query_classifier/query_classifier.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
/** getpid */
|
||||
#include <unistd.h>
|
||||
#include "../utils/skygw_utils.h"
|
||||
|
||||
EXTERN_C_BLOCK_BEGIN
|
||||
|
||||
/**
|
||||
* Query type for skygateway.
|
||||
* The meaninful difference is whether master data was modified
|
||||
*/
|
||||
typedef enum {
|
||||
QUERY_TYPE_UNKNOWN = 7, /*!< Couln't find out or parse error */
|
||||
QUERY_TYPE_WRITE, /*!< Master data will be modified */
|
||||
QUERY_TYPE_READ, /*!< No updates */
|
||||
QUERY_TYPE_SESSION_WRITE /*!< Session data will be modified */
|
||||
} skygw_query_type_t;
|
||||
|
||||
|
||||
|
||||
skygw_query_type_t skygw_query_classifier_get_type(
|
||||
const char* query_str,
|
||||
unsigned long client_flags);
|
||||
|
||||
|
||||
EXTERN_C_BLOCK_END
|
||||
|
34
query_classifier/test/makefile
Normal file
34
query_classifier/test/makefile
Normal file
@ -0,0 +1,34 @@
|
||||
include ../../build_gateway.inc
|
||||
include ../../makefile.inc
|
||||
|
||||
CC = gcc
|
||||
CPP = g++
|
||||
|
||||
TESTPATH := $(shell pwd)
|
||||
QUERY_CLASSIFIER_PATH := $(ROOT_PATH)/query_classifier/
|
||||
TESTAPP = $(TESTPATH)/testmain
|
||||
|
||||
runtest: makeall testall
|
||||
|
||||
makeall: clean all
|
||||
|
||||
clean:
|
||||
- $(DEL) testmain.o
|
||||
- $(DEL) testmain
|
||||
- $(DEL) *~
|
||||
|
||||
all: testcomp testall
|
||||
|
||||
testcomp:
|
||||
$(CC) $(CFLAGS) \
|
||||
-L$(QUERY_CLASSIFIER_PATH) \
|
||||
-L$(MARIADB_SRC_PATH)/libmysqld \
|
||||
-Wl,-rpath,$(MARIADB_SRC_PATH)/libmysqld \
|
||||
-Wl,-rpath,$(QUERY_CLASSIFIER_PATH)/ \
|
||||
-o testmain -DSS_DEBUG \
|
||||
-I$(MARIADB_SRC_PATH)/include testmain.c \
|
||||
-lquery_classifier $(LDLIBS) \
|
||||
$(QUERY_CLASSIFIER_PATH)/skygw_utils.o
|
||||
|
||||
testall:
|
||||
- $(LAUNCH_DEBUGGER) $(TESTAPP) $(BACKGR)
|
471
query_classifier/test/testmain.c
Normal file
471
query_classifier/test/testmain.c
Normal file
@ -0,0 +1,471 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <mysql.h>
|
||||
|
||||
#include "../../utils/skygw_utils.h"
|
||||
//#include "skygw_debug.h"
|
||||
//#include "skygw_types.h"
|
||||
#include "../query_classifier.h"
|
||||
|
||||
static char* server_options[] = {
|
||||
"raatikka",
|
||||
"--datadir=/home/raatikka/data/skygw_parse/",
|
||||
"--skip-innodb",
|
||||
"--default-storage-engine=myisam",
|
||||
NULL
|
||||
};
|
||||
|
||||
const int num_elements = (sizeof(server_options) / sizeof(char *)) - 1;
|
||||
|
||||
static char* server_groups[] = {
|
||||
"embedded",
|
||||
"server",
|
||||
"server",
|
||||
"server",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void slcursor_add_case(
|
||||
slist_cursor_t* c,
|
||||
void* data)
|
||||
{
|
||||
slcursor_add_data(c, data);
|
||||
}
|
||||
|
||||
|
||||
typedef struct query_test_st query_test_t;
|
||||
|
||||
struct query_test_st {
|
||||
skygw_chk_t qt_chk_top;
|
||||
const char* qt_query_str;
|
||||
skygw_query_type_t qt_query_type;
|
||||
skygw_query_type_t qt_result_type;
|
||||
bool qt_should_fail;
|
||||
bool qt_exec_also_in_server;
|
||||
skygw_chk_t qt_chk_tail;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static query_test_t* query_test_init(
|
||||
const char* query_str,
|
||||
skygw_query_type_t query_type,
|
||||
bool case_should_fail,
|
||||
bool exec_also_in_server)
|
||||
{
|
||||
query_test_t* qtest;
|
||||
|
||||
qtest = (query_test_t *)calloc(1, sizeof(query_test_t));
|
||||
ss_dassert(qtest != NULL);
|
||||
qtest->qt_chk_top = CHK_NUM_QUERY_TEST;
|
||||
qtest->qt_chk_tail = CHK_NUM_QUERY_TEST;
|
||||
qtest->qt_query_str = query_str;
|
||||
qtest->qt_query_type = query_type;
|
||||
qtest->qt_should_fail = case_should_fail;
|
||||
qtest->qt_exec_also_in_server = exec_also_in_server;
|
||||
return qtest;
|
||||
}
|
||||
|
||||
const char* query_test_get_querystr(
|
||||
query_test_t* qt)
|
||||
{
|
||||
CHK_QUERY_TEST(qt);
|
||||
return qt->qt_query_str;
|
||||
}
|
||||
|
||||
static skygw_query_type_t query_test_get_query_type(
|
||||
query_test_t* qt)
|
||||
{
|
||||
CHK_QUERY_TEST(qt);
|
||||
return qt->qt_query_type;
|
||||
}
|
||||
|
||||
static skygw_query_type_t query_test_get_result_type(
|
||||
query_test_t* qt)
|
||||
{
|
||||
CHK_QUERY_TEST(qt);
|
||||
return qt->qt_result_type;
|
||||
}
|
||||
|
||||
|
||||
static bool query_test_types_match(
|
||||
query_test_t* qt)
|
||||
{
|
||||
CHK_QUERY_TEST(qt);
|
||||
return (qt->qt_query_type == qt->qt_result_type);
|
||||
}
|
||||
|
||||
static bool query_test_exec_also_in_server(
|
||||
query_test_t* qt)
|
||||
{
|
||||
CHK_QUERY_TEST(qt);
|
||||
return (qt->qt_exec_also_in_server);
|
||||
}
|
||||
|
||||
static query_test_t* slcursor_get_case(
|
||||
slist_cursor_t* c)
|
||||
{
|
||||
return (query_test_t*)slcursor_get_data(c);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
slist_cursor_t* c;
|
||||
const char* q;
|
||||
query_test_t* qtest;
|
||||
skygw_query_type_t qtype;
|
||||
bool succp;
|
||||
bool failp = TRUE;
|
||||
unsigned int f = 0;
|
||||
int nsucc = 0;
|
||||
int nfail = 0;
|
||||
MYSQL* mysql;
|
||||
|
||||
ss_dfprintf(stderr, ">> testmain\n");
|
||||
c = slist_init();
|
||||
|
||||
/** Read-only SELECTs */
|
||||
q = "SELECT user from mysql.user";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_READ, FALSE, TRUE));
|
||||
|
||||
q = "select tt1.id, tt2.id from t1 tt1, t2 tt2 where tt1.name is "
|
||||
"not null and tt2.name is not null";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_READ, FALSE, FALSE));
|
||||
|
||||
/** SELECT ..INTO clauses > session updates */
|
||||
q = "SELECT user from mysql.user INTO DUMPFILE '/tmp/dump1'";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "SELECT user INTO DUMPFILE '/tmp/dump2 ' from mysql.user";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "SELECT user from mysql.user INTO OUTFILE '/tmp/out1'";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
/** Database and table name must be separated by a dot */
|
||||
q = "SELECT user INTO OUTFILE '/tmp/out2 ' from mysql-user";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, TRUE, FALSE));
|
||||
|
||||
/** Database and table name must be separated by a dot */
|
||||
q = "SELECT user INTO OUTFILE '/tmp/out2 ' from mysql_foo_user";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "SELECT user FROM mysql.user limit 1 INTO @local_variable";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "SELECT user INTO @local_variable FROM mysql.user limit 1";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "SELECT non_existent_attr INTO @d FROM non_existent_table";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "select * from table1 "
|
||||
"where table1.field IN "
|
||||
"(select * from table1a union select * from table1b) union "
|
||||
"select * from table2 where table2.field = "
|
||||
"(select (select f1 from table2a where table2a.f2 = table2b.f3) "
|
||||
"from table2b where table2b.f1 = table2.f2) union "
|
||||
"select * from table3";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, TRUE));
|
||||
|
||||
/** Functions */
|
||||
q = "SELECT NOW()";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_READ, FALSE, FALSE));
|
||||
|
||||
q = "SELECT SOUNDEX('Hello')";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_READ, FALSE, FALSE));
|
||||
|
||||
q = "SELECT MY_UDF('Hello')";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_READ, FALSE, TRUE));
|
||||
|
||||
/** RENAME TABLEs */
|
||||
q = "RENAME TABLE T1 to T2";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, FALSE));
|
||||
|
||||
|
||||
/** INSERTs */
|
||||
q = "INSERT INTO T1 (SELECT * FROM T2)";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "INSERT INTO T1 VALUES(2, 'foo', 'toomanyattributes')";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "INSERT INTO T2 VALUES(1, 'sthrgey')";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "INSERT INTO T2 VALUES(8, 'ergstrhe')";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "INSERT INTO T2 VALUES(9, NULL)";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, FALSE));
|
||||
|
||||
|
||||
/** Ok, delimeter is client-side parameter which shouldn't be handled
|
||||
* on server side.
|
||||
*/
|
||||
q = "delimiter //";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, TRUE, TRUE));
|
||||
|
||||
/** SETs, USEs > Session updates */
|
||||
q = "SET @a=1";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "USE TEST";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, FALSE));
|
||||
|
||||
|
||||
/** Object creation statements */
|
||||
q = "create procedure si (out param1 int) \nbegin select count(*) "
|
||||
"into param1 from t1; \nend";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "CREATE TABLE T1 (id integer primary key, name varchar(10))";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "DROP TABLE T1";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "ALTER TABLE T1 ADD COLUMN WHYME INTEGER NOT NULL";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "TRUNCATE TABLE T1";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, FALSE));
|
||||
|
||||
q = "DROP SERVER IF EXISTS VICTIMSRV";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "CREATE USER FOO IDENTIFIED BY 'BAR'";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "OPTIMIZE NO_WRITE_TO_BINLOG TABLE T1";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, TRUE));
|
||||
|
||||
q = "SELECT NOW();CREATE TABLE T1 (ID INTEGER);"
|
||||
"SET sql_log_bin=0;CREATE TABLE T2 (ID INTEGER)";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_WRITE, FALSE, TRUE));
|
||||
|
||||
|
||||
/** Setting database makes this SESSION_WRITE */
|
||||
q = "USE TEST;CREATE TABLE T1 (ID INTEGER);"
|
||||
"SET sql_log_bin=0;CREATE TABLE T2 (ID INTEGER)";
|
||||
slcursor_add_case(
|
||||
c,
|
||||
query_test_init(q, QUERY_TYPE_SESSION_WRITE, FALSE, TRUE));
|
||||
|
||||
/**
|
||||
* Init libmysqld.
|
||||
*/
|
||||
failp = mysql_library_init(num_elements, server_options, server_groups);
|
||||
|
||||
if (failp) {
|
||||
MYSQL* mysql = mysql_init(NULL);
|
||||
ss_dassert(mysql != NULL);
|
||||
fprintf(stderr,
|
||||
"mysql_init failed, %d : %s\n",
|
||||
mysql_errno(mysql),
|
||||
mysql_error(mysql));
|
||||
goto return_without_server;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"\nExecuting selected cases in "
|
||||
"skygw_query_classifier_get_type :\n\n");
|
||||
/**
|
||||
* Set cursor to the beginning, scan through the list and execute
|
||||
* test cases.
|
||||
*/
|
||||
succp = slcursor_move_to_begin(c);
|
||||
|
||||
while(succp) {
|
||||
qtest = slcursor_get_case(c);
|
||||
qtest->qt_result_type =
|
||||
skygw_query_classifier_get_type(qtest->qt_query_str, f);
|
||||
succp = slcursor_step_ahead(c);
|
||||
}
|
||||
/**
|
||||
* Scan through test results and compare them against expected
|
||||
* results.
|
||||
*/
|
||||
succp = slcursor_move_to_begin(c);
|
||||
fprintf(stderr, "\nScanning through the results :\n\n");
|
||||
|
||||
while(succp) {
|
||||
qtest = slcursor_get_case(c);
|
||||
|
||||
if (!query_test_types_match(qtest)) {
|
||||
nfail += 1;
|
||||
ss_dfprintf(stderr,
|
||||
"* Failed: \"%s\" -> %s (Expected %s)\n",
|
||||
query_test_get_querystr(qtest),
|
||||
STRQTYPE(query_test_get_result_type(qtest)),
|
||||
STRQTYPE(query_test_get_query_type(qtest)));
|
||||
} else {
|
||||
nsucc += 1;
|
||||
ss_dfprintf(stderr,
|
||||
"Succeed\t: \"%s\" -> %s\n",
|
||||
query_test_get_querystr(qtest),
|
||||
STRQTYPE(query_test_get_query_type(qtest)));
|
||||
}
|
||||
succp = slcursor_step_ahead(c);
|
||||
}
|
||||
fprintf(stderr,
|
||||
"------------------------------------------\n"
|
||||
"Tests in total %d, SUCCEED %d, FAILED %d\n",
|
||||
nsucc+nfail,
|
||||
nsucc,
|
||||
nfail);
|
||||
|
||||
/**
|
||||
* Scan test results and re-execute those which are marked to be
|
||||
* executed also in the server. This serves mostly debugging purposes.
|
||||
*/
|
||||
succp = slcursor_move_to_begin(c);
|
||||
mysql = mysql_init(NULL);
|
||||
|
||||
if (mysql == NULL) {
|
||||
fprintf(stderr, "mysql_init failed\n");
|
||||
goto return_without_server;
|
||||
}
|
||||
|
||||
mysql_options(mysql,
|
||||
MYSQL_READ_DEFAULT_GROUP,
|
||||
"libmysqld_client");
|
||||
mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
|
||||
mysql_options(mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
|
||||
|
||||
mysql = mysql_real_connect(mysql,
|
||||
NULL,
|
||||
"skygw",
|
||||
"skygw",
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
CLIENT_MULTI_STATEMENTS);
|
||||
|
||||
if (mysql == NULL) {
|
||||
fprintf(stderr, "mysql_real_connect failed\n");
|
||||
goto return_with_handle;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"\nRe-execution of selected cases in Embedded server :\n\n");
|
||||
|
||||
while(succp) {
|
||||
qtest = slcursor_get_case(c);
|
||||
|
||||
if (query_test_exec_also_in_server(qtest)) {
|
||||
MYSQL_RES* results;
|
||||
MYSQL_ROW record;
|
||||
const char* query_str;
|
||||
|
||||
query_str = query_test_get_querystr(qtest);
|
||||
failp = mysql_query(mysql, query_str);
|
||||
|
||||
if (failp) {
|
||||
ss_dfprintf(stderr,
|
||||
"* Failed: \"%s\" -> %d : %s\n",
|
||||
query_str,
|
||||
mysql_errno(mysql),
|
||||
mysql_error(mysql));
|
||||
} else {
|
||||
ss_dfprintf(stderr,
|
||||
"Succeed\t: \"%s\"\n",
|
||||
query_str);
|
||||
results = mysql_store_result(mysql);
|
||||
|
||||
if (results != NULL) {
|
||||
|
||||
while((record = mysql_fetch_row(results))) {
|
||||
while(record != NULL && *record != NULL) {
|
||||
ss_dfprintf(stderr, "%s ", *record);
|
||||
record++;
|
||||
}
|
||||
ss_dfprintf(stderr, "\n");
|
||||
}
|
||||
mysql_free_result(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
succp = slcursor_step_ahead(c);
|
||||
|
||||
}
|
||||
slist_done(c);
|
||||
fprintf(stderr, "------------------------------------------\n");
|
||||
|
||||
return_with_handle:
|
||||
mysql_close(mysql);
|
||||
mysql_thread_end();
|
||||
mysql_library_end();
|
||||
|
||||
return_without_server:
|
||||
ss_dfprintf(stderr, "\n<< testmain\n");
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user