Refactor rewadwritesplit temporary table handling
The temporary table detection and handling now uses C++ containers to store the set of temporary tables. The detection also uses the new query classifier field info API to detect which tables and databases are targeted.
This commit is contained in:
@ -306,18 +306,23 @@ static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *router_inst, MXS_SESSION *sess
|
|||||||
{
|
{
|
||||||
ROUTER_INSTANCE* router = (ROUTER_INSTANCE*)router_inst;
|
ROUTER_INSTANCE* router = (ROUTER_INSTANCE*)router_inst;
|
||||||
ROUTER_CLIENT_SES* client_rses = new (std::nothrow) ROUTER_CLIENT_SES;
|
ROUTER_CLIENT_SES* client_rses = new (std::nothrow) ROUTER_CLIENT_SES;
|
||||||
|
rses_property_t* prop = rses_property_init(RSES_PROP_TYPE_TMPTABLES);
|
||||||
|
|
||||||
if (client_rses == NULL)
|
if (client_rses == NULL || prop == NULL)
|
||||||
{
|
{
|
||||||
|
delete client_rses;
|
||||||
|
delete prop;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prop->rses_prop_rsession = client_rses;
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
client_rses->rses_chk_top = CHK_NUM_ROUTER_SES;
|
client_rses->rses_chk_top = CHK_NUM_ROUTER_SES;
|
||||||
client_rses->rses_chk_tail = CHK_NUM_ROUTER_SES;
|
client_rses->rses_chk_tail = CHK_NUM_ROUTER_SES;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
client_rses->rses_properties[RSES_PROP_TYPE_SESCMD] = NULL;
|
client_rses->rses_properties[RSES_PROP_TYPE_SESCMD] = NULL;
|
||||||
client_rses->rses_properties[RSES_PROP_TYPE_TMPTABLES] = NULL;
|
client_rses->rses_properties[RSES_PROP_TYPE_TMPTABLES] = prop;
|
||||||
client_rses->rses_closed = false;
|
client_rses->rses_closed = false;
|
||||||
client_rses->router = router;
|
client_rses->router = router;
|
||||||
client_rses->client_dcb = session->client_dcb;
|
client_rses->client_dcb = session->client_dcb;
|
||||||
@ -1069,10 +1074,13 @@ rses_property_t *rses_property_init(rses_property_type_t prop_type)
|
|||||||
|
|
||||||
if (prop == NULL)
|
if (prop == NULL)
|
||||||
{
|
{
|
||||||
|
MXS_OOM();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
prop->rses_prop_type = prop_type;
|
prop->rses_prop_type = prop_type;
|
||||||
|
prop->rses_prop_next = NULL;
|
||||||
|
prop->rses_prop_refcount = 1;
|
||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
prop->rses_prop_chk_top = CHK_NUM_ROUTER_PROPERTY;
|
prop->rses_prop_chk_top = CHK_NUM_ROUTER_PROPERTY;
|
||||||
prop->rses_prop_chk_tail = CHK_NUM_ROUTER_PROPERTY;
|
prop->rses_prop_chk_tail = CHK_NUM_ROUTER_PROPERTY;
|
||||||
@ -1101,7 +1109,7 @@ void rses_property_done(rses_property_t *prop)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RSES_PROP_TYPE_TMPTABLES:
|
case RSES_PROP_TYPE_TMPTABLES:
|
||||||
hashtable_free(prop->rses_prop_data.temp_tables);
|
// Nothing to do
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1111,6 +1119,7 @@ void rses_property_done(rses_property_t *prop)
|
|||||||
ss_dassert(false);
|
ss_dassert(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete prop;
|
delete prop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
|
|
||||||
#include <maxscale/cppdefs.hh>
|
#include <maxscale/cppdefs.hh>
|
||||||
|
|
||||||
|
#include <tr1/unordered_set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <maxscale/dcb.h>
|
#include <maxscale/dcb.h>
|
||||||
#include <maxscale/hashtable.h>
|
#include <maxscale/hashtable.h>
|
||||||
#include <maxscale/router.h>
|
#include <maxscale/router.h>
|
||||||
@ -153,6 +156,8 @@ struct mysql_sescmd_t
|
|||||||
skygw_chk_t my_sescmd_chk_tail;
|
skygw_chk_t my_sescmd_chk_tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::tr1::unordered_set<std::string> TableSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property structure
|
* Property structure
|
||||||
*/
|
*/
|
||||||
@ -163,10 +168,10 @@ struct rses_property_t
|
|||||||
int rses_prop_refcount;
|
int rses_prop_refcount;
|
||||||
rses_property_type_t rses_prop_type;
|
rses_property_type_t rses_prop_type;
|
||||||
|
|
||||||
union rses_prop_data
|
struct rses_prop_data // TODO: Remove the properties and integrate them into the session object
|
||||||
{
|
{
|
||||||
mysql_sescmd_t sescmd;
|
mysql_sescmd_t sescmd;
|
||||||
HASHTABLE* temp_tables;
|
TableSet 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 */
|
||||||
skygw_chk_t rses_prop_chk_tail;
|
skygw_chk_t rses_prop_chk_tail;
|
||||||
|
@ -125,9 +125,7 @@ bool select_connect_backend_servers(backend_ref_t **p_master_ref,
|
|||||||
/*
|
/*
|
||||||
* The following are implemented in rwsplit_tmp_table_multi.c
|
* The following are implemented in rwsplit_tmp_table_multi.c
|
||||||
*/
|
*/
|
||||||
void check_drop_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
|
void check_drop_tmp_table(ROUTER_CLIENT_SES *router_cli_ses, GWBUF *querybuf);
|
||||||
GWBUF *querybuf,
|
|
||||||
uint32_t packet_type);
|
|
||||||
bool is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
|
bool is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
|
||||||
GWBUF *querybuf,
|
GWBUF *querybuf,
|
||||||
uint32_t type);
|
uint32_t type);
|
||||||
|
@ -990,7 +990,7 @@ handle_multi_temp_and_load(ROUTER_CLIENT_SES *rses, GWBUF *querybuf,
|
|||||||
*/
|
*/
|
||||||
if (rses->have_tmp_tables)
|
if (rses->have_tmp_tables)
|
||||||
{
|
{
|
||||||
check_drop_tmp_table(rses, querybuf, packet_type);
|
check_drop_tmp_table(rses, querybuf);
|
||||||
if (is_packet_a_query(packet_type) && is_read_tmp_table(rses, querybuf, *qtype))
|
if (is_packet_a_query(packet_type) && is_read_tmp_table(rses, querybuf, *qtype))
|
||||||
{
|
{
|
||||||
*qtype |= QUERY_TYPE_MASTER_READ;
|
*qtype |= QUERY_TYPE_MASTER_READ;
|
||||||
|
@ -44,52 +44,27 @@
|
|||||||
* @param querybuf GWBUF containing the query
|
* @param querybuf GWBUF containing the query
|
||||||
* @param type The type of the query resolved so far
|
* @param type The type of the query resolved so far
|
||||||
*/
|
*/
|
||||||
void check_drop_tmp_table(ROUTER_CLIENT_SES *router_cli_ses, GWBUF *querybuf,
|
void check_drop_tmp_table(ROUTER_CLIENT_SES *router_cli_ses, GWBUF *querybuf)
|
||||||
uint32_t packet_type)
|
|
||||||
{
|
{
|
||||||
if (packet_type != MYSQL_COM_QUERY && packet_type != MYSQL_COM_DROP_DB)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tsize = 0, klen = 0, i;
|
|
||||||
char **tbl = NULL;
|
|
||||||
char *hkey, *dbname;
|
|
||||||
MYSQL_session *my_data;
|
|
||||||
rses_property_t *rses_prop_tmp;
|
|
||||||
MYSQL_session *data = (MYSQL_session *)router_cli_ses->client_dcb->data;
|
|
||||||
|
|
||||||
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
|
||||||
dbname = (char *)data->db;
|
|
||||||
|
|
||||||
if (qc_is_drop_table_query(querybuf))
|
if (qc_is_drop_table_query(querybuf))
|
||||||
{
|
{
|
||||||
tbl = qc_get_table_names(querybuf, &tsize, false);
|
const QC_FIELD_INFO* info;
|
||||||
if (tbl != NULL)
|
size_t n_infos;
|
||||||
{
|
qc_get_field_info(querybuf, &info, &n_infos);
|
||||||
for (i = 0; i < tsize; i++)
|
|
||||||
{
|
|
||||||
/* Not clear why the next six lines are outside the if block */
|
|
||||||
klen = strlen(dbname) + strlen(tbl[i]) + 2;
|
|
||||||
hkey = (char*)MXS_CALLOC(klen, sizeof(char));
|
|
||||||
MXS_ABORT_IF_NULL(hkey);
|
|
||||||
strcpy(hkey, dbname);
|
|
||||||
strcat(hkey, ".");
|
|
||||||
strcat(hkey, tbl[i]);
|
|
||||||
|
|
||||||
if (rses_prop_tmp && rses_prop_tmp->rses_prop_data.temp_tables)
|
for (size_t i = 0; i < n_infos; i++)
|
||||||
{
|
{
|
||||||
if (hashtable_delete(rses_prop_tmp->rses_prop_data.temp_tables,
|
MYSQL_session* data = static_cast<MYSQL_session*>(router_cli_ses->client_dcb->data);
|
||||||
(void *)hkey))
|
std::string table = info[i].database ? info[i].database : data->db;
|
||||||
|
table += ".";
|
||||||
|
|
||||||
|
if (info[i].table)
|
||||||
{
|
{
|
||||||
MXS_INFO("Temporary table dropped: %s", hkey);
|
table += info[i].table;
|
||||||
}
|
|
||||||
}
|
|
||||||
MXS_FREE(tbl[i]);
|
|
||||||
MXS_FREE(hkey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MXS_FREE(tbl);
|
rses_property_t* prop = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
||||||
|
prop->rses_prop_data.temp_tables.erase(table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,76 +80,41 @@ bool is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
|
|||||||
GWBUF *querybuf,
|
GWBUF *querybuf,
|
||||||
uint32_t qtype)
|
uint32_t qtype)
|
||||||
{
|
{
|
||||||
|
ss_dassert(router_cli_ses && querybuf && router_cli_ses->client_dcb);
|
||||||
bool target_tmp_table = false;
|
|
||||||
int tsize = 0, klen = 0, i;
|
|
||||||
char **tbl = NULL;
|
|
||||||
char *dbname;
|
|
||||||
char hkey[MYSQL_DATABASE_MAXLEN + MYSQL_TABLE_MAXLEN + 2];
|
|
||||||
MYSQL_session *data;
|
|
||||||
bool rval = false;
|
bool rval = false;
|
||||||
rses_property_t *rses_prop_tmp;
|
|
||||||
|
|
||||||
if (router_cli_ses == NULL || querybuf == NULL)
|
if (qtype & (QUERY_TYPE_READ |
|
||||||
|
QUERY_TYPE_LOCAL_READ |
|
||||||
|
QUERY_TYPE_USERVAR_READ |
|
||||||
|
QUERY_TYPE_SYSVAR_READ |
|
||||||
|
QUERY_TYPE_GSYSVAR_READ))
|
||||||
{
|
{
|
||||||
MXS_ERROR("[%s] Error: NULL parameters passed: %p %p", __FUNCTION__,
|
const QC_FIELD_INFO* info;
|
||||||
router_cli_ses, querybuf);
|
size_t n_infos;
|
||||||
return false;
|
qc_get_field_info(querybuf, &info, &n_infos);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n_infos; i++)
|
||||||
|
{
|
||||||
|
MYSQL_session* data = static_cast<MYSQL_session*>(router_cli_ses->client_dcb->data);
|
||||||
|
std::string table = info[i].database ? info[i].database : data->db;
|
||||||
|
table += ".";
|
||||||
|
|
||||||
|
if (info[i].table)
|
||||||
|
{
|
||||||
|
table += info[i].table;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (router_cli_ses->client_dcb == NULL)
|
rses_property_t* prop = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
||||||
{
|
|
||||||
MXS_ERROR("[%s] Error: Client DCB is NULL.", __FUNCTION__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
if (prop->rses_prop_data.temp_tables.find(table) !=
|
||||||
data = (MYSQL_session *)router_cli_ses->client_dcb->data;
|
prop->rses_prop_data.temp_tables.end())
|
||||||
|
|
||||||
if (data == NULL)
|
|
||||||
{
|
{
|
||||||
MXS_ERROR("[%s] Error: User data in client DBC is NULL.", __FUNCTION__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbname = (char *)data->db;
|
|
||||||
|
|
||||||
if (qc_query_is_type(qtype, QUERY_TYPE_READ) ||
|
|
||||||
qc_query_is_type(qtype, QUERY_TYPE_LOCAL_READ) ||
|
|
||||||
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
|
|
||||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) ||
|
|
||||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ))
|
|
||||||
{
|
|
||||||
tbl = qc_get_table_names(querybuf, &tsize, false);
|
|
||||||
|
|
||||||
if (tbl != NULL && tsize > 0)
|
|
||||||
{
|
|
||||||
/** Query targets at least one table */
|
|
||||||
for (i = 0; i < tsize && !target_tmp_table && tbl[i]; i++)
|
|
||||||
{
|
|
||||||
sprintf(hkey, "%s.%s", dbname, tbl[i]);
|
|
||||||
if (rses_prop_tmp && rses_prop_tmp->rses_prop_data.temp_tables)
|
|
||||||
{
|
|
||||||
if (hashtable_fetch(rses_prop_tmp->rses_prop_data.temp_tables, hkey))
|
|
||||||
{
|
|
||||||
/**Query target is a temporary table*/
|
|
||||||
rval = true;
|
rval = true;
|
||||||
MXS_INFO("Query targets a temporary table: %s", hkey);
|
MXS_INFO("Query targets a temporary table: %s", table.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tbl != NULL)
|
|
||||||
{
|
|
||||||
for (i = 0; i < tsize; i++)
|
|
||||||
{
|
|
||||||
MXS_FREE(tbl[i]);
|
|
||||||
}
|
|
||||||
MXS_FREE(tbl);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -191,112 +131,29 @@ bool is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
|
|||||||
void check_create_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
|
void check_create_tmp_table(ROUTER_CLIENT_SES *router_cli_ses,
|
||||||
GWBUF *querybuf, uint32_t type)
|
GWBUF *querybuf, uint32_t type)
|
||||||
{
|
{
|
||||||
if (!qc_query_is_type(type, QUERY_TYPE_CREATE_TMP_TABLE))
|
if (qc_query_is_type(type, QUERY_TYPE_CREATE_TMP_TABLE))
|
||||||
{
|
{
|
||||||
return;
|
ss_dassert(router_cli_ses && querybuf && router_cli_ses->client_dcb &&
|
||||||
}
|
router_cli_ses->client_dcb->data);
|
||||||
|
|
||||||
int klen = 0;
|
|
||||||
char *hkey, *dbname;
|
|
||||||
MYSQL_session *data;
|
|
||||||
rses_property_t *rses_prop_tmp;
|
|
||||||
HASHTABLE *h;
|
|
||||||
|
|
||||||
if (router_cli_ses == NULL || querybuf == NULL)
|
|
||||||
{
|
|
||||||
MXS_ERROR("[%s] Error: NULL parameters passed: %p %p", __FUNCTION__,
|
|
||||||
router_cli_ses, querybuf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (router_cli_ses->client_dcb == NULL)
|
|
||||||
{
|
|
||||||
MXS_ERROR("[%s] Error: Client DCB is NULL.", __FUNCTION__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
router_cli_ses->have_tmp_tables = true;
|
router_cli_ses->have_tmp_tables = true;
|
||||||
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
char* tblname = qc_get_created_table_name(querybuf);
|
||||||
data = (MYSQL_session *)router_cli_ses->client_dcb->data;
|
std::string table;
|
||||||
|
|
||||||
if (data == NULL)
|
if (tblname && *tblname)
|
||||||
{
|
{
|
||||||
MXS_ERROR("[%s] Error: User data in master server DBC is NULL.",
|
MYSQL_session* data = static_cast<MYSQL_session*>(router_cli_ses->client_dcb->data);
|
||||||
__FUNCTION__);
|
table += data->db;
|
||||||
return;
|
table += ".";
|
||||||
|
table += tblname;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbname = (char *)data->db;
|
/** Add the table to the set of temporary tables */
|
||||||
|
rses_property_t* prop = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
|
||||||
|
prop->rses_prop_data.temp_tables.insert(table);
|
||||||
|
|
||||||
bool is_temp = true;
|
|
||||||
char *tblname = NULL;
|
|
||||||
|
|
||||||
tblname = qc_get_created_table_name(querybuf);
|
|
||||||
|
|
||||||
if (tblname && strlen(tblname) > 0)
|
|
||||||
{
|
|
||||||
klen = strlen(dbname) + strlen(tblname) + 2;
|
|
||||||
hkey = (char*)MXS_CALLOC(klen, sizeof(char));
|
|
||||||
MXS_ABORT_IF_NULL(hkey);
|
|
||||||
strcpy(hkey, dbname);
|
|
||||||
strcat(hkey, ".");
|
|
||||||
strcat(hkey, tblname);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hkey = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rses_prop_tmp == NULL)
|
|
||||||
{
|
|
||||||
if ((rses_prop_tmp = (rses_property_t *)MXS_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)
|
|
||||||
{
|
|
||||||
if (rses_prop_tmp->rses_prop_data.temp_tables == NULL)
|
|
||||||
{
|
|
||||||
h = hashtable_alloc(7, rwsplit_hashkeyfun, rwsplit_hashcmpfun);
|
|
||||||
hashtable_memory_fns(h, rwsplit_hstrdup, NULL, rwsplit_hfree, NULL);
|
|
||||||
if (h != NULL)
|
|
||||||
{
|
|
||||||
rses_prop_tmp->rses_prop_data.temp_tables = h;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MXS_ERROR("Failed to allocate a new hashtable.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hkey && rses_prop_tmp->rses_prop_data.temp_tables &&
|
|
||||||
hashtable_add(rses_prop_tmp->rses_prop_data.temp_tables, (void *)hkey,
|
|
||||||
(void *)is_temp) == 0) /*< Conflict in hash table */
|
|
||||||
{
|
|
||||||
MXS_INFO("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)
|
|
||||||
{
|
|
||||||
MXS_INFO("Temporary table added: %s", hkey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
MXS_FREE(hkey);
|
|
||||||
MXS_FREE(tblname);
|
MXS_FREE(tblname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user