try to add query_classifier under skygteway

This commit is contained in:
vraatikka 2013-06-17 18:19:46 +03:00
parent 525861cab3
commit 4854b56aba
5 changed files with 1002 additions and 0 deletions

43
query_classifier/makefile Normal file
View 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

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

View 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

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

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