Merged some of the rabbitmq branch changes

query_classifier.cc: updated skygw_get_table_names to allow for partial or full table names
readwritesplit.c: transferred temporary table detection to separate functions
This commit is contained in:
Markus Makela
2014-09-04 13:31:20 +03:00
parent 5e529a8296
commit 4e11ea9b06
3 changed files with 355 additions and 264 deletions

View File

@ -890,26 +890,61 @@ char* skygw_query_classifier_get_stmtname(
} }
/**
*Returns the LEX struct of the parsed GWBUF
*@param The parsed GWBUF
*@return Pointer to the LEX struct or NULL if an error occurred or the query was not parsed
*/
LEX* get_lex(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;
}
return thd->lex;
}
/** /**
* Finds the head of the list of tables affected by the current select statement. * Finds the head of the list of tables affected by the current select statement.
* @param thd Pointer to a valid THD * @param thd Pointer to a valid THD
* @return Pointer to the head of the TABLE_LIST chain or NULL in case of an error * @return Pointer to the head of the TABLE_LIST chain or NULL in case of an error
*/ */
void* skygw_get_affected_tables(void* thdp) void* skygw_get_affected_tables(void* lexptr)
{ {
THD* thd = (THD*)thdp; LEX* lex = (LEX*)lexptr;
if(thd == NULL || if(lex == NULL ||
thd->lex == NULL || lex->current_select == NULL)
thd->lex->current_select == NULL)
{ {
ss_dassert(thd != NULL && ss_dassert(lex != NULL &&
thd->lex != NULL && lex->current_select != NULL);
thd->lex->current_select != NULL);
return NULL; return NULL;
} }
return (void*)thd->lex->current_select->table_list.first; return (void*)lex->current_select->table_list.first;
} }
@ -922,45 +957,25 @@ void* skygw_get_affected_tables(void* thdp)
* @param tblsize Pointer where the number of tables is written * @param tblsize Pointer where the number of tables is written
* @return Array of null-terminated strings with the table names * @return Array of null-terminated strings with the table names
*/ */
char** skygw_get_table_names(GWBUF* querybuf,int* tblsize) char** skygw_get_table_names(GWBUF* querybuf,int* tblsize, bool fullnames)
{ {
parsing_info_t* pi; LEX* lex;
MYSQL* mysql; TABLE_LIST* tbl;
THD* thd; int i = 0,
TABLE_LIST* tbl; currtblsz = 0;
int i = 0, currtblsz = 0; char **tables,
char**tables,**tmp; **tmp;
if (!GWBUF_IS_PARSED(querybuf)) if((lex = get_lex(querybuf)) == NULL)
{ {
tables = NULL;
goto retblock;
}
pi = (parsing_info_t *)gwbuf_get_buffer_object_data(querybuf,
GWBUF_PARSING_INFO);
if (pi == NULL)
{
tables = NULL;
goto retblock; goto retblock;
} }
if (pi->pi_query_plain_str == NULL || lex->current_select = lex->all_selects_list;
(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(lex->current_select){
while(thd->lex->current_select){ tbl = (TABLE_LIST*)skygw_get_affected_tables(lex);
tbl = (TABLE_LIST*)skygw_get_affected_tables(thd);
while (tbl) while (tbl)
{ {
@ -982,51 +997,57 @@ char** skygw_get_table_names(GWBUF* querybuf,int* tblsize)
} }
} }
tables[i++] = strdup(tbl->alias);
char *catnm = NULL;
if(fullnames)
{
if(tbl->db && strcmp(tbl->db,"skygw_virtual") != 0)
{
catnm = (char*)calloc(strlen(tbl->db) + strlen(tbl->table_name) + 2,sizeof(char));
strcpy(catnm,tbl->db);
strcat(catnm,".");
strcat(catnm,tbl->table_name);
}
}
if(catnm)
{
tables[i++] = catnm;
}
else
{
tables[i++] = strdup(tbl->table_name);
}
tbl=tbl->next_local; tbl=tbl->next_local;
} }
thd->lex->current_select = thd->lex->current_select->next_select_in_list(); lex->current_select = lex->current_select->next_select_in_list();
} }
retblock: retblock:
*tblsize = i; *tblsize = i;
return tables; return tables;
} }
/** /**
* Extract the name of the created table. * Extract, allocate memory and copy the name of the created table.
* @param querybuf Buffer to use. * @param querybuf Buffer to use.
* @return A pointer to the name if a table was created, otherwise NULL * @return A pointer to the name if a table was created, otherwise NULL
*/ */
char* skygw_get_created_table_name(GWBUF* querybuf) char* skygw_get_created_table_name(GWBUF* querybuf)
{ {
parsing_info_t* pi; LEX* lex;
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) if((lex = get_lex(querybuf)) == NULL)
{ {
return NULL; return NULL;
} }
if ((mysql = (MYSQL *)pi->pi_handle) == NULL || if(lex->create_last_non_select_table &&
(thd = (THD *)mysql->thd) == NULL) lex->create_last_non_select_table->table_name){
{ char* name = strdup(lex->create_last_non_select_table->table_name);
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; return name;
}else{ }else{
return NULL; return NULL;
@ -1040,31 +1061,10 @@ char* skygw_get_created_table_name(GWBUF* querybuf)
*/ */
bool is_drop_table_query(GWBUF* querybuf) bool is_drop_table_query(GWBUF* querybuf)
{ {
parsing_info_t* pi; LEX* lex;
MYSQL* mysql;
THD* thd;
if (!GWBUF_IS_PARSED(querybuf)) return (lex = get_lex(querybuf)) != NULL &&
{ lex->sql_command == SQLCOM_DROP_TABLE;
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;
} }
/* /*

View File

@ -76,8 +76,9 @@ skygw_query_type_t query_classifier_get_type(GWBUF* querybuf);
char* skygw_query_classifier_get_stmtname(MYSQL* mysql); char* skygw_query_classifier_get_stmtname(MYSQL* mysql);
char* skygw_get_created_table_name(GWBUF* querybuf); char* skygw_get_created_table_name(GWBUF* querybuf);
bool is_drop_table_query(GWBUF* querybuf); bool is_drop_table_query(GWBUF* querybuf);
void* skygw_get_affected_tables(void* thdp); bool skygw_is_real_query(GWBUF* querybuf);
char** skygw_get_table_names(GWBUF* querybuf,int* tblsize); void* skygw_get_affected_tables(void* lexptr);
char** skygw_get_table_names(GWBUF* querybuf,int* tblsize,bool fullnames);
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 *));

View File

@ -1215,7 +1215,255 @@ static route_target_t get_route_target (
return target; return target;
} }
/**
* Check if the query is a DROP TABLE... query and
* if it targets a temporary table, remove it from the hashtable.
* @param instance Router instance
* @param router_session Router client session
* @param querybuf GWBUF containing the query
* @param type The type of the query resolved so far
*/
void check_drop_tmp_table(
ROUTER* instance,
void* router_session,
GWBUF* querybuf,
skygw_query_type_t type)
{
int tsize = 0, klen = 0,i;
char** tbl;
char *hkey,*dbname;
MYSQL_session* data;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
DCB* master_dcb = NULL;
rses_property_t* rses_prop_tmp;
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
CHK_DCB(master_dcb);
data = (MYSQL_session*)master_dcb->session->data;
dbname = (char*)data->db;
if (is_drop_table_query(querybuf))
{
tbl = skygw_get_table_names(querybuf,&tsize,false);
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);
}
}
/**
* Check if the query targets a temporary table.
* @param instance Router instance
* @param router_session Router client session
* @param querybuf GWBUF containing the query
* @param type The type of the query resolved so far
* @return The type of the query
*/
skygw_query_type_t is_read_tmp_table(
ROUTER* instance,
void* router_session,
GWBUF* querybuf,
skygw_query_type_t type)
{
bool target_tmp_table = false;
int tsize = 0, klen = 0,i;
char** tbl;
char *hkey,*dbname;
MYSQL_session* data;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
DCB* master_dcb = NULL;
skygw_query_type_t qtype = type;
rses_property_t* rses_prop_tmp;
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
CHK_DCB(master_dcb);
data = (MYSQL_session*)master_dcb->session->data;
dbname = (char*)data->db;
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ))
{
tbl = skygw_get_table_names(querybuf,&tsize,false);
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[i]);
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);
free(tbl[i]);
}
free(tbl);
}
}
return qtype;
}
/**
* 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.
*
* @param instance Router instance
* @param router_session Router client session
* @param querybuf GWBUF containing the query
* @param type The type of the query resolved so far
*/
void check_create_tmp_table(
ROUTER* instance,
void* router_session,
GWBUF* querybuf,
skygw_query_type_t type)
{
int klen = 0;
char *hkey,*dbname;
MYSQL_session* data;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
DCB* master_dcb = NULL;
rses_property_t* rses_prop_tmp;
HASHTABLE* h;
rses_prop_tmp = router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES];
master_dcb = router_cli_ses->rses_master_ref->bref_dcb;
CHK_DCB(master_dcb);
data = (MYSQL_session*)master_dcb->session->data;
dbname = (char*)data->db;
if (QUERY_IS_TYPE(type, 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);
free(tblname);
}
}
/** /**
* The main routing entry, this is called with every packet that is * The main routing entry, this is called with every packet that is
@ -1246,28 +1494,15 @@ static int routeQuery(
GWBUF* querybuf) GWBUF* querybuf)
{ {
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN; skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
GWBUF* plainsqlbuf = NULL;
char* querystr = NULL; char* querystr = NULL;
char* startpos;
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;
route_target_t route_target; route_target_t route_target;
bool succp = false; bool succp = false;
int rlag_max = MAX_RLAG_UNDEFINED; int rlag_max = MAX_RLAG_UNDEFINED;
@ -1285,7 +1520,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)
{ {
@ -1317,8 +1552,6 @@ 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 */
@ -1364,48 +1597,15 @@ static int routeQuery(
break; break;
} /**< switch by packet type */ } /**< switch by packet type */
/** /**
* Check if the query targets a temporary table * Check if the query has anything to do with temporary tables.
*/ */
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ))
{
tbl = skygw_get_table_names(querybuf,&tsize);
if (tsize > 0) qtype = is_read_tmp_table(instance,router_session,querybuf,qtype);
{ check_create_tmp_table(instance,router_session,querybuf,qtype);
/** Query targets at least one table */ check_drop_tmp_table(instance,router_session,querybuf,qtype);
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
@ -1443,117 +1643,7 @@ static int routeQuery(
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
@ -3774,8 +3864,8 @@ static void print_error_packet(
} }
} }
ss_dassert(srv != NULL); ss_dassert(srv != NULL);
char* str = (char*)&ptr[7];
bufstr = strndup(&ptr[7], len-3); bufstr = strndup(str, len-3);
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, LOGFILE_ERROR,