Merge branch 'Z3' of https://github.com/skysql/MaxScale into Z3
Conflicts: server/modules/routing/readwritesplit/readwritesplit.c
This commit is contained in:
@ -12,7 +12,7 @@
|
|||||||
#
|
#
|
||||||
# Set debug flags
|
# Set debug flags
|
||||||
#
|
#
|
||||||
DEBUG :=
|
DEBUG := ${MAXSCALE_DEBUG}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Set build env
|
# Set build env
|
||||||
@ -22,7 +22,7 @@ UNIX := Y
|
|||||||
#
|
#
|
||||||
# Set MaxScale branch directory
|
# Set MaxScale branch directory
|
||||||
#
|
#
|
||||||
ROOT_PATH := $(HOME)/src/bazaar/tmp/maxscale
|
ROOT_PATH := $(HOME)/${MAXSCALE_SOURCE}
|
||||||
|
|
||||||
INC_PATH := $(HOME)/usr/include
|
INC_PATH := $(HOME)/usr/include
|
||||||
#
|
#
|
||||||
@ -38,7 +38,7 @@ MYSQL_HEADERS := -I$(INC_PATH) -I$(MYSQL_ROOT)/ -I$(MYSQL_ROOT)/private/ -I$(MYS
|
|||||||
#
|
#
|
||||||
# Set DYNLIB=Y if you want to link MaxScale with dynamic embedded lib
|
# Set DYNLIB=Y if you want to link MaxScale with dynamic embedded lib
|
||||||
#
|
#
|
||||||
DYNLIB :=
|
DYNLIB := ${MAXSCALE_DYNLIB}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Set path to Embedded MySQL Server
|
# Set path to Embedded MySQL Server
|
||||||
@ -51,3 +51,4 @@ endif
|
|||||||
# Set path to MySQL errors file
|
# Set path to MySQL errors file
|
||||||
#
|
#
|
||||||
ERRMSG := $(HOME)/usr/share/mysql
|
ERRMSG := $(HOME)/usr/share/mysql
|
||||||
|
|
||||||
|
@ -543,6 +543,13 @@ static skygw_query_type_t resolve_query_type(
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
type |= QUERY_TYPE_WRITE;
|
type |= QUERY_TYPE_WRITE;
|
||||||
|
|
||||||
|
if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE &&
|
||||||
|
lex->sql_command == SQLCOM_CREATE_TABLE)
|
||||||
|
{
|
||||||
|
type |= QUERY_TYPE_CREATE_TMP_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
goto return_qtype;
|
goto return_qtype;
|
||||||
}
|
}
|
||||||
@ -884,6 +891,183 @@ char* skygw_query_classifier_get_stmtname(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Finds the head of the list of tables affected by the current select statement.
|
||||||
|
* @param thd Pointer to a valid THD
|
||||||
|
* @return Pointer to the head of the TABLE_LIST chain or NULL in case of an error
|
||||||
|
*/
|
||||||
|
void* skygw_get_affected_tables(void* thdp)
|
||||||
|
{
|
||||||
|
THD* thd = (THD*)thdp;
|
||||||
|
|
||||||
|
if(thd == NULL ||
|
||||||
|
thd->lex == NULL ||
|
||||||
|
thd->lex->current_select == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(thd != NULL &&
|
||||||
|
thd->lex != NULL &&
|
||||||
|
thd->lex->current_select != NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void*)thd->lex->current_select->table_list.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the parsetree and lists all the affected tables and views in the query.
|
||||||
|
* In the case of an error, the size of the table is set to zero and no memory is allocated.
|
||||||
|
* The caller must free the allocated memory.
|
||||||
|
*
|
||||||
|
* @param querybuf GWBUF where the table names are extracted from
|
||||||
|
* @param tblsize Pointer where the number of tables is written
|
||||||
|
* @return Array of null-terminated strings with the table names
|
||||||
|
*/
|
||||||
|
char** skygw_get_table_names(GWBUF* querybuf,int* tblsize)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi;
|
||||||
|
MYSQL* mysql;
|
||||||
|
THD* thd;
|
||||||
|
TABLE_LIST* tbl;
|
||||||
|
int i = 0, currtblsz = 0;
|
||||||
|
char**tables,**tmp;
|
||||||
|
|
||||||
|
if (!GWBUF_IS_PARSED(querybuf))
|
||||||
|
{
|
||||||
|
tables = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
|
||||||
|
GWBUF_PARSING_INFO);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
tables = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pi->pi_query_plain_str == NULL ||
|
||||||
|
(mysql = (MYSQL *)pi->pi_handle) == NULL ||
|
||||||
|
(thd = (THD *)mysql->thd) == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(pi->pi_query_plain_str != NULL &&
|
||||||
|
mysql != NULL &&
|
||||||
|
thd != NULL);
|
||||||
|
tables = NULL;
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
thd->lex->current_select = thd->lex->all_selects_list;
|
||||||
|
|
||||||
|
while(thd->lex->current_select){
|
||||||
|
|
||||||
|
tbl = (TABLE_LIST*)skygw_get_affected_tables(thd);
|
||||||
|
|
||||||
|
while (tbl)
|
||||||
|
{
|
||||||
|
if(i >= currtblsz){
|
||||||
|
|
||||||
|
tmp = (char**)malloc(sizeof(char*)*(currtblsz*2+1));
|
||||||
|
|
||||||
|
if(tmp){
|
||||||
|
if(currtblsz > 0){
|
||||||
|
int x;
|
||||||
|
for(x = 0;x<currtblsz;x++){
|
||||||
|
tmp[x] = tables[x];
|
||||||
|
}
|
||||||
|
free(tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
tables = tmp;
|
||||||
|
currtblsz = currtblsz*2 + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
tables[i++] = strdup(tbl->alias);
|
||||||
|
tbl=tbl->next_local;
|
||||||
|
}
|
||||||
|
thd->lex->current_select = thd->lex->current_select->next_select_in_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
retblock:
|
||||||
|
*tblsize = i;
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract the name of the created table.
|
||||||
|
* @param querybuf Buffer to use.
|
||||||
|
* @return A pointer to the name if a table was created, otherwise NULL
|
||||||
|
*/
|
||||||
|
char* skygw_get_created_table_name(GWBUF* querybuf)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi;
|
||||||
|
MYSQL* mysql;
|
||||||
|
THD* thd;
|
||||||
|
if (!GWBUF_IS_PARSED(querybuf))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
|
||||||
|
GWBUF_PARSING_INFO);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mysql = (MYSQL *)pi->pi_handle) == NULL ||
|
||||||
|
(thd = (THD *)mysql->thd) == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(mysql != NULL &&
|
||||||
|
thd != NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(thd->lex->create_last_non_select_table &&
|
||||||
|
thd->lex->create_last_non_select_table->table_name){
|
||||||
|
char* name = strdup(thd->lex->create_last_non_select_table->table_name);
|
||||||
|
return name;
|
||||||
|
}else{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks whether the buffer contains a DROP TABLE... query.
|
||||||
|
* @param querybuf Buffer to inspect
|
||||||
|
* @return true if it contains the query otherwise false
|
||||||
|
*/
|
||||||
|
bool is_drop_table_query(GWBUF* querybuf)
|
||||||
|
{
|
||||||
|
parsing_info_t* pi;
|
||||||
|
MYSQL* mysql;
|
||||||
|
THD* thd;
|
||||||
|
|
||||||
|
if (!GWBUF_IS_PARSED(querybuf))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
|
||||||
|
GWBUF_PARSING_INFO);
|
||||||
|
|
||||||
|
if (pi == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mysql = (MYSQL *)pi->pi_handle) == NULL ||
|
||||||
|
(thd = (THD *)mysql->thd) == NULL)
|
||||||
|
{
|
||||||
|
ss_dassert(mysql != NULL &&
|
||||||
|
thd != NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return thd->lex->sql_command == SQLCOM_DROP_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
* Replace user-provided literals with question marks. Return a copy of the
|
* Replace user-provided literals with question marks. Return a copy of the
|
||||||
* querystr with replacements.
|
* querystr with replacements.
|
||||||
*
|
*
|
||||||
|
@ -45,7 +45,9 @@ typedef enum {
|
|||||||
QUERY_TYPE_PREPARE_NAMED_STMT = 0x0400, /*< Prepared stmt with name from user */
|
QUERY_TYPE_PREPARE_NAMED_STMT = 0x0400, /*< Prepared stmt with name from user */
|
||||||
QUERY_TYPE_PREPARE_STMT = 0x0800, /*< Prepared stmt with id provided by server */
|
QUERY_TYPE_PREPARE_STMT = 0x0800, /*< Prepared stmt with id provided by server */
|
||||||
QUERY_TYPE_EXEC_STMT = 0x1000, /*< Execute prepared statement */
|
QUERY_TYPE_EXEC_STMT = 0x1000, /*< Execute prepared statement */
|
||||||
QUERY_TYPE_SESSION_READ = 0x2000 /*< Read session data (from master 31.8.14) */
|
QUERY_TYPE_SESSION_READ = 0x2000, /*< Read session data (from master 31.8.14) */
|
||||||
|
QUERY_TYPE_CREATE_TMP_TABLE = 0x4000, /*< Create temporary table */
|
||||||
|
QUERY_TYPE_READ_TMP_TABLE = 0x8000 /*< Read temporary table */
|
||||||
} skygw_query_type_t;
|
} skygw_query_type_t;
|
||||||
|
|
||||||
|
|
||||||
@ -72,6 +74,10 @@ skygw_query_type_t query_classifier_get_type(GWBUF* querybuf);
|
|||||||
|
|
||||||
/** Free THD context and close MYSQL */
|
/** Free THD context and close MYSQL */
|
||||||
char* skygw_query_classifier_get_stmtname(MYSQL* mysql);
|
char* skygw_query_classifier_get_stmtname(MYSQL* mysql);
|
||||||
|
char* skygw_get_created_table_name(GWBUF* querybuf);
|
||||||
|
bool is_drop_table_query(GWBUF* querybuf);
|
||||||
|
void* skygw_get_affected_tables(void* thdp);
|
||||||
|
char** skygw_get_table_names(GWBUF* querybuf,int* tblsize);
|
||||||
char* skygw_get_canonical(GWBUF* querybuf);
|
char* skygw_get_canonical(GWBUF* querybuf);
|
||||||
bool parse_query (GWBUF* querybuf);
|
bool parse_query (GWBUF* querybuf);
|
||||||
parsing_info_t* parsing_info_init(void (*donefun)(void *));
|
parsing_info_t* parsing_info_init(void (*donefun)(void *));
|
||||||
|
@ -298,6 +298,7 @@ char* admin_remove_user(
|
|||||||
fname,
|
fname,
|
||||||
err)));
|
err)));
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
fclose(fp_tmp);
|
||||||
unlink(fname_tmp);
|
unlink(fname_tmp);
|
||||||
return ADMIN_ERR_PWDFILEACCESS;
|
return ADMIN_ERR_PWDFILEACCESS;
|
||||||
}
|
}
|
||||||
@ -325,6 +326,7 @@ char* admin_remove_user(
|
|||||||
fname,
|
fname,
|
||||||
err)));
|
err)));
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
fclose(fp_tmp);
|
||||||
unlink(fname_tmp);
|
unlink(fname_tmp);
|
||||||
return ADMIN_ERR_PWDFILEACCESS;
|
return ADMIN_ERR_PWDFILEACCESS;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,29 @@ static char *config_file = NULL;
|
|||||||
static GATEWAY_CONF gateway;
|
static GATEWAY_CONF gateway;
|
||||||
char *version_string = NULL;
|
char *version_string = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trim whitespace from the front and rear of a string
|
||||||
|
*
|
||||||
|
* @param str String to trim
|
||||||
|
* @return Trimmed string, changes are done in situ
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
trim(char *str)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
while (isspace(*str))
|
||||||
|
str++;
|
||||||
|
|
||||||
|
/* Point to last character of the string */
|
||||||
|
ptr = str + strlen(str) - 1;
|
||||||
|
while (ptr > str && isspace(*ptr))
|
||||||
|
*ptr-- = 0;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config item handler for the ini file reader
|
* Config item handler for the ini file reader
|
||||||
*
|
*
|
||||||
@ -508,7 +531,7 @@ int error_count = 0;
|
|||||||
CONFIG_CONTEXT *obj1 = context;
|
CONFIG_CONTEXT *obj1 = context;
|
||||||
while (obj1)
|
while (obj1)
|
||||||
{
|
{
|
||||||
if (strcmp(s, obj1->object) == 0 &&
|
if (strcmp(trim(s), obj1->object) == 0 &&
|
||||||
obj->element && obj1->element)
|
obj->element && obj1->element)
|
||||||
{
|
{
|
||||||
serviceAddBackend(
|
serviceAddBackend(
|
||||||
|
@ -380,7 +380,7 @@ getUsers(SERVICE *service, struct users *users)
|
|||||||
memcpy(users->cksum, hash, SHA_DIGEST_LENGTH);
|
memcpy(users->cksum, hash, SHA_DIGEST_LENGTH);
|
||||||
|
|
||||||
free(users_data);
|
free(users_data);
|
||||||
|
free(key.user);
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
mysql_close(con);
|
mysql_close(con);
|
||||||
|
|
||||||
|
@ -318,6 +318,9 @@ filterApply(FILTER_DEF *filter, SESSION *session, DOWNSTREAM *downstream)
|
|||||||
{
|
{
|
||||||
DOWNSTREAM *me;
|
DOWNSTREAM *me;
|
||||||
|
|
||||||
|
if (filter == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (filter->obj == NULL)
|
if (filter->obj == NULL)
|
||||||
{
|
{
|
||||||
/* Filter not yet loaded */
|
/* Filter not yet loaded */
|
||||||
@ -359,7 +362,7 @@ DOWNSTREAM *me;
|
|||||||
UPSTREAM *
|
UPSTREAM *
|
||||||
filterUpstream(FILTER_DEF *filter, void *fsession, UPSTREAM *upstream)
|
filterUpstream(FILTER_DEF *filter, void *fsession, UPSTREAM *upstream)
|
||||||
{
|
{
|
||||||
UPSTREAM *me;
|
UPSTREAM *me = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The the filter has no setUpstream entry point then is does
|
* The the filter has no setUpstream entry point then is does
|
||||||
|
@ -63,6 +63,10 @@ static void hashtable_read_lock(HASHTABLE *table);
|
|||||||
static void hashtable_read_unlock(HASHTABLE *table);
|
static void hashtable_read_unlock(HASHTABLE *table);
|
||||||
static void hashtable_write_lock(HASHTABLE *table);
|
static void hashtable_write_lock(HASHTABLE *table);
|
||||||
static void hashtable_write_unlock(HASHTABLE *table);
|
static void hashtable_write_unlock(HASHTABLE *table);
|
||||||
|
static HASHTABLE *hashtable_alloc_real(HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special null function used as default memory allfunctions in the hashtable
|
* Special null function used as default memory allfunctions in the hashtable
|
||||||
@ -88,11 +92,39 @@ nullfn(void *data)
|
|||||||
*/
|
*/
|
||||||
HASHTABLE *
|
HASHTABLE *
|
||||||
hashtable_alloc(int size, int (*hashfn)(), int (*cmpfn)())
|
hashtable_alloc(int size, int (*hashfn)(), int (*cmpfn)())
|
||||||
|
{
|
||||||
|
return hashtable_alloc_real(NULL, size, hashfn, cmpfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
HASHTABLE* hashtable_alloc_flat(
|
||||||
|
HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)())
|
||||||
|
{
|
||||||
|
return hashtable_alloc_real(target, size, hashfn, cmpfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HASHTABLE *
|
||||||
|
hashtable_alloc_real(
|
||||||
|
HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)())
|
||||||
{
|
{
|
||||||
HASHTABLE *rval;
|
HASHTABLE *rval;
|
||||||
|
|
||||||
|
if (target == NULL)
|
||||||
|
{
|
||||||
if ((rval = malloc(sizeof(HASHTABLE))) == NULL)
|
if ((rval = malloc(sizeof(HASHTABLE))) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
rval->ht_isflat = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rval = target;
|
||||||
|
rval->ht_isflat = true;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
rval->ht_chk_top = CHK_NUM_HASHTABLE;
|
rval->ht_chk_top = CHK_NUM_HASHTABLE;
|
||||||
@ -143,8 +175,12 @@ HASHENTRIES *entry, *ptr;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(table->entries);
|
free(table->entries);
|
||||||
|
|
||||||
|
if (!table->ht_isflat)
|
||||||
|
{
|
||||||
free(table);
|
free(table);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide memory management functions to the hash table. This allows
|
* Provide memory management functions to the hash table. This allows
|
||||||
|
@ -1009,7 +1009,7 @@ bool service_set_param_value (
|
|||||||
{
|
{
|
||||||
char* p;
|
char* p;
|
||||||
int valint;
|
int valint;
|
||||||
bool succp;
|
bool succp = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find out whether the value is numeric and ends with '%' or '\0'
|
* Find out whether the value is numeric and ends with '%' or '\0'
|
||||||
|
@ -333,12 +333,14 @@ bool session_free(
|
|||||||
{
|
{
|
||||||
for (i = 0; i < session->n_filters; i++)
|
for (i = 0; i < session->n_filters; i++)
|
||||||
{
|
{
|
||||||
|
if (session->filters[i].filter)
|
||||||
session->filters[i].filter->obj->closeSession(
|
session->filters[i].filter->obj->closeSession(
|
||||||
session->filters[i].instance,
|
session->filters[i].instance,
|
||||||
session->filters[i].session);
|
session->filters[i].session);
|
||||||
}
|
}
|
||||||
for (i = 0; i < session->n_filters; i++)
|
for (i = 0; i < session->n_filters; i++)
|
||||||
{
|
{
|
||||||
|
if (session->filters[i].filter)
|
||||||
session->filters[i].filter->obj->freeSession(
|
session->filters[i].filter->obj->freeSession(
|
||||||
session->filters[i].instance,
|
session->filters[i].instance,
|
||||||
session->filters[i].session);
|
session->filters[i].session);
|
||||||
@ -653,6 +655,14 @@ int i;
|
|||||||
session->n_filters = service->n_filters;
|
session->n_filters = service->n_filters;
|
||||||
for (i = service->n_filters - 1; i >= 0; i--)
|
for (i = service->n_filters - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
if (service->filters[i] == NULL)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Service '%s' contians an unresolved filter.\n",
|
||||||
|
service->name)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if ((head = filterApply(service->filters[i], session,
|
if ((head = filterApply(service->filters[i], session,
|
||||||
&session->head)) == NULL)
|
&session->head)) == NULL)
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ include ../../../makefile.inc
|
|||||||
include ../../../test.inc
|
include ../../../test.inc
|
||||||
|
|
||||||
CC=cc
|
CC=cc
|
||||||
TESTLOG := $(shell pwd)/testhash.log
|
TESTLOG := $(shell pwd)/testcore.log
|
||||||
|
|
||||||
LOGPATH := $(ROOT_PATH)/log_manager
|
LOGPATH := $(ROOT_PATH)/log_manager
|
||||||
UTILSPATH := $(ROOT_PATH)/utils
|
UTILSPATH := $(ROOT_PATH)/utils
|
||||||
@ -21,7 +21,7 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \
|
|||||||
LIBS= -lz -lm -lcrypt -lcrypto -ldl -laio -lrt -pthread -llog_manager \
|
LIBS= -lz -lm -lcrypt -lcrypto -ldl -laio -lrt -pthread -llog_manager \
|
||||||
-L../../inih/extra -linih -lssl -lstdc++
|
-L../../inih/extra -linih -lssl -lstdc++
|
||||||
|
|
||||||
TESTS=testhash testspinlock testfilter
|
TESTS=testhash testspinlock testfilter testadminusers
|
||||||
|
|
||||||
cleantests:
|
cleantests:
|
||||||
- $(DEL) *.o
|
- $(DEL) *.o
|
||||||
@ -40,17 +40,25 @@ testhash: testhash.c
|
|||||||
-I$(ROOT_PATH)/server/include \
|
-I$(ROOT_PATH)/server/include \
|
||||||
-I$(ROOT_PATH)/utils \
|
-I$(ROOT_PATH)/utils \
|
||||||
testhash.c ../hashtable.o ../atomic.o ../spinlock.o -o testhash
|
testhash.c ../hashtable.o ../atomic.o ../spinlock.o -o testhash
|
||||||
|
|
||||||
testspinlock: testspinlock.c
|
testspinlock: testspinlock.c
|
||||||
$(CC) $(CFLAGS) \
|
$(CC) $(CFLAGS) \
|
||||||
-I$(ROOT_PATH)/server/include \
|
-I$(ROOT_PATH)/server/include \
|
||||||
-I$(ROOT_PATH)/utils \
|
-I$(ROOT_PATH)/utils \
|
||||||
testspinlock.c ../spinlock.o ../atomic.o ../thread.o -o testspinlock
|
testspinlock.c ../spinlock.o ../atomic.o ../thread.o -o testspinlock
|
||||||
|
|
||||||
testfilter: testfilter.c libcore.a
|
testfilter: testfilter.c libcore.a
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) \
|
$(CC) $(CFLAGS) $(LDFLAGS) \
|
||||||
-I$(ROOT_PATH)/server/include \
|
-I$(ROOT_PATH)/server/include \
|
||||||
-I$(ROOT_PATH)/utils \
|
-I$(ROOT_PATH)/utils \
|
||||||
testfilter.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testfilter
|
testfilter.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testfilter
|
||||||
|
|
||||||
|
testadminusers: testadminusers.c libcore.a
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) \
|
||||||
|
-I$(ROOT_PATH)/server/include \
|
||||||
|
-I$(ROOT_PATH)/utils \
|
||||||
|
testadminusers.c libcore.a $(UTILSPATH)/skygw_utils.o $(LIBS) -o testadminusers
|
||||||
|
|
||||||
libcore.a: ../*.o
|
libcore.a: ../*.o
|
||||||
ar rv libcore.a ../*.o
|
ar rv libcore.a ../*.o
|
||||||
|
|
||||||
|
278
server/core/test/testadminusers.c
Normal file
278
server/core/test/testadminusers.c
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* 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 SkySQL Ab 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
|
*
|
||||||
|
* Date Who Description
|
||||||
|
* 20-08-2014 Mark Riddoch Initial implementation
|
||||||
|
*
|
||||||
|
* @endverbatim
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <adminusers.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test1 default user
|
||||||
|
*
|
||||||
|
* Test that the username password admin/skysql is accepted if no users
|
||||||
|
* have been created and that no other users are accepted
|
||||||
|
*
|
||||||
|
* WARNING: $MAXSCALE_HOME/etc/passwd must be removed before this test is run
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test1()
|
||||||
|
{
|
||||||
|
if (admin_verify("admin", "skysql") == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 1.1 (default user) failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (admin_verify("bad", "user"))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 1.2 (wrong user) failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test2 creating users
|
||||||
|
*
|
||||||
|
* Create a user
|
||||||
|
* Try to create a duplicate user - expects a failure
|
||||||
|
* Remove that user - expected to fail as one user must always remain
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test2()
|
||||||
|
{
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if ((err = admin_add_user("user0", "passwd0")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 2.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (admin_add_user("user0", "passwd0") == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 2.2 (add user) failed, du;plicate.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deleting the last user is forbidden so we expect this to fail */
|
||||||
|
if ((err = admin_remove_user("user0", "passwd0")) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 2.3 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test3 search/verify users
|
||||||
|
*
|
||||||
|
* Create a user
|
||||||
|
* Search for that user
|
||||||
|
* Search for a non-existant user
|
||||||
|
* Remove the user
|
||||||
|
* Search for the user that was removed
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test3()
|
||||||
|
{
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if ((err = admin_add_user("user1", "passwd1")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 3.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admin_search_user("user1") == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 3.2 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (admin_search_user("user2") != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 3.3 (search user) failed, unexpeted user found.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = admin_remove_user("user1", "passwd1")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 3.4 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admin_search_user("user1"))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 3.5 (search user) failed - user was deleted.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test4 verify users
|
||||||
|
*
|
||||||
|
* Create a numebr of users
|
||||||
|
* search for each user in turn
|
||||||
|
* verify each user in turn (password verification)
|
||||||
|
* Verify each user in turn with incorrect password
|
||||||
|
* Randomly verify each user
|
||||||
|
* Remove each user
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test4()
|
||||||
|
{
|
||||||
|
char *err, user[40], passwd[40];
|
||||||
|
int i, n_users = 50;
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "passwd%d", i);
|
||||||
|
if ((err = admin_add_user(user, passwd)) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 4.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
if (admin_search_user(user) == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_search_user: test 4.2 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "passwd%d", i);
|
||||||
|
if (admin_verify(user, passwd) == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 4.3 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "badpasswd%d", i);
|
||||||
|
if (admin_verify(user, passwd) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 4.4 (search user) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srand(time(0));
|
||||||
|
for (i = 1; i < 1000; i++)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
j = rand() % n_users;
|
||||||
|
if (j == 0)
|
||||||
|
j = 1;
|
||||||
|
sprintf(user, "user%d", j);
|
||||||
|
sprintf(passwd, "passwd%d", j);
|
||||||
|
if (admin_verify(user, passwd) == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_verify: test 4.5 (random) failed.\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < n_users; i++)
|
||||||
|
{
|
||||||
|
sprintf(user, "user%d", i);
|
||||||
|
sprintf(passwd, "passwd%d", i);
|
||||||
|
if ((err = admin_remove_user(user, passwd)) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 4.6 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test5 remove first user
|
||||||
|
*
|
||||||
|
* Create a user so that user0 may be removed
|
||||||
|
* Remove the first user created (user0)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
test5()
|
||||||
|
{
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if ((err = admin_add_user("user", "passwd")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_add_user: test 5.1 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((err = admin_remove_user("user0", "passwd0")) != NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "admin_remove_user: test 5.2 (add user) failed, %s.\n", err);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
result += test1();
|
||||||
|
result += test2();
|
||||||
|
result += test3();
|
||||||
|
result += test4();
|
||||||
|
result += test5();
|
||||||
|
|
||||||
|
exit(result);
|
||||||
|
}
|
||||||
|
|
@ -140,6 +140,8 @@ int i, n_filters = 1000;
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
*
|
*
|
||||||
* @endverbatim
|
* @endverbatim
|
||||||
*/
|
*/
|
||||||
|
#include <dcb.h>
|
||||||
|
|
||||||
#define ADMIN_SALT "MS"
|
#define ADMIN_SALT "MS"
|
||||||
|
|
||||||
extern int admin_verify(char *, char *);
|
extern int admin_verify(char *, char *);
|
||||||
|
@ -84,12 +84,17 @@ typedef struct hashtable {
|
|||||||
SPINLOCK spin; /**< Internal spinlock for the hashtable */
|
SPINLOCK spin; /**< Internal spinlock for the hashtable */
|
||||||
int n_readers; /**< Number of clients reading the table */
|
int n_readers; /**< Number of clients reading the table */
|
||||||
int writelock; /**< The table is locked by a writer */
|
int writelock; /**< The table is locked by a writer */
|
||||||
|
bool ht_isflat; /**< Indicates whether hashtable is in stack or heap */
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
skygw_chk_t ht_chk_tail;
|
skygw_chk_t ht_chk_tail;
|
||||||
#endif
|
#endif
|
||||||
} HASHTABLE;
|
} HASHTABLE;
|
||||||
|
|
||||||
extern HASHTABLE *hashtable_alloc(int, int (*hashfn)(), int (*cmpfn)());
|
extern HASHTABLE *hashtable_alloc(int, int (*hashfn)(), int (*cmpfn)());
|
||||||
|
HASHTABLE *hashtable_alloc_flat(HASHTABLE* target,
|
||||||
|
int size,
|
||||||
|
int (*hashfn)(),
|
||||||
|
int (*cmpfn)());
|
||||||
/**< Allocate a hashtable */
|
/**< Allocate a hashtable */
|
||||||
extern void hashtable_memory_fns(HASHTABLE *, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN);
|
extern void hashtable_memory_fns(HASHTABLE *, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN);
|
||||||
/**< Provide an interface to control key/value memory
|
/**< Provide an interface to control key/value memory
|
||||||
|
@ -92,7 +92,8 @@ typedef enum rses_property_type_t {
|
|||||||
RSES_PROP_TYPE_UNDEFINED=-1,
|
RSES_PROP_TYPE_UNDEFINED=-1,
|
||||||
RSES_PROP_TYPE_SESCMD=0,
|
RSES_PROP_TYPE_SESCMD=0,
|
||||||
RSES_PROP_TYPE_FIRST = RSES_PROP_TYPE_SESCMD,
|
RSES_PROP_TYPE_FIRST = RSES_PROP_TYPE_SESCMD,
|
||||||
RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_SESCMD,
|
RSES_PROP_TYPE_TMPTABLES,
|
||||||
|
RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_TMPTABLES,
|
||||||
RSES_PROP_TYPE_COUNT=RSES_PROP_TYPE_LAST+1
|
RSES_PROP_TYPE_COUNT=RSES_PROP_TYPE_LAST+1
|
||||||
} rses_property_type_t;
|
} rses_property_type_t;
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ struct rses_property_st {
|
|||||||
rses_property_type_t rses_prop_type;
|
rses_property_type_t rses_prop_type;
|
||||||
union rses_prop_data {
|
union rses_prop_data {
|
||||||
mysql_sescmd_t sescmd;
|
mysql_sescmd_t sescmd;
|
||||||
void* placeholder; /*< to be removed due new type */
|
HASHTABLE* temp_tables;
|
||||||
} rses_prop_data;
|
} rses_prop_data;
|
||||||
rses_property_t* rses_prop_next; /*< next property of same type */
|
rses_property_t* rses_prop_next; /*< next property of same type */
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
|
@ -281,6 +281,47 @@ static bool have_enough_servers(
|
|||||||
static SPINLOCK instlock;
|
static SPINLOCK instlock;
|
||||||
static ROUTER_INSTANCE* instances;
|
static ROUTER_INSTANCE* instances;
|
||||||
|
|
||||||
|
static int hashkeyfun(void* key);
|
||||||
|
static int hashcmpfun (void *, void *);
|
||||||
|
|
||||||
|
static int hashkeyfun(
|
||||||
|
void* key)
|
||||||
|
{
|
||||||
|
if(key == NULL){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsigned int hash = 0,c = 0;
|
||||||
|
char* ptr = (char*)key;
|
||||||
|
while((c = *ptr++)){
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
}
|
||||||
|
return *(int *)key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hashcmpfun(
|
||||||
|
void* v1,
|
||||||
|
void* v2)
|
||||||
|
{
|
||||||
|
char* i1 = (char*) v1;
|
||||||
|
char* i2 = (char*) v2;
|
||||||
|
|
||||||
|
return strcmp(i1,i2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* hstrdup(void* fval)
|
||||||
|
{
|
||||||
|
char* str = (char*)fval;
|
||||||
|
return strdup(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void* hfree(void* fval)
|
||||||
|
{
|
||||||
|
free (fval);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the mandatory version entry point
|
* Implementation of the mandatory version entry point
|
||||||
*
|
*
|
||||||
@ -1211,11 +1252,21 @@ static int routeQuery(
|
|||||||
mysql_server_cmd_t packet_type;
|
mysql_server_cmd_t packet_type;
|
||||||
uint8_t* packet;
|
uint8_t* packet;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int tsize = 0;
|
||||||
|
int klen = 0;
|
||||||
|
int i = 0;
|
||||||
DCB* master_dcb = NULL;
|
DCB* master_dcb = NULL;
|
||||||
DCB* target_dcb = NULL;
|
DCB* target_dcb = NULL;
|
||||||
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
|
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
|
||||||
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
||||||
|
rses_property_t* rses_prop_tmp;
|
||||||
bool rses_is_closed = false;
|
bool rses_is_closed = false;
|
||||||
|
bool target_tmp_table = false;
|
||||||
|
char* dbname;
|
||||||
|
char* hkey;
|
||||||
|
char** tbl;
|
||||||
|
HASHTABLE* h;
|
||||||
|
MYSQL_session* data;
|
||||||
size_t len;
|
size_t len;
|
||||||
route_target_t route_target;
|
route_target_t route_target;
|
||||||
bool succp = false;
|
bool succp = false;
|
||||||
@ -1234,6 +1285,7 @@ static int routeQuery(
|
|||||||
|
|
||||||
packet = GWBUF_DATA(querybuf);
|
packet = GWBUF_DATA(querybuf);
|
||||||
packet_type = packet[4];
|
packet_type = packet[4];
|
||||||
|
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
||||||
|
|
||||||
if (rses_is_closed)
|
if (rses_is_closed)
|
||||||
{
|
{
|
||||||
@ -1265,6 +1317,9 @@ static int routeQuery(
|
|||||||
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
|
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
|
||||||
CHK_DCB(master_dcb);
|
CHK_DCB(master_dcb);
|
||||||
|
|
||||||
|
data = (MYSQL_session*)master_dcb->session->data;
|
||||||
|
dbname = data->db;
|
||||||
|
|
||||||
switch(packet_type) {
|
switch(packet_type) {
|
||||||
case MYSQL_COM_QUIT: /*< 1 QUIT will close all sessions */
|
case MYSQL_COM_QUIT: /*< 1 QUIT will close all sessions */
|
||||||
case MYSQL_COM_INIT_DB: /*< 2 DDL must go to the master */
|
case MYSQL_COM_INIT_DB: /*< 2 DDL must go to the master */
|
||||||
@ -1309,6 +1364,48 @@ static int routeQuery(
|
|||||||
break;
|
break;
|
||||||
} /**< switch by packet type */
|
} /**< switch by packet type */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the query targets a temporary table
|
||||||
|
*/
|
||||||
|
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ))
|
||||||
|
{
|
||||||
|
tbl = skygw_get_table_names(querybuf,&tsize);
|
||||||
|
|
||||||
|
if (tsize > 0)
|
||||||
|
{
|
||||||
|
/** Query targets at least one table */
|
||||||
|
for(i = 0; i<tsize && !target_tmp_table && tbl[i]; i++)
|
||||||
|
{
|
||||||
|
klen = strlen(dbname) + strlen(tbl[i]) + 2;
|
||||||
|
hkey = calloc(klen,sizeof(char));
|
||||||
|
strcpy(hkey,dbname);
|
||||||
|
strcat(hkey,".");
|
||||||
|
strcat(hkey,tbl[0]);
|
||||||
|
|
||||||
|
if (rses_prop_tmp &&
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables)
|
||||||
|
{
|
||||||
|
|
||||||
|
if( (target_tmp_table =
|
||||||
|
(bool)hashtable_fetch(rses_prop_tmp->rses_prop_data.temp_tables,(void *)hkey)))
|
||||||
|
{
|
||||||
|
/**Query target is a temporary table*/
|
||||||
|
qtype = QUERY_TYPE_READ_TMP_TABLE;
|
||||||
|
LOGIF(LT,
|
||||||
|
(skygw_log_write(LOGFILE_TRACE,
|
||||||
|
"Query targets a temporary table: %s",hkey)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(hkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i<tsize; i++)
|
||||||
|
{
|
||||||
|
free(tbl[i]);
|
||||||
|
}
|
||||||
|
free(tbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* If autocommit is disabled or transaction is explicitly started
|
* If autocommit is disabled or transaction is explicitly started
|
||||||
* transaction becomes active and master gets all statements until
|
* transaction becomes active and master gets all statements until
|
||||||
@ -1345,6 +1442,118 @@ static int routeQuery(
|
|||||||
router_cli_ses->rses_autocommit_enabled = true;
|
router_cli_ses->rses_autocommit_enabled = true;
|
||||||
router_cli_ses->rses_transaction_active = false;
|
router_cli_ses->rses_transaction_active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If query is of type QUERY_TYPE_CREATE_TMP_TABLE then find out
|
||||||
|
* the database and table name, create a hashvalue and
|
||||||
|
* add it to the router client session's property. If property
|
||||||
|
* doesn't exist then create it first. If the query is DROP TABLE...
|
||||||
|
* then see if it targets a temporary table and remove it from the hashtable
|
||||||
|
* if it does.
|
||||||
|
*/
|
||||||
|
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_CREATE_TMP_TABLE))
|
||||||
|
{
|
||||||
|
bool is_temp = true;
|
||||||
|
char* tblname = NULL;
|
||||||
|
|
||||||
|
tblname = skygw_get_created_table_name(querybuf);
|
||||||
|
|
||||||
|
if (tblname && strlen(tblname) > 0)
|
||||||
|
{
|
||||||
|
klen = strlen(dbname) + strlen(tblname) + 2;
|
||||||
|
hkey = calloc(klen,sizeof(char));
|
||||||
|
strcpy(hkey,dbname);
|
||||||
|
strcat(hkey,".");
|
||||||
|
strcat(hkey,tblname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hkey = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rses_prop_tmp == NULL)
|
||||||
|
{
|
||||||
|
if((rses_prop_tmp =
|
||||||
|
(rses_property_t*)calloc(1,sizeof(rses_property_t))))
|
||||||
|
{
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
rses_prop_tmp->rses_prop_chk_top = CHK_NUM_ROUTER_PROPERTY;
|
||||||
|
rses_prop_tmp->rses_prop_chk_tail = CHK_NUM_ROUTER_PROPERTY;
|
||||||
|
#endif
|
||||||
|
rses_prop_tmp->rses_prop_rsession = router_cli_ses;
|
||||||
|
rses_prop_tmp->rses_prop_refcount = 1;
|
||||||
|
rses_prop_tmp->rses_prop_next = NULL;
|
||||||
|
rses_prop_tmp->rses_prop_type = RSES_PROP_TYPE_TMPTABLES;
|
||||||
|
router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES] = rses_prop_tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rses_prop_tmp->rses_prop_data.temp_tables == NULL)
|
||||||
|
{
|
||||||
|
h = hashtable_alloc(7, hashkeyfun, hashcmpfun);
|
||||||
|
hashtable_memory_fns(h,hstrdup,NULL,hfree,NULL);
|
||||||
|
if (h != NULL)
|
||||||
|
{
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hkey &&
|
||||||
|
hashtable_add(rses_prop_tmp->rses_prop_data.temp_tables,
|
||||||
|
(void *)hkey,
|
||||||
|
(void *)is_temp) == 0) /*< Conflict in hash table */
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Temporary table conflict in hashtable: %s",
|
||||||
|
hkey)));
|
||||||
|
}
|
||||||
|
#if defined(SS_DEBUG)
|
||||||
|
{
|
||||||
|
bool retkey =
|
||||||
|
hashtable_fetch(
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables,
|
||||||
|
hkey);
|
||||||
|
if (retkey)
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Temporary table added: %s",
|
||||||
|
hkey)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
free(hkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if DROP TABLE... targets a temporary table */
|
||||||
|
if (is_drop_table_query(querybuf))
|
||||||
|
{
|
||||||
|
tbl = skygw_get_table_names(querybuf,&tsize);
|
||||||
|
|
||||||
|
for(i = 0; i<tsize; i++)
|
||||||
|
{
|
||||||
|
klen = strlen(dbname) + strlen(tbl[i]) + 2;
|
||||||
|
hkey = calloc(klen,sizeof(char));
|
||||||
|
strcpy(hkey,dbname);
|
||||||
|
strcat(hkey,".");
|
||||||
|
strcat(hkey,tbl[i]);
|
||||||
|
|
||||||
|
if (rses_prop_tmp &&
|
||||||
|
rses_prop_tmp->rses_prop_data.temp_tables)
|
||||||
|
{
|
||||||
|
if (hashtable_delete(rses_prop_tmp->rses_prop_data.temp_tables,
|
||||||
|
(void *)hkey))
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE,
|
||||||
|
"Temporary table dropped: %s",hkey)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(tbl[i]);
|
||||||
|
free(hkey);
|
||||||
|
}
|
||||||
|
free(tbl);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Find out where to route the query. Result may not be clear; it is
|
* Find out where to route the query. Result may not be clear; it is
|
||||||
* possible to have a hint for routing to a named server which can
|
* possible to have a hint for routing to a named server which can
|
||||||
@ -2486,6 +2695,11 @@ static void rses_property_done(
|
|||||||
case RSES_PROP_TYPE_SESCMD:
|
case RSES_PROP_TYPE_SESCMD:
|
||||||
mysql_sescmd_done(&prop->rses_prop_data.sescmd);
|
mysql_sescmd_done(&prop->rses_prop_data.sescmd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RSES_PROP_TYPE_TMPTABLES:
|
||||||
|
hashtable_free(prop->rses_prop_data.temp_tables);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOGIF(LD, (skygw_log_write(
|
LOGIF(LD, (skygw_log_write(
|
||||||
LOGFILE_DEBUG,
|
LOGFILE_DEBUG,
|
||||||
@ -2885,8 +3099,23 @@ static bool execute_sescmd_in_backend(
|
|||||||
sescmd_cursor_clone_querybuf(scur));
|
sescmd_cursor_clone_querybuf(scur));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MYSQL_COM_QUERY:
|
|
||||||
case MYSQL_COM_INIT_DB:
|
case MYSQL_COM_INIT_DB:
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Record database name and store to session.
|
||||||
|
*/
|
||||||
|
GWBUF* tmpbuf;
|
||||||
|
MYSQL_session* data;
|
||||||
|
unsigned int qlen;
|
||||||
|
|
||||||
|
data = dcb->session->data;
|
||||||
|
tmpbuf = scur->scmd_cur_cmd->my_sescmd_buf;
|
||||||
|
qlen = MYSQL_GET_PACKET_LEN((unsigned char*)tmpbuf->start);
|
||||||
|
memset(data->db,0,MYSQL_DATABASE_MAXLEN+1);
|
||||||
|
strncpy(data->db,tmpbuf->start+5,qlen - 1);
|
||||||
|
}
|
||||||
|
/** Fallthrough */
|
||||||
|
case MYSQL_COM_QUERY:
|
||||||
default:
|
default:
|
||||||
/**
|
/**
|
||||||
* Mark session command buffer, it triggers writing
|
* Mark session command buffer, it triggers writing
|
||||||
|
@ -230,6 +230,16 @@ if [ "$a" != "$TRETVAL" ]; then
|
|||||||
else
|
else
|
||||||
echo "$TINPUT PASSED">>$TLOG ;
|
echo "$TINPUT PASSED">>$TLOG ;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
TINPUT=test_temporary_table.sql
|
||||||
|
a=`$RUNCMD < ./$TINPUT`
|
||||||
|
TRETVAL=1
|
||||||
|
if [ "$a" != "$TRETVAL" ]; then
|
||||||
|
echo "$TINPUT FAILED, return value $a when $TRETVAL was expected">>$TLOG;
|
||||||
|
else
|
||||||
|
echo "$TINPUT PASSED">>$TLOG ;
|
||||||
|
fi
|
||||||
|
|
||||||
echo "-----------------------------------" >> $TLOG
|
echo "-----------------------------------" >> $TLOG
|
||||||
echo "Session variables: Stress Test 1" >> $TLOG
|
echo "Session variables: Stress Test 1" >> $TLOG
|
||||||
echo "-----------------------------------" >> $TLOG
|
echo "-----------------------------------" >> $TLOG
|
||||||
@ -238,6 +248,10 @@ RUNCMD=mysql\ --host=$THOST\ -P$TPORT\ -u$TUSER\ -p$TPWD\ --unbuffered=true\ --d
|
|||||||
TINPUT=test_sescmd2.sql
|
TINPUT=test_sescmd2.sql
|
||||||
for ((i = 0;i<1000;i++))
|
for ((i = 0;i<1000;i++))
|
||||||
do
|
do
|
||||||
|
if [[ $(( i % 50 )) -eq 0 ]]
|
||||||
|
then
|
||||||
|
printf "."
|
||||||
|
fi
|
||||||
a=`$RUNCMD < $TINPUT 2>&1`
|
a=`$RUNCMD < $TINPUT 2>&1`
|
||||||
if [[ "`echo "$a"|grep -i 'error'`" != "" ]]
|
if [[ "`echo "$a"|grep -i 'error'`" != "" ]]
|
||||||
then
|
then
|
||||||
@ -255,15 +269,19 @@ fi
|
|||||||
echo "-----------------------------------" >> $TLOG
|
echo "-----------------------------------" >> $TLOG
|
||||||
echo "Session variables: Stress Test 2" >> $TLOG
|
echo "Session variables: Stress Test 2" >> $TLOG
|
||||||
echo "-----------------------------------" >> $TLOG
|
echo "-----------------------------------" >> $TLOG
|
||||||
|
echo ""
|
||||||
err=""
|
err=""
|
||||||
TINPUT=test_sescmd3.sql
|
TINPUT=test_sescmd3.sql
|
||||||
for ((j = 0;j<1000;j++))
|
for ((j = 0;j<1000;j++))
|
||||||
do
|
do
|
||||||
b=`$RUNCMD < $TINPUT 2>&1`
|
if [[ $(( j % 50 )) -eq 0 ]]
|
||||||
if [[ "`echo "$b"|grep -i 'null'`" != "" ]]
|
|
||||||
then
|
then
|
||||||
err=`echo "$b" | grep -i null`
|
printf "."
|
||||||
|
fi
|
||||||
|
b=`$RUNCMD < $TINPUT 2>&1`
|
||||||
|
if [[ "`echo "$b"|grep -i 'null|error'`" != "" ]]
|
||||||
|
then
|
||||||
|
err=`echo "$b" | grep -i null|error`
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
use test;
|
||||||
|
drop table if exists t1;
|
||||||
|
create temporary table t1 (id integer);
|
||||||
|
insert into t1 values(1);
|
||||||
|
select id from t1;
|
@ -1912,6 +1912,7 @@ char* replace_literal(
|
|||||||
fprintf(stderr, "Regex memory allocation failed : %s\n",
|
fprintf(stderr, "Regex memory allocation failed : %s\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
free(search_re);
|
free(search_re);
|
||||||
|
free(newstr);
|
||||||
newstr = haystack;
|
newstr = haystack;
|
||||||
goto retblock;
|
goto retblock;
|
||||||
}
|
}
|
||||||
@ -1928,6 +1929,7 @@ char* replace_literal(
|
|||||||
search_re,
|
search_re,
|
||||||
error_message);
|
error_message);
|
||||||
free(search_re);
|
free(search_re);
|
||||||
|
free(newstr);
|
||||||
newstr = haystack;
|
newstr = haystack;
|
||||||
goto retblock;
|
goto retblock;
|
||||||
}
|
}
|
||||||
@ -1936,6 +1938,7 @@ char* replace_literal(
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
free(search_re);
|
free(search_re);
|
||||||
|
free(newstr);
|
||||||
regfree(&re);
|
regfree(&re);
|
||||||
newstr = haystack;
|
newstr = haystack;
|
||||||
goto retblock;
|
goto retblock;
|
||||||
|
Reference in New Issue
Block a user