Added utility functions to skygw_utils and cleaned up tee filter.
This commit is contained in:
@ -179,13 +179,10 @@ typedef struct {
|
|||||||
int residual; /* Any outstanding SQL text */
|
int residual; /* Any outstanding SQL text */
|
||||||
GWBUF* tee_replybuf; /* Buffer for reply */
|
GWBUF* tee_replybuf; /* Buffer for reply */
|
||||||
GWBUF* tee_partials[2];
|
GWBUF* tee_partials[2];
|
||||||
GWBUF* querybuf;
|
GWBUF* queue;
|
||||||
SPINLOCK tee_lock;
|
SPINLOCK tee_lock;
|
||||||
DCB* client_dcb;
|
DCB* client_dcb;
|
||||||
int statements; /*< Number of statements in the query,
|
|
||||||
* used to identify and track multi-statement
|
|
||||||
* queries and that both the parent and the child
|
|
||||||
* branch are in sync. */
|
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
long d_id;
|
long d_id;
|
||||||
#endif
|
#endif
|
||||||
@ -209,30 +206,12 @@ static SPINLOCK orphanLock;
|
|||||||
static int packet_is_required(GWBUF *queue);
|
static int packet_is_required(GWBUF *queue);
|
||||||
static int detect_loops(TEE_INSTANCE *instance, HASHTABLE* ht, SERVICE* session);
|
static int detect_loops(TEE_INSTANCE *instance, HASHTABLE* ht, SERVICE* session);
|
||||||
int internal_route(DCB* dcb);
|
int internal_route(DCB* dcb);
|
||||||
|
GWBUF* clone_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer);
|
||||||
static int hkfn(
|
int route_single_query(TEE_INSTANCE* my_instance,
|
||||||
void* key)
|
TEE_SESSION* my_session,
|
||||||
{
|
GWBUF* buffer,
|
||||||
if(key == NULL){
|
GWBUF* clone);
|
||||||
return 0;
|
int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer);
|
||||||
}
|
|
||||||
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 hcfn(
|
|
||||||
void* v1,
|
|
||||||
void* v2)
|
|
||||||
{
|
|
||||||
char* i1 = (char*) v1;
|
|
||||||
char* i2 = (char*) v2;
|
|
||||||
|
|
||||||
return strcmp(i1,i2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
orphan_free(void* data)
|
orphan_free(void* data)
|
||||||
@ -492,7 +471,7 @@ char *remote, *userName;
|
|||||||
goto retblock;
|
goto retblock;
|
||||||
}
|
}
|
||||||
|
|
||||||
HASHTABLE* ht = hashtable_alloc(100,hkfn,hcfn);
|
HASHTABLE* ht = hashtable_alloc(100,simple_str_hash,strcmp);
|
||||||
bool is_loop = detect_loops(my_instance,ht,session->service);
|
bool is_loop = detect_loops(my_instance,ht,session->service);
|
||||||
hashtable_free(ht);
|
hashtable_free(ht);
|
||||||
|
|
||||||
@ -509,12 +488,11 @@ char *remote, *userName;
|
|||||||
{
|
{
|
||||||
my_session->active = 1;
|
my_session->active = 1;
|
||||||
my_session->residual = 0;
|
my_session->residual = 0;
|
||||||
my_session->statements = 0;
|
|
||||||
my_session->tee_replybuf = NULL;
|
my_session->tee_replybuf = NULL;
|
||||||
my_session->client_dcb = session->client;
|
my_session->client_dcb = session->client;
|
||||||
my_session->instance = my_instance;
|
my_session->instance = my_instance;
|
||||||
my_session->client_multistatement = false;
|
my_session->client_multistatement = false;
|
||||||
|
my_session->queue = NULL;
|
||||||
spinlock_init(&my_session->tee_lock);
|
spinlock_init(&my_session->tee_lock);
|
||||||
if (my_instance->source &&
|
if (my_instance->source &&
|
||||||
(remote = session_get_remote(session)) != NULL)
|
(remote = session_get_remote(session)) != NULL)
|
||||||
@ -812,155 +790,61 @@ setUpstream(FILTER *instance, void *session, UPSTREAM *upstream)
|
|||||||
static int
|
static int
|
||||||
routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||||
{
|
{
|
||||||
TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance;
|
TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance;
|
||||||
TEE_SESSION *my_session = (TEE_SESSION *)session;
|
TEE_SESSION *my_session = (TEE_SESSION *)session;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int length, rval, residual = 0;
|
int rval;
|
||||||
GWBUF *clone = NULL;
|
GWBUF *buffer = NULL, *clone = NULL;
|
||||||
unsigned char command = *((unsigned char*)queue->start + 4);
|
unsigned char command = gwbuf_length(queue) >= 5 ?
|
||||||
|
*((unsigned char*)queue->start + 4) : 1;
|
||||||
|
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
|
skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s",
|
||||||
atomic_add(&debug_seq,1),
|
atomic_add(&debug_seq,1),
|
||||||
((char*)queue->start + 5));
|
((char*)queue->start + 5));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
spinlock_acquire(&my_session->tee_lock);
|
spinlock_acquire(&my_session->tee_lock);
|
||||||
|
|
||||||
if(!my_session->active)
|
|
||||||
{
|
|
||||||
skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
|
|
||||||
gwbuf_free(queue);
|
|
||||||
rval = 0;
|
|
||||||
goto retblock;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (my_session->branch_session &&
|
|
||||||
my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
|
||||||
{
|
|
||||||
if (my_session->residual)
|
|
||||||
{
|
|
||||||
clone = gwbuf_clone_all(queue);
|
|
||||||
|
|
||||||
if (my_session->residual < GWBUF_LENGTH(clone))
|
|
||||||
{
|
|
||||||
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
|
|
||||||
}
|
|
||||||
my_session->residual -= GWBUF_LENGTH(clone);
|
|
||||||
|
|
||||||
if (my_session->residual < 0)
|
|
||||||
{
|
|
||||||
my_session->residual = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (my_session->active && (ptr = modutil_get_SQL(queue)) != NULL)
|
|
||||||
{
|
|
||||||
if ((my_instance->match == NULL ||
|
|
||||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
|
||||||
(my_instance->nomatch == NULL ||
|
|
||||||
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
|
|
||||||
{
|
|
||||||
length = modutil_MySQL_query_len(queue, &residual);
|
|
||||||
clone = gwbuf_clone_all(queue);
|
|
||||||
my_session->residual = residual;
|
|
||||||
}
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
else if (packet_is_required(queue))
|
|
||||||
{
|
|
||||||
clone = gwbuf_clone_all(queue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(!my_session->active)
|
||||||
|
{
|
||||||
|
skygw_log_write(LOGFILE_TRACE, "Tee: Received a reply when the session was closed.");
|
||||||
|
gwbuf_free(queue);
|
||||||
spinlock_release(&my_session->tee_lock);
|
spinlock_release(&my_session->tee_lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Pass the query downstream */
|
if(my_session->queue)
|
||||||
|
{
|
||||||
|
my_session->queue = gwbuf_append(my_session->queue,queue);
|
||||||
|
buffer = modutil_get_next_MySQL_packet(&my_session->queue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer = modutil_get_next_MySQL_packet(&queue);
|
||||||
|
my_session->queue = queue;
|
||||||
|
}
|
||||||
|
|
||||||
switch(command)
|
if(buffer == NULL)
|
||||||
{
|
{
|
||||||
case 0x1b:
|
spinlock_release(&my_session->tee_lock);
|
||||||
my_session->client_multistatement = *((unsigned char*) queue->start + 5);
|
return 1;
|
||||||
case 0x03:
|
}
|
||||||
case 0x16:
|
|
||||||
case 0x17:
|
clone = clone_query(my_instance, my_session,buffer);
|
||||||
case 0x04:
|
spinlock_release(&my_session->tee_lock);
|
||||||
case 0x0a:
|
|
||||||
memset(my_session->multipacket,(char)true,2*sizeof(bool));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
memset(my_session->multipacket,(char)false,2*sizeof(bool));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(my_session->replies,0,2*sizeof(int));
|
|
||||||
memset(my_session->reply_packets,0,2*sizeof(int));
|
|
||||||
memset(my_session->eof,0,2*sizeof(int));
|
|
||||||
memset(my_session->waiting,1,2*sizeof(bool));
|
|
||||||
my_session->statements = modutil_count_statements(queue);
|
|
||||||
my_session->command = command;
|
|
||||||
#ifdef SS_DEBUG
|
|
||||||
spinlock_acquire(&debug_lock);
|
|
||||||
my_session->d_id = ++debug_id;
|
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] command [%x]",
|
|
||||||
my_session->d_id,
|
|
||||||
my_session->command);
|
|
||||||
if(command == 0x03)
|
|
||||||
{
|
|
||||||
char* tmpstr = modutil_get_SQL(queue);
|
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c query: '%s'",
|
|
||||||
tmpstr);
|
|
||||||
free(tmpstr);
|
|
||||||
}
|
|
||||||
spinlock_release(&debug_lock);
|
|
||||||
#endif
|
|
||||||
spinlock_acquire(&my_session->tee_lock);
|
|
||||||
|
|
||||||
if(!my_session->active ||
|
/* Reset session state */
|
||||||
my_session->branch_session == NULL ||
|
if(!reset_session_state(my_session,buffer))
|
||||||
my_session->branch_session->state != SESSION_STATE_ROUTER_READY)
|
return 0;
|
||||||
{
|
|
||||||
rval = 0;
|
/** Route query downstream */
|
||||||
my_session->active = 0;
|
spinlock_acquire(&my_session->tee_lock);
|
||||||
goto retblock;
|
rval = route_single_query(my_instance,my_session,buffer,clone);
|
||||||
}
|
spinlock_release(&my_session->tee_lock);
|
||||||
rval = my_session->down.routeQuery(my_session->down.instance,
|
|
||||||
my_session->down.session,
|
return rval;
|
||||||
queue);
|
|
||||||
if (clone)
|
|
||||||
{
|
|
||||||
my_session->n_duped++;
|
|
||||||
|
|
||||||
if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
|
||||||
{
|
|
||||||
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/** Close tee session */
|
|
||||||
my_session->active = 0;
|
|
||||||
rval = 0;
|
|
||||||
LOGIF(LT, (skygw_log_write(
|
|
||||||
LOGFILE_TRACE,
|
|
||||||
"Closed tee filter session: Child session in invalid state.")));
|
|
||||||
gwbuf_free(clone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (my_session->active)
|
|
||||||
{
|
|
||||||
LOGIF(LT, (skygw_log_write(
|
|
||||||
LOGFILE_TRACE,
|
|
||||||
"Closed tee filter session: Child session is NULL.")));
|
|
||||||
my_session->active = 0;
|
|
||||||
rval = 0;
|
|
||||||
}
|
|
||||||
my_session->n_rejected++;
|
|
||||||
}
|
|
||||||
retblock:
|
|
||||||
spinlock_release(&my_session->tee_lock);
|
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int count_replies(GWBUF* buffer)
|
int count_replies(GWBUF* buffer)
|
||||||
@ -1043,41 +927,42 @@ uint16_t get_response_flags(uint8_t* datastart, bool ok_packet)
|
|||||||
static int
|
static int
|
||||||
clientReply (FILTER* instance, void *session, GWBUF *reply)
|
clientReply (FILTER* instance, void *session, GWBUF *reply)
|
||||||
{
|
{
|
||||||
int rc, branch, eof;
|
int rc, branch, eof;
|
||||||
TEE_SESSION *my_session = (TEE_SESSION *) session;
|
TEE_SESSION *my_session = (TEE_SESSION *) session;
|
||||||
bool route = false,mpkt;
|
bool route = false,mpkt;
|
||||||
GWBUF *complete = NULL;
|
GWBUF *complete = NULL;
|
||||||
unsigned char *ptr;
|
unsigned char *ptr;
|
||||||
uint16_t flags = 0;
|
uint16_t flags = 0;
|
||||||
int min_eof = my_session->command != 0x04 ? 2 : 1;
|
int min_eof = my_session->command != 0x04 ? 2 : 1;
|
||||||
int more_results = 0;
|
int more_results = 0;
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
ptr = (unsigned char*) reply->start;
|
ptr = (unsigned char*) reply->start;
|
||||||
skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d",
|
skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d",
|
||||||
instance ? "parent":"child",
|
instance ? "parent":"child",
|
||||||
my_session->active ? "open" : "closed",
|
my_session->active ? "open" : "closed",
|
||||||
PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET",
|
PTR_IS_ERR(ptr) ? "ERR" : PTR_IS_OK(ptr) ? "OK" : "RSET",
|
||||||
atomic_add(&debug_seq,1));
|
atomic_add(&debug_seq,1));
|
||||||
#endif
|
#endif
|
||||||
spinlock_acquire(&my_session->tee_lock);
|
|
||||||
|
|
||||||
if(!my_session->active)
|
spinlock_acquire(&my_session->tee_lock);
|
||||||
{
|
|
||||||
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
|
|
||||||
gwbuf_free(reply);
|
|
||||||
rc = 0;
|
|
||||||
if(my_session->waiting[PARENT])
|
|
||||||
{
|
|
||||||
GWBUF* errbuf = modutil_create_mysql_err_msg(1,0,1,"0000","Session closed.");
|
|
||||||
my_session->waiting[PARENT] = false;
|
|
||||||
my_session->up.clientReply (my_session->up.instance,
|
|
||||||
my_session->up.session,
|
|
||||||
errbuf);
|
|
||||||
}
|
|
||||||
goto retblock;
|
|
||||||
}
|
|
||||||
|
|
||||||
branch = instance == NULL ? CHILD : PARENT;
|
if(!my_session->active)
|
||||||
|
{
|
||||||
|
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
|
||||||
|
gwbuf_free(reply);
|
||||||
|
rc = 0;
|
||||||
|
if(my_session->waiting[PARENT])
|
||||||
|
{
|
||||||
|
GWBUF* errbuf = modutil_create_mysql_err_msg(1,0,1,"0000","Session closed.");
|
||||||
|
my_session->waiting[PARENT] = false;
|
||||||
|
my_session->up.clientReply (my_session->up.instance,
|
||||||
|
my_session->up.session,
|
||||||
|
errbuf);
|
||||||
|
}
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
branch = instance == NULL ? CHILD : PARENT;
|
||||||
|
|
||||||
my_session->tee_partials[branch] = gwbuf_append(my_session->tee_partials[branch], reply);
|
my_session->tee_partials[branch] = gwbuf_append(my_session->tee_partials[branch], reply);
|
||||||
my_session->tee_partials[branch] = gwbuf_make_contiguous(my_session->tee_partials[branch]);
|
my_session->tee_partials[branch] = gwbuf_make_contiguous(my_session->tee_partials[branch]);
|
||||||
@ -1106,131 +991,146 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
|
|||||||
if(my_session->replies[branch] == 0)
|
if(my_session->replies[branch] == 0)
|
||||||
{
|
{
|
||||||
skygw_log_write(LOGFILE_TRACE,"Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD");
|
skygw_log_write(LOGFILE_TRACE,"Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD");
|
||||||
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
|
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
|
||||||
* Otherwise the reply is a result set and the amount of packets is unknown.
|
* Otherwise the reply is a result set and the amount of packets is unknown.
|
||||||
*/
|
*/
|
||||||
if(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) ||
|
if(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr) ||
|
||||||
PTR_IS_OK(ptr) || !my_session->multipacket[branch] )
|
PTR_IS_OK(ptr) || !my_session->multipacket[branch] )
|
||||||
{
|
{
|
||||||
my_session->waiting[branch] = false;
|
my_session->waiting[branch] = false;
|
||||||
my_session->multipacket[branch] = false;
|
my_session->multipacket[branch] = false;
|
||||||
if(PTR_IS_OK(ptr))
|
if(PTR_IS_OK(ptr))
|
||||||
{
|
{
|
||||||
flags = get_response_flags(ptr,true);
|
flags = get_response_flags(ptr,true);
|
||||||
more_results = (flags & 0x08) && my_session->client_multistatement;
|
more_results = (flags & 0x08) && my_session->client_multistatement;
|
||||||
if(more_results)
|
if(more_results)
|
||||||
{
|
{
|
||||||
skygw_log_write(LOGFILE_TRACE,
|
skygw_log_write(LOGFILE_TRACE,
|
||||||
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
|
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.",
|
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.",
|
||||||
my_session->d_id,
|
my_session->d_id,
|
||||||
branch == PARENT?"parent":"child");
|
branch == PARENT?"parent":"child");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if(my_session->waiting[branch])
|
if(my_session->waiting[branch])
|
||||||
{
|
{
|
||||||
eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0,&more_results);
|
eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0,&more_results);
|
||||||
more_results &= my_session->client_multistatement;
|
more_results &= my_session->client_multistatement;
|
||||||
my_session->eof[branch] += eof;
|
my_session->eof[branch] += eof;
|
||||||
|
|
||||||
if(my_session->eof[branch] >= min_eof)
|
if(my_session->eof[branch] >= min_eof)
|
||||||
{
|
{
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] %s received last EOF packet",
|
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c [%d] %s received last EOF packet",
|
||||||
my_session->d_id,
|
my_session->d_id,
|
||||||
branch == PARENT?"parent":"child");
|
branch == PARENT?"parent":"child");
|
||||||
#endif
|
#endif
|
||||||
my_session->waiting[branch] = more_results;
|
my_session->waiting[branch] = more_results;
|
||||||
if(more_results)
|
if(more_results)
|
||||||
{
|
{
|
||||||
my_session->eof[branch] = 0;
|
my_session->eof[branch] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(branch == PARENT)
|
if(branch == PARENT)
|
||||||
{
|
{
|
||||||
my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf,complete);
|
my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf,complete);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(complete)
|
if(complete)
|
||||||
gwbuf_free(complete);
|
gwbuf_free(complete);
|
||||||
}
|
}
|
||||||
|
|
||||||
my_session->replies[branch]++;
|
|
||||||
rc = 1;
|
|
||||||
mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD];
|
|
||||||
|
|
||||||
if(my_session->tee_replybuf != NULL)
|
my_session->replies[branch]++;
|
||||||
|
rc = 1;
|
||||||
|
mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD];
|
||||||
|
|
||||||
|
if(my_session->tee_replybuf != NULL)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(my_session->branch_session == NULL)
|
if(my_session->branch_session == NULL)
|
||||||
{
|
{
|
||||||
rc = 0;
|
rc = 0;
|
||||||
gwbuf_free(my_session->tee_replybuf);
|
gwbuf_free(my_session->tee_replybuf);
|
||||||
my_session->tee_replybuf = NULL;
|
my_session->tee_replybuf = NULL;
|
||||||
skygw_log_write_flush(LOGFILE_ERROR,"Error : Tee child session was closed.");
|
skygw_log_write_flush(LOGFILE_ERROR,"Error : Tee child session was closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mpkt)
|
if(mpkt)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(my_session->waiting[PARENT])
|
if(my_session->waiting[PARENT])
|
||||||
{
|
{
|
||||||
route = true;
|
route = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if(my_session->eof[PARENT] >= min_eof &&
|
else if(my_session->eof[PARENT] >= min_eof &&
|
||||||
my_session->eof[CHILD] >= min_eof)
|
my_session->eof[CHILD] >= min_eof)
|
||||||
{
|
{
|
||||||
route = true;
|
route = true;
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing final packet of response set.",my_session->d_id);
|
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing final packet of response set.",my_session->d_id);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!my_session->waiting[PARENT] &&
|
else if(!my_session->waiting[PARENT] &&
|
||||||
!my_session->waiting[CHILD])
|
!my_session->waiting[CHILD])
|
||||||
{
|
{
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id);
|
skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id);
|
||||||
#endif
|
#endif
|
||||||
route = true;
|
route = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(route)
|
if(route)
|
||||||
{
|
{
|
||||||
#ifdef SS_DEBUG
|
#ifdef SS_DEBUG
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG, "tee.c:[%d] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])"
|
skygw_log_write_flush(LOGFILE_DEBUG, "tee.c:[%d] Routing buffer '%p' parent(waiting [%s] replies [%d] eof[%d])"
|
||||||
" child(waiting [%s] replies[%d] eof [%d])",
|
" child(waiting [%s] replies[%d] eof [%d])",
|
||||||
my_session->d_id,
|
my_session->d_id,
|
||||||
my_session->tee_replybuf,
|
my_session->tee_replybuf,
|
||||||
my_session->waiting[PARENT] ? "true":"false",
|
my_session->waiting[PARENT] ? "true":"false",
|
||||||
my_session->replies[PARENT],
|
my_session->replies[PARENT],
|
||||||
my_session->eof[PARENT],
|
my_session->eof[PARENT],
|
||||||
my_session->waiting[CHILD]?"true":"false",
|
my_session->waiting[CHILD]?"true":"false",
|
||||||
my_session->replies[CHILD],
|
my_session->replies[CHILD],
|
||||||
my_session->eof[CHILD]);
|
my_session->eof[CHILD]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rc = my_session->up.clientReply (my_session->up.instance,
|
rc = my_session->up.clientReply (my_session->up.instance,
|
||||||
my_session->up.session,
|
my_session->up.session,
|
||||||
my_session->tee_replybuf);
|
my_session->tee_replybuf);
|
||||||
my_session->tee_replybuf = NULL;
|
my_session->tee_replybuf = NULL;
|
||||||
}
|
}
|
||||||
retblock:
|
|
||||||
spinlock_release(&my_session->tee_lock);
|
if(my_session->queue &&
|
||||||
return rc;
|
!my_session->waiting[PARENT] &&
|
||||||
|
!my_session->waiting[CHILD])
|
||||||
|
{
|
||||||
|
GWBUF* buffer = modutil_get_next_MySQL_packet(&my_session->queue);
|
||||||
|
GWBUF* clone = clone_query(my_session->instance,my_session,buffer);
|
||||||
|
reset_session_state(my_session,buffer);
|
||||||
|
route_single_query(my_session->instance,my_session,buffer,clone);
|
||||||
|
LOGIF(LT,(skygw_log_write(LT,"tee: routing queued query")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
retblock:
|
||||||
|
|
||||||
|
spinlock_release(&my_session->tee_lock);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1352,4 +1252,152 @@ int internal_route(DCB* dcb)
|
|||||||
TEE_SESSION* session = dcb->data;
|
TEE_SESSION* session = dcb->data;
|
||||||
|
|
||||||
return routeQuery((FILTER*)session->instance,session,buffer);
|
return routeQuery((FILTER*)session->instance,session,buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param my_instance
|
||||||
|
* @param my_session
|
||||||
|
* @param buffer
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
GWBUF* clone_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer)
|
||||||
|
{
|
||||||
|
GWBUF* clone = NULL;
|
||||||
|
int length, residual;
|
||||||
|
char* ptr;
|
||||||
|
|
||||||
|
if (my_session->branch_session &&
|
||||||
|
my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||||
|
{
|
||||||
|
if (my_session->residual)
|
||||||
|
{
|
||||||
|
clone = gwbuf_clone_all(buffer);
|
||||||
|
|
||||||
|
if (my_session->residual < GWBUF_LENGTH(clone))
|
||||||
|
{
|
||||||
|
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
|
||||||
|
}
|
||||||
|
my_session->residual -= GWBUF_LENGTH(clone);
|
||||||
|
|
||||||
|
if (my_session->residual < 0)
|
||||||
|
{
|
||||||
|
my_session->residual = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (my_session->active && (ptr = modutil_get_SQL(buffer)) != NULL)
|
||||||
|
{
|
||||||
|
if ((my_instance->match == NULL ||
|
||||||
|
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
||||||
|
(my_instance->nomatch == NULL ||
|
||||||
|
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
|
||||||
|
{
|
||||||
|
length = modutil_MySQL_query_len(buffer, &residual);
|
||||||
|
clone = gwbuf_clone_all(buffer);
|
||||||
|
my_session->residual = residual;
|
||||||
|
}
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
else if (packet_is_required(buffer))
|
||||||
|
{
|
||||||
|
clone = gwbuf_clone_all(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route the main query downstream along the main filter chain and possibly route
|
||||||
|
* a clone of the buffer to the branch session. If the clone buffer is NULL, nothing
|
||||||
|
* is routed to the branch session.
|
||||||
|
* @param my_instance Tee instance
|
||||||
|
* @param my_session Tee session
|
||||||
|
* @param buffer Main buffer
|
||||||
|
* @param clone Cloned buffer
|
||||||
|
* @return 1 on success, 0 on failure.
|
||||||
|
*/
|
||||||
|
int route_single_query(TEE_INSTANCE* my_instance, TEE_SESSION* my_session, GWBUF* buffer, GWBUF* clone)
|
||||||
|
{
|
||||||
|
int rval = 0;
|
||||||
|
if(!my_session->active ||
|
||||||
|
my_session->branch_session == NULL ||
|
||||||
|
my_session->branch_session->state != SESSION_STATE_ROUTER_READY)
|
||||||
|
{
|
||||||
|
rval = 0;
|
||||||
|
my_session->active = 0;
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
rval = my_session->down.routeQuery(my_session->down.instance,
|
||||||
|
my_session->down.session,
|
||||||
|
buffer);
|
||||||
|
if (clone)
|
||||||
|
{
|
||||||
|
my_session->n_duped++;
|
||||||
|
|
||||||
|
if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||||
|
{
|
||||||
|
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/** Close tee session */
|
||||||
|
my_session->active = 0;
|
||||||
|
rval = 0;
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Closed tee filter session: Child session in invalid state.")));
|
||||||
|
gwbuf_free(clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (my_session->active)
|
||||||
|
{
|
||||||
|
LOGIF(LT, (skygw_log_write(
|
||||||
|
LOGFILE_TRACE,
|
||||||
|
"Closed tee filter session: Child session is NULL.")));
|
||||||
|
my_session->active = 0;
|
||||||
|
rval = 0;
|
||||||
|
}
|
||||||
|
my_session->n_rejected++;
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the session's internal counters.
|
||||||
|
* @param my_session Tee session
|
||||||
|
* @param buffer Buffer with the query of the main branch in it
|
||||||
|
* @return 1 on success, 0 on error
|
||||||
|
*/
|
||||||
|
int reset_session_state(TEE_SESSION* my_session, GWBUF* buffer)
|
||||||
|
{
|
||||||
|
if(gwbuf_length(buffer) < 5)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned char command = *((unsigned char*)buffer->start + 4);
|
||||||
|
|
||||||
|
switch(command)
|
||||||
|
{
|
||||||
|
case 0x1b:
|
||||||
|
my_session->client_multistatement = *((unsigned char*) buffer->start + 5);
|
||||||
|
case 0x03:
|
||||||
|
case 0x16:
|
||||||
|
case 0x17:
|
||||||
|
case 0x04:
|
||||||
|
case 0x0a:
|
||||||
|
memset(my_session->multipacket,(char)true,2*sizeof(bool));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
memset(my_session->multipacket,(char)false,2*sizeof(bool));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(my_session->replies,0,2*sizeof(int));
|
||||||
|
memset(my_session->reply_packets,0,2*sizeof(int));
|
||||||
|
memset(my_session->eof,0,2*sizeof(int));
|
||||||
|
memset(my_session->waiting,1,2*sizeof(bool));
|
||||||
|
my_session->command = command;
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1045,6 +1045,59 @@ void slcursor_add_data(
|
|||||||
CHK_SLIST_CURSOR(c);
|
CHK_SLIST_CURSOR(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the current node in the slist. This does not delete the data in the
|
||||||
|
* node but will delete the structure pointing to that data. This is useful when
|
||||||
|
* the user wants to free the allocated memory. After node removal, the cursor
|
||||||
|
* will point to the node before the removed node.
|
||||||
|
* @param c Cursor pointing to the data node to be removed
|
||||||
|
*/
|
||||||
|
void slcursor_remove_data(slist_cursor_t* c)
|
||||||
|
{
|
||||||
|
slist_node_t* node = c->slcursor_pos;
|
||||||
|
int havemore = slist_size(c);
|
||||||
|
slcursor_move_to_begin (c);
|
||||||
|
|
||||||
|
if(node == c->slcursor_pos)
|
||||||
|
{
|
||||||
|
c->slcursor_list->slist_head = c->slcursor_list->slist_head->slnode_next;
|
||||||
|
slcursor_move_to_begin (c);
|
||||||
|
atomic_add((int*)&node->slnode_list->slist_nelems,-1);
|
||||||
|
atomic_add((int*)&node->slnode_cursor_refcount,-1);
|
||||||
|
if(node->slnode_cursor_refcount == 0)
|
||||||
|
{
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(havemore)
|
||||||
|
{
|
||||||
|
if( c->slcursor_pos->slnode_next == node)
|
||||||
|
{
|
||||||
|
c->slcursor_pos->slnode_next = node->slnode_next;
|
||||||
|
atomic_add((int*)&node->slnode_list->slist_nelems,-1);
|
||||||
|
atomic_add((int*)&node->slnode_cursor_refcount,-1);
|
||||||
|
if(node->slnode_cursor_refcount == 0)
|
||||||
|
{
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
havemore = slcursor_step_ahead (c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size of the slist.
|
||||||
|
* @param c slist cursor which refers to a list
|
||||||
|
* @return nummber of elements in the list
|
||||||
|
*/
|
||||||
|
size_t slist_size(slist_cursor_t* c)
|
||||||
|
{
|
||||||
|
return c->slcursor_list->slist_nelems;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void slist_done(
|
void slist_done(
|
||||||
slist_cursor_t* c)
|
slist_cursor_t* c)
|
||||||
@ -2176,3 +2229,22 @@ strip_escape_chars (char* val)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a hash value for a null-terminated string.
|
||||||
|
* @param key String to hash
|
||||||
|
* @return Hash value of the string
|
||||||
|
*/
|
||||||
|
int simple_str_hash(char* key)
|
||||||
|
{
|
||||||
|
if(key == NULL){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int hash = 0,c = 0;
|
||||||
|
char* ptr = key;
|
||||||
|
while((c = *ptr++)){
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|||||||
@ -84,8 +84,9 @@ EXTERN_C_BLOCK_BEGIN
|
|||||||
|
|
||||||
slist_cursor_t* slist_init(void);
|
slist_cursor_t* slist_init(void);
|
||||||
void slist_done(slist_cursor_t* c);
|
void slist_done(slist_cursor_t* c);
|
||||||
|
size_t slist_size(slist_cursor_t* c);
|
||||||
void slcursor_add_data(slist_cursor_t* c, void* data);
|
void slcursor_add_data(slist_cursor_t* c, void* data);
|
||||||
|
void slcursor_remove_data(slist_cursor_t* c);
|
||||||
void* slcursor_get_data(slist_cursor_t* c);
|
void* slcursor_get_data(slist_cursor_t* c);
|
||||||
|
|
||||||
bool slcursor_move_to_begin(slist_cursor_t* c);
|
bool slcursor_move_to_begin(slist_cursor_t* c);
|
||||||
@ -202,6 +203,9 @@ char* replace_literal(char* haystack,
|
|||||||
const char* replacement);
|
const char* replacement);
|
||||||
bool is_valid_posix_path(char* path);
|
bool is_valid_posix_path(char* path);
|
||||||
bool strip_escape_chars(char*);
|
bool strip_escape_chars(char*);
|
||||||
|
int simple_str_hash(char* key);
|
||||||
|
|
||||||
|
|
||||||
EXTERN_C_BLOCK_END
|
EXTERN_C_BLOCK_END
|
||||||
|
|
||||||
#endif /* SKYGW_UTILS_H */
|
#endif /* SKYGW_UTILS_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user