Merge branch 'develop' into 1.2.1-binlog_router_trx

This commit is contained in:
MassimilianoPinto
2015-08-17 12:12:16 +02:00
10 changed files with 270 additions and 60 deletions

View File

@ -234,6 +234,7 @@ if(PACKAGE)
else() else()
# Generic CPack configuration variables # Generic CPack configuration variables
SET(CPACK_SET_DESTDIR ON)
set(CPACK_STRIP_FILES FALSE) set(CPACK_STRIP_FILES FALSE)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale")
set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}")
@ -277,7 +278,7 @@ add_custom_target(testall
COMMENT "Running full test suite..." VERBATIM) COMMENT "Running full test suite..." VERBATIM)
add_custom_target(testcore add_custom_target(testcore
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR} COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR} -DBUILD_TESTS=Y -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} -DWITH_SCRIPTS=N -DWITH_MAXSCALE_CNF=N -DMAXSCALE_VARDIR=${CMAKE_BINARY_DIR}
COMMAND make install COMMAND make install
COMMAND ctest -R Internal COMMAND ctest -R Internal
COMMENT "Running core test suite..." VERBATIM) COMMENT "Running core test suite..." VERBATIM)

View File

@ -1,6 +1,9 @@
#Changelog #Changelog
These are the changes introduced in the next MaxScale version. This is not the official change log and the latest changelog can always be found in here: [MaxScale 1.1 Release Notes](Release-Notes/MaxScale-1.1-Release-Notes.md) These are the changes introduced in the next MaxScale version. This is not the official change log and the latest changelog can always be found in here7: [MaxScale 1.1 Release Notes](Release-Notes/MaxScale-1.1-Release-Notes.md)
## MaxScale 1.3
* Added support for persistent backend connections
## MaxScale 1.2 ## MaxScale 1.2
* Logfiles have been renamed. The log names are now named error.log, messages.log, trace.log and debug.log. * Logfiles have been renamed. The log names are now named error.log, messages.log, trace.log and debug.log.

View File

@ -75,6 +75,8 @@ You will also need some version specific packages.
#### Ubuntu 14.04 or later, Debian 8 (Jessie) or later #### Ubuntu 14.04 or later, Debian 8 (Jessie) or later
*At the time of writing, the libmariadbd-dev package is broken and does not contain the required libmysqld.a library. Please follow the install instructions for earlier version of Ubuntu and Debian.*
``` ```
libmariadbclient-dev libmariadbd-dev libmariadbclient-dev libmariadbd-dev
``` ```
@ -144,7 +146,7 @@ cmake ..
``` ```
This will automatically search your system for the right files and libraries and if you have your libraries installed in standard locations, it should succeed. If there are errors with the CMake configuration, read the error messages, provide the needed variables for CMake and call `cmake` again with the additional parameters. This will automatically search your system for the right files and libraries and if you have your libraries installed in standard locations, it should succeed. If there are errors with the CMake configuration, read the error messages, provide the needed variables for CMake and call `cmake` again with the additional parameters.
Here is an example of a cmake call with parameters for custom library locations, building of tests and without the installation of init scripts. Here is an example of a cmake call with parameters for custom library locations, building of tests and without the installation of init scripts or the example maxscale.cnf file.
``` ```
$ cmake .. -DMYSQL_DIR=/usr/mariadb-5.5.41-linux-x86_64/include/mysql \ $ cmake .. -DMYSQL_DIR=/usr/mariadb-5.5.41-linux-x86_64/include/mysql \
@ -152,7 +154,7 @@ $ cmake .. -DMYSQL_DIR=/usr/mariadb-5.5.41-linux-x86_64/include/mysql \
-DMYSQLCLIENT_LIBRARIES=/usr/mariadb-5.5.41-linux-x86_64/lib/libmysqlclient.so \ -DMYSQLCLIENT_LIBRARIES=/usr/mariadb-5.5.41-linux-x86_64/lib/libmysqlclient.so \
-DERRMSG=/usr/mariadb-5.5.41-linux-x86_64/share/english/errmsg.sys \ -DERRMSG=/usr/mariadb-5.5.41-linux-x86_64/share/english/errmsg.sys \
-DCMAKE_INSTALL_PREFIX=/home/maxscale/MaxScale -DBUILD_TESTS=Y \ -DCMAKE_INSTALL_PREFIX=/home/maxscale/MaxScale -DBUILD_TESTS=Y \
-DWITH_SCRIPTS=N -DWITH_SCRIPTS=N -DWITH_MAXSCALE_CNF=N
<pre> <pre>
-- CMake version: 2.8.12.2 -- CMake version: 2.8.12.2

View File

@ -45,9 +45,11 @@ This would in effect allow the user 'john' to only see the database 'shard' on t
The schemarouter supports the following router options: The schemarouter supports the following router options:
|option |parameter |description| |option |parameter |description|
--------------------------------------------- |-------------------|-----------|-----------|
|max_sescmd_hitory |<int> |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consupmtion of the session.| |max_sescmd_history |integer |Set a limit on the number of session modifying commands a session can execute. This sets an effective cap on the memory consumption of the session.|
|disable_sescmd_history|<boolean>|Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.| |disable_sescmd_history|true, false|Disable the session command history. This will prevent growing memory consumption of a long-running session and allows pooled connections to MaxScale to be used. The drawback of this is the fact that if a server goes down, the session state will not be consistent anymore.|
|refresh_databases|true, false|Enable database map refreshing mid-session. These are triggered by a failure to change the database i.e. `USE ...``queries.|
|refresh_interval|float|The minimum interval between database map refreshes in seconds.|
## Limitations ## Limitations
The schemarouter router currently has some limitations due to the nature of the sharding implementation and the way the session variables are detected and routed. Here is a list of the current limitations. The schemarouter router currently has some limitations due to the nature of the sharding implementation and the way the session variables are detected and routed. Here is a list of the current limitations.

View File

@ -85,6 +85,7 @@
#include <ini.h> #include <ini.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/prctl.h>
/** for procname */ /** for procname */
#if !defined(_GNU_SOURCE) #if !defined(_GNU_SOURCE)
@ -2218,6 +2219,15 @@ static int set_user(char* user)
pwname->pw_name,errno,strerror(errno)); pwname->pw_name,errno,strerror(errno));
return rval; return rval;
} }
if(prctl(PR_GET_DUMPABLE) == 0)
{
if(prctl(PR_SET_DUMPABLE ,1) == -1)
{
printf("Error: Failed to set dumpable flag on for the process '%s': %d %s\n",
pwname->pw_name,errno,strerror(errno));
return -1;
}
}
#ifdef SS_DEBUG #ifdef SS_DEBUG
else else
{ {

View File

@ -37,18 +37,6 @@
* @endverbatim * @endverbatim
*/ */
/**
* The hash function we user for storing users.
*
* @param key The key value, i.e. username
* @return The hash key
*/
static int
user_hash(char *key)
{
return (*key + *(key + 1));
}
/** /**
* Allocate a new users table * Allocate a new users table
* *
@ -65,7 +53,7 @@ USERS *rval;
return NULL; return NULL;
} }
if ((rval->data = hashtable_alloc(USERS_HASHTABLE_DEFAULT_SIZE, user_hash, strcmp)) == NULL) if ((rval->data = hashtable_alloc(USERS_HASHTABLE_DEFAULT_SIZE, simple_str_hash, strcmp)) == NULL)
{ {
skygw_log_write(LE,"[%s:%d] Error: Memory allocation failed.",__FUNCTION__,__LINE__); skygw_log_write(LE,"[%s:%d] Error: Memory allocation failed.",__FUNCTION__,__LINE__);
free(rval); free(rval);

View File

@ -241,6 +241,10 @@ typedef struct schemarouter_config_st {
target_t rw_use_sql_variables_in; target_t rw_use_sql_variables_in;
int max_sescmd_hist; int max_sescmd_hist;
bool disable_sescmd_hist; bool disable_sescmd_hist;
time_t last_refresh; /*< Last time the database list was refreshed */
double refresh_min_interval; /*< Minimum required interval between refreshes of databases */
bool refresh_databases; /*< Are databases refreshed when they are not found in the hashtable */
bool debug; /*< Enable verbose debug messages to clients */
} schemarouter_config_t; } schemarouter_config_t;
/** /**

View File

@ -1633,11 +1633,17 @@ static GWBUF* process_response_data (
{ {
uint8_t* data; uint8_t* data;
/** Read next packet length */ /** Read next packet length if there is at least
data = GWBUF_DATA(readbuf); * three bytes left. If there is less than three
nbytes_left = MYSQL_GET_PACKET_LEN(data)+MYSQL_HEADER_LEN; * bytes in the buffer or it is NULL, we need to
/** Store new status to protocol structure */ wait for more data from the backend server.*/
protocol_set_response_status(p, npackets_left, nbytes_left); if(readbuf == NULL || GWBUF_LENGTH(readbuf) < 3)
break;
data = GWBUF_DATA(readbuf);
nbytes_left = MYSQL_GET_PACKET_LEN(data)+MYSQL_HEADER_LEN;
/** Store new status to protocol structure */
protocol_set_response_status(p, npackets_left, nbytes_left);
} }
} }
} }

View File

@ -1584,9 +1584,24 @@ void check_drop_tmp_table(
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES]; rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
master_dcb = router_cli_ses->rses_master_ref->bref_dcb; master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
if(master_dcb == NULL)
{
skygw_log_write(LE,"[%s] Error: Master server DBC is NULL. "
"This means that the connection to the master server is already "
"closed while a query is still being routed.",__FUNCTION__);
return;
}
CHK_DCB(master_dcb); CHK_DCB(master_dcb);
data = (MYSQL_session*)master_dcb->session->data; data = (MYSQL_session*)master_dcb->session->data;
if(data == NULL)
{
skygw_log_write(LE,"[%s] Error: User data in master server DBC is NULL.",__FUNCTION__);
return;
}
dbname = (char*)data->db; dbname = (char*)data->db;
if (is_drop_table_query(querybuf)) if (is_drop_table_query(querybuf))
@ -1647,9 +1662,23 @@ static skygw_query_type_t is_read_tmp_table(
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES]; rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
master_dcb = router_cli_ses->rses_master_ref->bref_dcb; master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
if(master_dcb == NULL)
{
skygw_log_write(LE,"[%s] Error: Master server DBC is NULL. "
"This means that the connection to the master server is already "
"closed while a query is still being routed.",__FUNCTION__);
return qtype;
}
CHK_DCB(master_dcb); CHK_DCB(master_dcb);
data = (MYSQL_session*)master_dcb->session->data; data = (MYSQL_session*)master_dcb->session->data;
if(data == NULL)
{
skygw_log_write(LE,"[%s] Error: User data in master server DBC is NULL.",__FUNCTION__);
return qtype;
}
dbname = (char*)data->db; dbname = (char*)data->db;
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) ||
@ -1724,9 +1753,24 @@ static void check_create_tmp_table(
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES]; rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
master_dcb = router_cli_ses->rses_master_ref->bref_dcb; master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
if(master_dcb == NULL)
{
skygw_log_write(LE,"[%s] Error: Master server DBC is NULL. "
"This means that the connection to the master server is already "
"closed while a query is still being routed.",__FUNCTION__);
return;
}
CHK_DCB(master_dcb); CHK_DCB(master_dcb);
data = (MYSQL_session*)master_dcb->session->data; data = (MYSQL_session*)master_dcb->session->data;
if(data == NULL)
{
skygw_log_write(LE,"[%s] Error: User data in master server DBC is NULL.",__FUNCTION__);
return;
}
dbname = (char*)data->db; dbname = (char*)data->db;
@ -2110,10 +2154,15 @@ static bool route_single_stmt(
/** /**
* Check if the query has anything to do with temporary tables. * Check if the query has anything to do with temporary tables.
*/ */
if (!rses_begin_locked_router_action(rses))
{
succp = false;
goto retblock;
}
qtype = is_read_tmp_table(rses, querybuf, qtype); qtype = is_read_tmp_table(rses, querybuf, qtype);
check_create_tmp_table(rses, querybuf, qtype); check_create_tmp_table(rses, querybuf, qtype);
check_drop_tmp_table(rses, querybuf,qtype); check_drop_tmp_table(rses, querybuf,qtype);
rses_end_locked_router_action(rses);
/** /**
* 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
@ -2561,7 +2610,10 @@ static bool rses_begin_locked_router_action(
ROUTER_CLIENT_SES* rses) ROUTER_CLIENT_SES* rses)
{ {
bool succp = false; bool succp = false;
if(rses == NULL)
return false;
CHK_CLIENT_RSES(rses); CHK_CLIENT_RSES(rses);
if (rses->rses_closed) { if (rses->rses_closed) {
@ -2617,6 +2669,7 @@ ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
int i = 0; int i = 0;
BACKEND *backend; BACKEND *backend;
char *weightby; char *weightby;
double master_pct = 0.0;
spinlock_acquire(&router->lock); spinlock_acquire(&router->lock);
router_cli_ses = router->connections; router_cli_ses = router->connections;
@ -2626,7 +2679,12 @@ char *weightby;
router_cli_ses = router_cli_ses->next; router_cli_ses = router_cli_ses->next;
} }
spinlock_release(&router->lock); spinlock_release(&router->lock);
if(router->stats.n_master + router->stats.n_slave > 0)
{
master_pct = (double)router->stats.n_master/(double)(router->stats.n_master + router->stats.n_slave);
}
dcb_printf(dcb, dcb_printf(dcb,
"\tNumber of router sessions: %d\n", "\tNumber of router sessions: %d\n",
router->stats.n_sessions); router->stats.n_sessions);
@ -2645,6 +2703,10 @@ char *weightby;
dcb_printf(dcb, dcb_printf(dcb,
"\tNumber of queries forwarded to all: %d\n", "\tNumber of queries forwarded to all: %d\n",
router->stats.n_all); router->stats.n_all);
dcb_printf(dcb,
"\tMaster/Slave percentage: %.2f%%\n",
master_pct * 100.0);
if ((weightby = serviceGetWeightingParameter(router->service)) != NULL) if ((weightby = serviceGetWeightingParameter(router->service)) != NULL)
{ {
dcb_printf(dcb, dcb_printf(dcb,

View File

@ -35,6 +35,8 @@
#include <modutil.h> #include <modutil.h>
#include <mysql_client_server_protocol.h> #include <mysql_client_server_protocol.h>
#define DEFAULT_REFRESH_INTERVAL 30.0
MODULE_INFO info = { MODULE_INFO info = {
MODULE_API_ROUTER, MODULE_API_ROUTER,
MODULE_BETA_RELEASE, MODULE_BETA_RELEASE,
@ -206,6 +208,8 @@ static void handle_error_reply_client(
static SPINLOCK instlock; static SPINLOCK instlock;
static ROUTER_INSTANCE* instances; static ROUTER_INSTANCE* instances;
bool detect_show_shards(GWBUF* query);
int process_show_shards(ROUTER_CLIENT_SES* rses);
static int hashkeyfun(void* key); static int hashkeyfun(void* key);
static int hashcmpfun (void *, void *); static int hashcmpfun (void *, void *);
@ -399,7 +403,13 @@ int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
GWBUF *buffer,*clone; GWBUF *buffer,*clone;
int i,rval = 0; int i,rval = 0;
unsigned int len; unsigned int len;
for(i = 0;i<session->rses_nbackends;i++)
{
session->rses_backend_ref[i].bref_mapped = false;
session->rses_backend_ref[i].n_mapping_eof = 0;
}
session->init |= INIT_MAPPING; session->init |= INIT_MAPPING;
session->init &= ~INIT_UNINT; session->init &= ~INIT_UNINT;
len = strlen(query) + 1; len = strlen(query) + 1;
@ -623,7 +633,7 @@ char** tokenize_string(char* str)
*/ */
int internalRoute(DCB* dcb) int internalRoute(DCB* dcb)
{ {
if(dcb->dcb_readqueue) if(dcb->dcb_readqueue && dcb->session)
{ {
GWBUF* tmp = dcb->dcb_readqueue; GWBUF* tmp = dcb->dcb_readqueue;
void* rinst = dcb->session->service->router_instance; void* rinst = dcb->session->service->router_instance;
@ -642,7 +652,7 @@ int internalRoute(DCB* dcb)
*/ */
int internalReply(DCB* dcb) int internalReply(DCB* dcb)
{ {
if(dcb->dcb_readqueue) if(dcb->dcb_readqueue && dcb->session)
{ {
GWBUF* tmp = dcb->dcb_readqueue; GWBUF* tmp = dcb->dcb_readqueue;
dcb->dcb_readqueue = NULL; dcb->dcb_readqueue = NULL;
@ -712,6 +722,9 @@ createInstance(SERVICE *service, char **options)
} }
router->service = service; router->service = service;
router->schemarouter_config.max_sescmd_hist = 0; router->schemarouter_config.max_sescmd_hist = 0;
router->schemarouter_config.last_refresh = time(NULL);
router->schemarouter_config.refresh_databases = false;
router->schemarouter_config.refresh_min_interval = DEFAULT_REFRESH_INTERVAL;
router->stats.longest_sescmd = 0; router->stats.longest_sescmd = 0;
router->stats.n_hist_exceeded = 0; router->stats.n_hist_exceeded = 0;
router->stats.n_queries = 0; router->stats.n_queries = 0;
@ -753,6 +766,18 @@ createInstance(SERVICE *service, char **options)
{ {
router->schemarouter_config.disable_sescmd_hist = config_truth_value(value); router->schemarouter_config.disable_sescmd_hist = config_truth_value(value);
} }
else if(strcmp(options[i],"refresh_databases") == 0)
{
router->schemarouter_config.refresh_databases = config_truth_value(value);
}
else if(strcmp(options[i],"refresh_interval") == 0)
{
router->schemarouter_config.refresh_min_interval = atof(value);
}
else if(strcmp(options[i],"debug") == 0)
{
router->schemarouter_config.debug = config_truth_value(value);
}
else else
{ {
skygw_log_write(LOGFILE_ERROR,"Error: Unknown router options for Schemarouter: %s",options[i]); skygw_log_write(LOGFILE_ERROR,"Error: Unknown router options for Schemarouter: %s",options[i]);
@ -935,6 +960,7 @@ static void* newSession(
client_rses->dcb_route->func.read = internalRoute; client_rses->dcb_route->func.read = internalRoute;
client_rses->dcb_route->state = DCB_STATE_POLLING; client_rses->dcb_route->state = DCB_STATE_POLLING;
client_rses->dcb_route->session = session; client_rses->dcb_route->session = session;
client_rses->rses_config.last_refresh = time(NULL);
client_rses->init = INIT_UNINT; client_rses->init = INIT_UNINT;
if(using_db) if(using_db)
client_rses->init |= INIT_USE_DB; client_rses->init |= INIT_USE_DB;
@ -1931,6 +1957,13 @@ static int routeQuery(
querybuf = gwbuf_make_contiguous(querybuf); querybuf = gwbuf_make_contiguous(querybuf);
} }
if(detect_show_shards(querybuf))
{
process_show_shards(router_cli_ses);
ret = 1;
goto retblock;
}
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 */
@ -2014,20 +2047,52 @@ static int routeQuery(
router_cli_ses->dbhash, router_cli_ses->dbhash,
querybuf))) querybuf)))
{ {
time_t now = time(NULL);
if(router_cli_ses->rses_config.refresh_databases &&
difftime(now,router_cli_ses->rses_config.last_refresh) >
router_cli_ses->rses_config.refresh_min_interval)
{
rses_begin_locked_router_action(router_cli_ses);
router_cli_ses->rses_config.last_refresh = now;
router_cli_ses->queue = querybuf;
hashtable_free(router_cli_ses->dbhash);
if((router_cli_ses->dbhash = hashtable_alloc(100, hashkeyfun, hashcmpfun)) == NULL)
{
skygw_log_write(LE,"Error: Hashtable allocation failed.");
rses_end_locked_router_action(router_cli_ses);
return 1;
}
hashtable_memory_fns(router_cli_ses->dbhash,(HASHMEMORYFN)strdup,
(HASHMEMORYFN)strdup,
(HASHMEMORYFN)free,
(HASHMEMORYFN)free);
gen_databaselist(inst,router_cli_ses);
rses_end_locked_router_action(router_cli_ses);
return 1;
}
extract_database(querybuf,db); extract_database(querybuf,db);
snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db); snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db);
for(i = 0;i<router_cli_ses->rses_nbackends;i++) if(router_cli_ses->rses_config.debug)
{ {
if(SERVER_IS_RUNNING(router_cli_ses->rses_backend_ref[i].bref_backend->backend_server)) sprintf(errbuf + strlen(errbuf)," ([%lu]: DB change failed)",router_cli_ses->rses_client_dcb->session->ses_id);
{
create_error_reply(errbuf,router_cli_ses->rses_backend_ref[i].bref_dcb);
break;
}
} }
GWBUF* error = modutil_create_mysql_err_msg(1, 0, 1049, "42000", errbuf);
if (error == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Creating buffer for error message failed.")));
return 0;
}
/** Set flags that help router to identify session commands reply */
router_cli_ses->rses_client_dcb->func.write(router_cli_ses->rses_client_dcb,error);
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,
"Error : Changing database failed."))); "Error : Changing database failed.")));
ret = 1;
goto retblock;
} }
} }
@ -2561,7 +2626,12 @@ static void clientReply (
router_cli_ses->connect_db); router_cli_ses->connect_db);
char errmsg[128 + MYSQL_DATABASE_MAXLEN+1]; char errmsg[128 + MYSQL_DATABASE_MAXLEN+1];
sprintf(errmsg,"Unknown database '%s'",router_cli_ses->connect_db); sprintf(errmsg,"Unknown database '%s'",router_cli_ses->connect_db);
if(router_cli_ses->rses_config.debug)
{
sprintf(errmsg + strlen(errmsg)," ([%lu]: DB not found on connect)",router_cli_ses->rses_client_dcb->session->ses_id);
}
GWBUF* errbuff = modutil_create_mysql_err_msg(1,0,1049,"42000",errmsg); GWBUF* errbuff = modutil_create_mysql_err_msg(1,0,1049,"42000",errmsg);
router_cli_ses->rses_client_dcb->func.write(router_cli_ses->rses_client_dcb,errbuff); router_cli_ses->rses_client_dcb->func.write(router_cli_ses->rses_client_dcb,errbuff);
if(router_cli_ses->queue) if(router_cli_ses->queue)
{ {
@ -4255,30 +4325,7 @@ static bool handle_error_new_connection(
succp = false; succp = false;
goto return_succp; goto return_succp;
} }
rses->init |= INIT_MAPPING;
for(i = 0;i<rses->rses_nbackends;i++)
{
bref_clear_state(&rses->rses_backend_ref[i],BREF_DB_MAPPED);
rses->rses_backend_ref[i].n_mapping_eof = 0;
}
HASHITERATOR* iter = hashtable_iterator(rses->dbhash);
char* srvnm = bref->bref_backend->backend_server->unique_name;
char *key, *value;
while((key = (char*)hashtable_next(iter)))
{
value = hashtable_fetch(rses->dbhash,key);
if(strcmp(value,srvnm) == 0)
{
hashtable_delete(rses->dbhash,key);
}
}
skygw_log_write(LOGFILE_TRACE,"schemarouter: Re-mapping databases");
gen_databaselist(rses->router,rses);
hashtable_iterator_free(iter);
return_succp: return_succp:
return succp; return succp;
} }
@ -4393,4 +4440,89 @@ static sescmd_cursor_t* backend_ref_get_sescmd_cursor (
return scur; return scur;
} }
/**
* Detect if a query contains a SHOW SHARDS query.
* @param query Query to inspect
* @return true if the query is a SHOW SHARDS query otherwise false
*/
bool detect_show_shards(GWBUF* query)
{
bool rval = false;
char *querystr,*tok,*sptr;
if(query == NULL)
{
skygw_log_write(LE,"Fatal Error: NULL value passed at %s:%d",__FILE__,__LINE__);
return false;
}
if (!modutil_is_SQL(query) && !modutil_is_SQL_prepare(query))
{
return false;
}
if((querystr = modutil_get_SQL(query)) == NULL)
{
skygw_log_write(LE,"Fatal Error: failure to parse SQL at %s:%d",__FILE__,__LINE__);
return false;
}
tok = strtok_r(querystr," ",&sptr);
if(tok && strcasecmp(tok,"show") == 0)
{
tok = strtok_r(NULL," ",&sptr);
if(tok && strcasecmp(tok,"shards") == 0)
rval = true;
}
free(querystr);
return rval;
}
struct shard_list{
HASHITERATOR* iter;
ROUTER_CLIENT_SES* rses;
RESULTSET* rset;
};
/**
* Callback for the shard list result set creation
*/
RESULT_ROW* shard_list_cb(struct resultset* rset, void* data)
{
char *key,*value;
struct shard_list *sl = (struct shard_list*)data;
RESULT_ROW* rval = NULL;
if((key = hashtable_next(sl->iter)) &&
(value = hashtable_fetch(sl->rses->dbhash,key)))
{
if((rval = resultset_make_row(sl->rset)))
{
resultset_row_set(rval,0,key);
resultset_row_set(rval,1,value);
}
}
return rval;
}
/**
* Send a result set of all shards and their locations to the client.
* @param rses Router client session
* @return 0 on success, -1 on error
*/
int process_show_shards(ROUTER_CLIENT_SES* rses)
{
HASHITERATOR* iter = hashtable_iterator(rses->dbhash);
struct shard_list sl;
sl.iter = iter;
sl.rses = rses;
sl.rset = resultset_create(shard_list_cb,&sl);
resultset_add_column(sl.rset,"Database",MYSQL_DATABASE_MAXLEN,COL_TYPE_VARCHAR);
resultset_add_column(sl.rset,"Server",MYSQL_DATABASE_MAXLEN,COL_TYPE_VARCHAR);
resultset_stream_mysql(sl.rset,rses->rses_client_dcb);
resultset_free(sl.rset);
hashtable_iterator_free(iter);
return 0;
}