Merge branch 'SESvars' of https://github.com/skysql/MaxScale into SESvars

Conflicts:
	server/core/dcb.c
	server/core/poll.c
	server/modules/include/mysql_client_server_protocol.h
	server/modules/routing/readwritesplit/readwritesplit.c
This commit is contained in:
VilhoRaatikka 2014-03-18 10:28:06 +02:00
commit c356b396a6
19 changed files with 1667 additions and 403 deletions

View File

@ -81,6 +81,7 @@ SHARED_BUF *sbuf;
sbuf->refcount = 1;
rval->sbuf = sbuf;
rval->next = NULL;
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
rval->command = 0;
CHK_GWBUF(rval);
return rval;
@ -127,11 +128,93 @@ GWBUF *rval;
rval->sbuf = buf->sbuf;
rval->start = buf->start;
rval->end = buf->end;
rval->gwbuf_type = buf->gwbuf_type;
rval->next = NULL;
rval->command = buf->command;
CHK_GWBUF(rval);
return rval;
}
GWBUF *gwbuf_clone_portion(
GWBUF *buf,
size_t start_offset,
size_t length)
{
GWBUF* clonebuf;
CHK_GWBUF(buf);
ss_dassert(start_offset+length <= GWBUF_LENGTH(buf));
if ((clonebuf = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
{
return NULL;
}
atomic_add(&buf->sbuf->refcount, 1);
clonebuf->sbuf = buf->sbuf;
clonebuf->start = (void *)((char*)buf->start)+start_offset;
clonebuf->end = (void *)((char *)clonebuf->start)+length;
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */
clonebuf->next = NULL;
CHK_GWBUF(clonebuf);
return clonebuf;
}
/**
* Returns pointer to GWBUF of a requested type.
* As of 10.3.14 only MySQL to plain text conversion is supported.
* Return NULL if conversion between types is not supported or due lacking
* type information.
*/
GWBUF *gwbuf_clone_transform(
GWBUF * head,
gwbuf_type_t targettype)
{
gwbuf_type_t src_type;
GWBUF* clonebuf;
CHK_GWBUF(head);
src_type = head->gwbuf_type;
if (targettype == GWBUF_TYPE_UNDEFINED ||
src_type == GWBUF_TYPE_UNDEFINED ||
src_type == GWBUF_TYPE_PLAINSQL ||
targettype == src_type)
{
clonebuf = NULL;
goto return_clonebuf;
}
switch (src_type)
{
case GWBUF_TYPE_MYSQL:
if (targettype == GWBUF_TYPE_PLAINSQL)
{
/** Crete reference to string part of buffer */
clonebuf = gwbuf_clone_portion(
head,
5,
GWBUF_LENGTH(head)-5);
ss_dassert(clonebuf != NULL);
/** Overwrite the type with new format */
clonebuf->gwbuf_type = targettype;
}
else
{
clonebuf = NULL;
}
break;
default:
clonebuf = NULL;
break;
} /*< switch (src_type) */
return_clonebuf:
return clonebuf;
}
/**
* Append a buffer onto a linked list of buffer structures.
*
@ -150,6 +233,7 @@ GWBUF *ptr = head;
if (!head)
return tail;
CHK_GWBUF(head);
CHK_GWBUF(tail);
while (ptr->next)
{
ptr = ptr->next;
@ -178,9 +262,10 @@ GWBUF *
gwbuf_consume(GWBUF *head, unsigned int length)
{
GWBUF *rval = head;
CHK_GWBUF(head);
GWBUF_CONSUME(head, length);
CHK_GWBUF(head);
if (GWBUF_EMPTY(head))
{
rval = head->next;
@ -207,3 +292,28 @@ int rval = 0;
}
return rval;
}
bool gwbuf_set_type(
GWBUF* buf,
gwbuf_type_t type)
{
bool succp;
CHK_GWBUF(buf);
switch (type) {
case GWBUF_TYPE_MYSQL:
case GWBUF_TYPE_PLAINSQL:
case GWBUF_TYPE_UNDEFINED:
buf->gwbuf_type = type;
succp = true;
break;
default:
succp = false;
break;
}
ss_dassert(succp);
return succp;
}

View File

@ -303,12 +303,8 @@ dcb_final_free(DCB *dcb)
if (dcb->remote)
free(dcb->remote);
bitmask_free(&dcb->memdata.bitmask);
#if 1
simple_mutex_done(&dcb->dcb_read_lock);
simple_mutex_done(&dcb->dcb_write_lock);
#endif
free(dcb);
}
@ -527,6 +523,8 @@ int rc;
* Successfully connected to backend. Assign file descriptor to dcb
*/
dcb->fd = fd;
/** Copy status field to DCB */
dcb->dcb_server_status = server->status;
/*<
* backend_dcb is connected to backend server, and once backend_dcb
@ -690,10 +688,18 @@ dcb_write(DCB *dcb, GWBUF *queue)
dcb->state != DCB_STATE_LISTENING &&
dcb->state != DCB_STATE_NOPOLLING))
{
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Write aborted to dcb %p because "
"it is in state %s",
pthread_self(),
dcb->stats.n_buffered,
dcb,
STRDCBSTATE(dcb->state),
dcb->fd)));
return 0;
}
spinlock_acquire(&dcb->writeqlock);
if (dcb->writeq != NULL)
@ -750,7 +756,11 @@ dcb_write(DCB *dcb, GWBUF *queue)
#endif /* SS_DEBUG */
len = GWBUF_LENGTH(queue);
GW_NOINTR_CALL(
w = gw_write(dcb->fd, GWBUF_DATA(queue), len);
w = gw_write(
#if defined(SS_DEBUG)
dcb,
#endif
dcb->fd, GWBUF_DATA(queue), len);
dcb->stats.n_writes++;
);
@ -759,6 +769,8 @@ dcb_write(DCB *dcb, GWBUF *queue)
saved_errno = errno;
errno = 0;
if (LOG_IS_ENABLED(LOGFILE_DEBUG))
{
if (saved_errno == EPIPE) {
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
@ -771,7 +783,12 @@ dcb_write(DCB *dcb, GWBUF *queue)
dcb->fd,
saved_errno,
strerror(saved_errno))));
} else if (saved_errno != EAGAIN &&
}
}
if (LOG_IS_ENABLED(LOGFILE_ERROR))
{
if (saved_errno != EPIPE &&
saved_errno != EAGAIN &&
saved_errno != EWOULDBLOCK)
{
LOGIF(LE, (skygw_log_write_flush(
@ -785,6 +802,7 @@ dcb_write(DCB *dcb, GWBUF *queue)
saved_errno,
strerror(saved_errno))));
}
}
break;
}
/*
@ -799,9 +817,9 @@ dcb_write(DCB *dcb, GWBUF *queue)
pthread_self(),
w,
dcb,
STRDCBSTATE(dcb->state),
STRDCBSTATE(dcb->state),
dcb->fd)));
}
} /*< while (queue != NULL) */
/*<
* What wasn't successfully written is stored to write queue
* for suspended write.
@ -819,7 +837,6 @@ dcb_write(DCB *dcb, GWBUF *queue)
saved_errno != EAGAIN &&
saved_errno != EWOULDBLOCK)
{
queue = gwbuf_consume(queue, gwbuf_length(queue));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Writing to %s socket failed due %d, %s.",
@ -862,9 +879,13 @@ int saved_errno = 0;
while (dcb->writeq != NULL)
{
len = GWBUF_LENGTH(dcb->writeq);
GW_NOINTR_CALL(w = gw_write(dcb->fd,
GWBUF_DATA(dcb->writeq),
len););
GW_NOINTR_CALL(w = gw_write(
#if defined(SS_DEBUG)
dcb,
#endif
dcb->fd,
GWBUF_DATA(dcb->writeq),
len););
saved_errno = errno;
errno = 0;
@ -1319,12 +1340,15 @@ static bool dcb_set_state_nomutex(
}
int gw_write(
#if defined(SS_DEBUG)
DCB* dcb,
#endif
int fd,
const void* buf,
size_t nbytes)
{
int w;
#if defined(SS_DEBUG)
#if defined(SS_DEBUG)
if (dcb_fake_write_errno[fd] != 0) {
ss_dassert(dcb_fake_write_ev[fd] != 0);
w = write(fd, buf, nbytes/2); /*< leave peer to read missing bytes */
@ -1339,6 +1363,57 @@ int gw_write(
#else
w = write(fd, buf, nbytes);
#endif /* SS_DEBUG && SS_TEST */
#if defined(SS_DEBUG_MYSQL)
{
size_t len;
uint8_t* packet = (uint8_t *)buf;
char* str;
/** Print only MySQL packets */
if (w > 5)
{
str = (char *)&packet[5];
len = packet[0];
len += 256*packet[1];
len += 256*256*packet[2];
if (strncmp(str, "insert", 6) == 0 ||
strncmp(str, "create", 6) == 0 ||
strncmp(str, "drop", 4) == 0)
{
ss_dassert((dcb->dcb_server_status & (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE))==(SERVER_RUNNING|SERVER_MASTER));
}
if (strncmp(str, "set autocommit", 14) == 0 && nbytes > 17)
{
char* s = (char *)calloc(1, nbytes+1);
if (nbytes-5 > len)
{
size_t len2 = packet[4+len];
len2 += 256*packet[4+len+1];
len2 += 256*256*packet[4+len+2];
char* str2 = (char *)&packet[4+len+5];
snprintf(s, 5+len+len2, "long %s %s", (char *)str, (char *)str2);
}
else
{
snprintf(s, len, "%s", (char *)str);
}
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"%lu [gw_write] Wrote %d bytes : %s ",
pthread_self(),
w,
s)));
free(s);
}
}
}
#endif
return w;
}

View File

@ -363,7 +363,6 @@ poll_waitevents(void *arg)
eno = gw_getsockerrno(dcb->fd);
if (eno == 0) {
#if 1
simple_mutex_lock(
&dcb->dcb_write_lock,
true);
@ -371,16 +370,13 @@ poll_waitevents(void *arg)
!dcb->dcb_write_active,
"Write already active");
dcb->dcb_write_active = TRUE;
#endif
atomic_add(
&pollStats.n_write,
1);
dcb->func.write_ready(dcb);
#if 1
dcb->dcb_write_active = FALSE;
simple_mutex_unlock(
&dcb->dcb_write_lock);
#endif
} else {
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
@ -396,13 +392,12 @@ poll_waitevents(void *arg)
}
if (ev & EPOLLIN)
{
#if 1
simple_mutex_lock(&dcb->dcb_read_lock,
true);
ss_info_dassert(!dcb->dcb_read_active,
"Read already active");
dcb->dcb_read_active = TRUE;
#endif
if (dcb->state == DCB_STATE_LISTENING)
{
LOGIF(LD, (skygw_log_write(
@ -427,11 +422,9 @@ poll_waitevents(void *arg)
atomic_add(&pollStats.n_read, 1);
dcb->func.read(dcb);
}
#if 1
dcb->dcb_read_active = FALSE;
simple_mutex_unlock(
&dcb->dcb_read_lock);
#endif
}
if (ev & EPOLLERR)
{
@ -462,6 +455,7 @@ poll_waitevents(void *arg)
atomic_add(&pollStats.n_error, 1);
dcb->func.error(dcb);
}
if (ev & EPOLLHUP)
{
int eno = 0;

View File

@ -41,9 +41,18 @@
*
* @endverbatim
*/
#include <skygw_debug.h>
typedef enum
{
GWBUF_TYPE_UNDEFINED = 0x0,
GWBUF_TYPE_PLAINSQL = 0x1,
GWBUF_TYPE_MYSQL = 0x2
} gwbuf_type_t;
/**
* A structure to encapsualte the data in a form that the data itself can be
* A structure to encapsulate the data in a form that the data itself can be
* shared between multiple GWBUF's without the need to make multiple copies
* but still maintain separate data pointers.
*/
@ -64,8 +73,9 @@ typedef struct gwbuf {
struct gwbuf *next; /*< Next buffer in a linked chain of buffers */
void *start; /*< Start of the valid data */
void *end; /*< First byte after the valid data */
SHARED_BUF *sbuf; /*< The shared buffer with the real data */
SHARED_BUF *sbuf; /*< The shared buffer with the real data */
int command;/*< The command type for the queue */
gwbuf_type_t gwbuf_type; /*< buffer's data type information */
} GWBUF;
/*<
@ -83,6 +93,7 @@ typedef struct gwbuf {
/*< Consume a number of bytes in the buffer */
#define GWBUF_CONSUME(b, bytes) (b)->start += bytes
#define GWBUF_TYPE(b) (b)->gwbuf_type
/*<
* Function prototypes for the API to maniplate the buffers
*/
@ -92,6 +103,7 @@ extern GWBUF *gwbuf_clone(GWBUF *buf);
extern GWBUF *gwbuf_append(GWBUF *head, GWBUF *tail);
extern GWBUF *gwbuf_consume(GWBUF *head, unsigned int length);
extern unsigned int gwbuf_length(GWBUF *head);
extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len);
extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type);
extern bool gwbuf_set_type(GWBUF *head, gwbuf_type_t type);
#endif

View File

@ -179,7 +179,7 @@ typedef struct dcb {
SPINLOCK authlock; /**< Generic Authorization spinlock */
DCBSTATS stats; /**< DCB related statistics */
unsigned int dcb_server_status; /*< the server role indicator from SERVER */
struct dcb *next; /**< Next DCB in the chain of allocated DCB's */
struct service *service; /**< The related service */
void *data; /**< Specific client data */
@ -205,7 +205,13 @@ int fail_accept_errno;
#define DCB_ISZOMBIE(x) ((x)->state == DCB_STATE_ZOMBIE)
DCB *dcb_get_zombies(void);
int gw_write(int fd, const void* buf, size_t nbytes);
int gw_write(
#if defined(SS_DEBUG)
DCB* dcb,
#endif
int fd,
const void* buf,
size_t nbytes);
int dcb_write(DCB *, GWBUF *);
DCB *dcb_alloc(dcb_role_t);
void dcb_free(DCB *);

View File

@ -54,6 +54,12 @@ int do_read_dcb(DCB *dcb);
int do_read_10(DCB *dcb, uint8_t *buffer);
int MySQLWrite(DCB *dcb, GWBUF *queue);
int setnonblocking(int fd);
int gw_write(int fd, const void* buf, size_t nbytes);
int gw_write(
#if defined(SS_DEBUG)
DCB* dcb,
#endif
int fd,
const void* buf,
size_t nbytes);
int gw_getsockerrno(int fd);
int parse_bindconfig(char *, unsigned short, struct sockaddr_in *);

View File

@ -35,6 +35,7 @@
#include <service.h>
#include <session.h>
#include <buffer.h>
#include <stdint.h>
/**
* The ROUTER handle points to module specific data, so the best we can do
@ -74,10 +75,13 @@ typedef struct router_object {
void (*diagnostics)(ROUTER *instance, DCB *dcb);
void (*clientReply)(ROUTER* instance, void* router_session, GWBUF* queue, DCB *backend_dcb);
void (*errorReply)(ROUTER* instance, void* router_session, char* message, DCB *backend_dcb, int action);
uint8_t (*getCapabilities)(ROUTER *instance, void* router_session);
} ROUTER_OBJECT;
/* Router commands */
#define ROUTER_DEFAULT 0 /**< Standard routing */
#define ROUTER_CHANGE_SESSION 1 /**< Route a change session */
typedef enum router_capability_t {
RCAP_TYPE_UNDEFINED = 0,
RCAP_TYPE_STMT_INPUT = (1 << 0),
RCAP_TYPE_PACKET_INPUT = (1 << 1)
} router_capability_t;
#endif

View File

@ -53,6 +53,8 @@ typedef struct spinlock {
#define SPINLOCK_INIT { 0 }
#endif
#define SPINLOCK_IS_LOCKED(l) ((l)->lock != 0 ? true : false)
extern void spinlock_init(SPINLOCK *lock);
extern void spinlock_acquire(SPINLOCK *lock);
extern int spinlock_acquire_nowait(SPINLOCK *lock);

View File

@ -297,6 +297,7 @@ void gw_str_xor(
const uint8_t *input1,
const uint8_t *input2,
unsigned int len);
<<<<<<< HEAD
char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len);
int gw_hex2bin(uint8_t *out, const char *in, unsigned int len);
int gw_generate_random_str(char *output, int len);
@ -304,3 +305,13 @@ char *gw_strend(register const char *s);
int setnonblocking(int fd);
int setipaddress(struct in_addr *a, char *p);
int gw_read_gwbuff(DCB *dcb, GWBUF **head, int b);
=======
char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len);
int gw_hex2bin(uint8_t *out, const char *in, unsigned int len);
int gw_generate_random_str(char *output, int len);
char *gw_strend(register const char *s);
int setnonblocking(int fd);
void setipaddress(struct in_addr *a, char *p);
int gw_read_gwbuff(DCB *dcb, GWBUF **head, int b);
GWBUF* gw_MySQL_get_next_stmt(GWBUF** p_readbuf);
>>>>>>> 67d9b3afb94559f13b44780c9c54760b667a068a

View File

@ -53,8 +53,8 @@ typedef struct router_client_session {
bool rses_closed; /*< true when closeSession is called */
BACKEND *backend; /*< Backend used by the client session */
DCB *backend_dcb; /*< DCB Connection to the backend */
struct router_client_session
*next;
struct router_client_session *next;
int rses_capabilities; /*< input type, for example */
#if defined(SS_DEBUG)
skygw_chk_t rses_chk_tail;
#endif

View File

@ -24,7 +24,7 @@
* @verbatim
* Revision History
*
* bazaar..
* See GitHub https://github.com/skysql/MaxScale
*
* @endverbatim
*/
@ -41,25 +41,91 @@ typedef struct backend {
int backend_conn_count; /*< Number of connections to the server */
} BACKEND;
typedef struct rses_property_st rses_property_t;
typedef struct router_client_session ROUTER_CLIENT_SES;
typedef enum rses_property_type_t {
RSES_PROP_TYPE_UNDEFINED=0,
RSES_PROP_TYPE_SESCMD,
RSES_PROP_TYPE_FIRST = RSES_PROP_TYPE_SESCMD,
RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_SESCMD,
RSES_PROP_TYPE_COUNT=RSES_PROP_TYPE_LAST+1
} rses_property_type_t;
typedef enum backend_type_t {
BE_UNDEFINED=-1,
BE_MASTER,
BE_SLAVE,
BE_COUNT
} backend_type_t;
/**
* Session variable command
*/
typedef struct mysql_sescmd_st {
#if defined(SS_DEBUG)
skygw_chk_t my_sescmd_chk_top;
#endif
rses_property_t* my_sescmd_prop; /*< parent property */
GWBUF* my_sescmd_buf; /*< query buffer */
unsigned char my_sescmd_packet_type;/*< packet type */
bool my_sescmd_is_replied; /*< is cmd replied to client */
#if defined(SS_DEBUG)
skygw_chk_t my_sescmd_chk_tail;
#endif
} mysql_sescmd_t;
/**
* Property structure
*/
struct rses_property_st {
#if defined(SS_DEBUG)
skygw_chk_t rses_prop_chk_top;
#endif
ROUTER_CLIENT_SES* rses_prop_rsession; /*< parent router session */
int rses_prop_refcount;
rses_property_type_t rses_prop_type;
union rses_prop_data {
mysql_sescmd_t sescmd;
void* placeholder; /*< to be removed due new type */
} rses_prop_data;
rses_property_t* rses_prop_next; /*< next property of same type */
#if defined(SS_DEBUG)
skygw_chk_t rses_prop_chk_tail;
#endif
};
typedef struct sescmd_cursor_st {
ROUTER_CLIENT_SES* scmd_cur_rses; /*< pointer to owning router session */
rses_property_t** scmd_cur_ptr_property; /*< address of pointer to owner property */
mysql_sescmd_t* scmd_cur_cmd; /*< pointer to current session command */
bool scmd_cur_active; /*< true if command is being executed */
backend_type_t scmd_cur_be_type; /*< BE_MASTER or BE_SLAVE */
} sescmd_cursor_t;
/**
* The client session structure used within this router.
*/
typedef struct router_client_session {
struct router_client_session {
#if defined(SS_DEBUG)
skygw_chk_t rses_chk_top;
skygw_chk_t rses_chk_top;
#endif
SPINLOCK rses_lock; /*< protects rses_deleted */
int rses_versno; /*< even = no active update, else odd */
bool rses_closed; /*< true when closeSession is called */
BACKEND* be_slave; /*< Slave backend used by client session */
BACKEND* be_master; /*< Master backend used by client session */
DCB* slave_dcb; /*< Slave connection */
DCB* master_dcb; /*< Master connection */
SPINLOCK rses_lock; /*< protects rses_deleted */
int rses_versno; /*< even = no active update, else odd */
bool rses_closed; /*< true when closeSession is called */
/** Properties listed by their type */
rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT];
BACKEND* rses_backend[BE_COUNT];/*< Backends used by client session */
DCB* rses_dcb[BE_COUNT];
/*< cursor is pointer and status variable to current session command */
sescmd_cursor_t rses_cursor[BE_COUNT];
int rses_capabilities; /*< input type, for example */
struct router_client_session* next;
#if defined(SS_DEBUG)
skygw_chk_t rses_chk_tail;
skygw_chk_t rses_chk_tail;
#endif
} ROUTER_CLIENT_SES;
};
/**
* The statistics for this router instance
@ -88,5 +154,4 @@ typedef struct router_instance {
struct router_instance* next; /*< Next router on the list */
} ROUTER_INSTANCE;
#endif
#endif /*< _RWSPLITROUTER_H */

View File

@ -283,6 +283,8 @@ static int gw_read_backend_event(DCB *dcb) {
}
if (backend_protocol->state == MYSQL_AUTH_FAILED) {
spinlock_acquire(&dcb->delayqlock);
/*<
* vraa : errorHandle
* check the delayq before the reply
@ -295,10 +297,12 @@ static int gw_read_backend_event(DCB *dcb) {
0,
"Connection to backend lost.");
// consume all the delay queue
dcb->delayq = gwbuf_consume(
while ((dcb->delayq = gwbuf_consume(
dcb->delayq,
gwbuf_length(dcb->delayq));
GWBUF_LENGTH(dcb->delayq))) != NULL);
}
spinlock_release(&dcb->delayqlock);
/* try reload users' table for next connection */
service_refresh_users(dcb->session->client->service);
@ -350,7 +354,7 @@ static int gw_read_backend_event(DCB *dcb) {
pthread_self(),
dcb->fd,
current_session->user)));
/* check the delay queue and flush the data */
if (dcb->delayq)
{
@ -524,7 +528,7 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
/*<
* Don't write to backend if backend_dcb is not in poll set anymore.
*/
spinlock_acquire(&dcb->authlock);
spinlock_acquire(&dcb->dcb_initlock);
if (dcb->state != DCB_STATE_POLLING) {
/*< vraa : errorHandle */
/*< Free buffer memory */
@ -539,10 +543,12 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
dcb->fd,
STRDCBSTATE(dcb->state))));
spinlock_release(&dcb->authlock);
spinlock_release(&dcb->dcb_initlock);
return 0;
}
spinlock_release(&dcb->dcb_initlock);
spinlock_acquire(&dcb->authlock);
/*<
* Now put the incoming data to the delay queue unless backend is
* connected with auth ok
@ -556,7 +562,6 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
dcb,
dcb->fd,
STRPROTOCOLSTATE(backend_protocol->state))));
backend_set_delayqueue(dcb, queue);
spinlock_release(&dcb->authlock);
return 1;
@ -565,8 +570,6 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
/*<
* Now we set the last command received, from the current queue
*/
memcpy(&dcb->command, &queue->command, sizeof(dcb->command));
spinlock_release(&dcb->authlock);
rc = dcb_write(dcb, queue);
return rc;
@ -804,12 +807,6 @@ static int backend_write_delayqueue(DCB *dcb)
localq = dcb->delayq;
dcb->delayq = NULL;
/*<
* Now we set the last command received, from the delayed queue
*/
memcpy(&dcb->command, &localq->command, sizeof(dcb->command));
spinlock_release(&dcb->delayqlock);
rc = dcb_write(dcb, localq);
@ -851,15 +848,12 @@ static int gw_change_user(DCB *backend, SERVER *server, SESSION *in_session, GWB
unsigned int auth_token_len = 0;
uint8_t *auth_token = NULL;
int rv = -1;
int len = 0;
int auth_ret = 1;
current_session = (MYSQL_session *)in_session->client->data;
backend_protocol = backend->protocol;
client_protocol = in_session->client->protocol;
queue->command = ROUTER_CHANGE_SESSION;
// now get the user, after 4 bytes header and 1 byte command
client_auth_packet += 5;
strcpy(username, (char *)client_auth_packet);
@ -910,31 +904,14 @@ static int gw_change_user(DCB *backend, SERVER *server, SESSION *in_session, GWB
rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
/*<
* The current queue was not handled by func.write() in gw_send_change_user_to_backend()
* We wrote a new gwbuf
* Set backend command here!
*/
memcpy(&backend->command, &queue->command, sizeof(backend->command));
/*<
* Now copy new data into user session
*/
strcpy(current_session->user, username);
strcpy(current_session->db, database);
memcpy(current_session->client_sha1, client_sha1, sizeof(current_session->client_sha1));
}
// consume all the data received from client
spinlock_acquire(&backend->writeqlock);
len = gwbuf_length(queue);
queue = gwbuf_consume(queue, len);
spinlock_release(&backend->writeqlock);
}
gwbuf_free(queue);
return rv;
}
@ -951,7 +928,6 @@ static int gw_session(DCB *backend_dcb, void *data) {
GWBUF *queue = NULL;
queue = (GWBUF *) data;
queue->command = ROUTER_CHANGE_SESSION;
backend_dcb->func.write(backend_dcb, queue);
return 1;

View File

@ -57,6 +57,11 @@ static int gw_client_hangup_event(DCB *dcb);
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
static int route_by_statement(
ROUTER* router_instance,
ROUTER_OBJECT* router,
void* rsession,
GWBUF* read_buf);
/*
* The "module object" for the mysqld client protocol module.
@ -73,7 +78,7 @@ static GWPROTOCOL MyObject = {
gw_MySQLListener, /* Listen */
NULL, /* Authentication */
NULL /* Session */
};
};
/**
* Implementation of the mandatory version entry point
@ -621,10 +626,11 @@ int gw_read_client_event(DCB* dcb) {
*/
{
int len = -1;
GWBUF *queue = NULL;
GWBUF *gw_buffer = NULL;
uint8_t cap = 0;
GWBUF *read_buffer = NULL;
uint8_t *ptr_buff = NULL;
int mysql_command = -1;
bool stmt_input; /*< router input type */
session = dcb->session;
@ -640,21 +646,20 @@ int gw_read_client_event(DCB* dcb) {
//////////////////////////////////////////////////////
// read and handle errors & close, or return if busy
//////////////////////////////////////////////////////
rc = gw_read_gwbuff(dcb, &gw_buffer, b);
rc = gw_read_gwbuff(dcb, &read_buffer, b);
if (rc != 0) {
goto return_rc;
}
/* Now, we are assuming in the first buffer there is
* the information form mysql command */
queue = gw_buffer;
len = GWBUF_LENGTH(queue);
ptr_buff = GWBUF_DATA(queue);
len = GWBUF_LENGTH(read_buffer);
ptr_buff = GWBUF_DATA(read_buffer);
/* get mysql commang at fifth byte */
if (ptr_buff) {
mysql_command = ptr_buff[4];
}
}
/**
* Without rsession there is no access to backend.
* COM_QUIT : close client dcb
@ -683,12 +688,38 @@ int gw_read_client_event(DCB* dcb) {
}
rc = 1;
/** Free buffer */
queue = gwbuf_consume(queue, len);
read_buffer = gwbuf_consume(read_buffer, len);
goto return_rc;
}
/** Ask what type of input the router expects */
cap = router->getCapabilities(router_instance, rsession);
if (cap == 0 || (cap == RCAP_TYPE_PACKET_INPUT))
{
stmt_input = false;
}
else if (cap == RCAP_TYPE_STMT_INPUT)
{
stmt_input = true;
/** Mark buffer to as MySQL type */
gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL);
}
else
{
mysql_send_custom_error(dcb,
1,
0,
"Reading router capabilities "
"failed. Router session is "
"closed.");
rc = 1;
goto return_rc;
}
/** Route COM_QUIT to backend */
if (mysql_command == '\x01') {
router->routeQuery(router_instance, rsession, queue);
router->routeQuery(router_instance, rsession, read_buffer);
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] Routed COM_QUIT to "
@ -704,10 +735,25 @@ int gw_read_client_event(DCB* dcb) {
}
else
{
/** Route other commands to backend */
rc = router->routeQuery(router_instance,
if (stmt_input)
{
/**
* Feed each statement completely and separately
* to router.
*/
rc = route_by_statement(router_instance,
router,
rsession,
read_buffer);
}
else
{
/** Feed whole packet to router */
rc = router->routeQuery(router_instance,
rsession,
queue);
read_buffer);
}
/** succeed */
if (rc == 1) {
rc = 0; /**< here '0' means success */
@ -1193,7 +1239,6 @@ static int gw_error_client_event(DCB *dcb) {
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
router->closeSession(router_instance, rsession);
}
dcb_close(dcb);
@ -1251,7 +1296,8 @@ gw_client_hangup_event(DCB *dcb)
void* router_instance;
void* rsession;
int rc = 1;
#if defined(SS_DEBUG)
#if defined(SS_DEBUG)
MySQLProtocol* protocol = (MySQLProtocol *)dcb->protocol;
if (dcb->state == DCB_STATE_POLLING ||
dcb->state == DCB_STATE_NOPOLLING ||
@ -1260,8 +1306,6 @@ gw_client_hangup_event(DCB *dcb)
CHK_PROTOCOL(protocol);
}
#endif
CHK_DCB(dcb);
if (dcb->state != DCB_STATE_POLLING) {
@ -1278,7 +1322,6 @@ gw_client_hangup_event(DCB *dcb)
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
router->closeSession(router_instance, rsession);
}
@ -1286,3 +1329,99 @@ gw_client_hangup_event(DCB *dcb)
return_rc:
return rc;
}
/**
* Detect if buffer includes partial mysql packet or multiple packets.
* Store partial packet to pendingqueue. Send complete packets one by one
* to router.
*/
static int route_by_statement(
ROUTER* router_instance,
ROUTER_OBJECT* router,
void* rsession,
GWBUF* readbuf)
{
int rc = -1;
DCB* master_dcb;
GWBUF* stmtbuf;
uint8_t* payload;
static size_t len;
do
{
stmtbuf = gw_MySQL_get_next_stmt(&readbuf);
ss_dassert(stmtbuf != NULL);
CHK_GWBUF(stmtbuf);
payload = (uint8_t *)GWBUF_DATA(stmtbuf);
/**
* If message is longer than read data, suspend routing and
* add statement buffer to wait queue.
*/
rc = router->routeQuery(router_instance, rsession, stmtbuf);
}
while (readbuf != NULL);
return rc;
}
/**
* Create a character array including the query string.
* GWBUF given as input includes either one complete or partial query.
* Length of buffer is at most the query length+4 (length of packet header).
*/
#if defined(NOT_USED)
static char* gw_get_or_create_querystr (
void* data,
bool* new_allocation)
{
GWBUF* buf = (GWBUF *)data;
size_t buflen; /*< first gw buffer data length */
size_t packetlen; /*< length of mysql packet */
size_t querylen; /*< total buffer length-<length of type indicator> */
size_t nbytes_copied;
char* startpos; /*< first byte of query in gw buffer */
char* str; /*< resulting query string */
CHK_GWBUF(buf);
packetlen = MYSQL_GET_PACKET_LEN((uint8_t *)GWBUF_DATA(buf));
str = (char *)malloc(packetlen); /*< leave space for terminating null */
if (str == NULL)
{
goto return_str;
}
*new_allocation = true;
/**
* First buffer includes 4 bytes header and a type indicator byte.
*/
buflen = GWBUF_LENGTH(buf);
querylen = packetlen-1;
ss_dassert(buflen<=querylen+5); /*< 5 == header+type indicator */
startpos = (char *)GWBUF_DATA(buf)+5;
nbytes_copied = MIN(querylen, buflen-5);
memcpy(str, startpos, nbytes_copied);
memset(&str[querylen-1], 0, 1);
buf = gwbuf_consume(buf, querylen-1);
/**
* In case of multi-packet statement whole buffer consists of query
* string.
*/
while (buf != NULL)
{
buflen = GWBUF_LENGTH(buf);
memcpy(str+nbytes_copied, GWBUF_DATA(buf), buflen);
nbytes_copied += buflen;
buf = gwbuf_consume(buf, buflen);
}
ss_dassert(str[querylen-1] == 0);
return_str:
return str;
}
#endif

View File

@ -182,7 +182,7 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
conn->state = MYSQL_AUTH_SENT;
// consume all the data here
head = gwbuf_consume(head, gwbuf_length(head));
head = gwbuf_consume(head, GWBUF_LENGTH(head));
return 0;
}
@ -342,7 +342,7 @@ int gw_receive_backend_auth(
/*<
* Remove data from buffer.
*/
head = gwbuf_consume(head, gwbuf_length(head));
head = gwbuf_consume(head, GWBUF_LENGTH(head));
}
else if (n == 0)
{
@ -1280,3 +1280,57 @@ mysql_send_auth_error (DCB *dcb, int packet_number, int in_affected_rows, const
return sizeof(mysql_packet_header) + mysql_payload_size;
}
/**
* Remove the first mysql statement from buffer. Return pointer to the removed
* statement or NULL if buffer is empty.
*
* Clone buf, calculate the length of included mysql stmt, and point the
* statement with cloned buffer. Move the start pointer of buf accordingly
* so that it only cover the remaining buffer.
*
*/
GWBUF* gw_MySQL_get_next_stmt(
GWBUF** p_readbuf)
{
GWBUF* stmtbuf;
size_t buflen;
size_t strlen;
uint8_t* packet;
if (*p_readbuf == NULL)
{
stmtbuf = NULL;
goto return_stmtbuf;
}
CHK_GWBUF(*p_readbuf);
if (GWBUF_EMPTY(*p_readbuf))
{
stmtbuf = NULL;
goto return_stmtbuf;
}
buflen = GWBUF_LENGTH((*p_readbuf));
packet = GWBUF_DATA((*p_readbuf));
strlen = MYSQL_GET_PACKET_LEN(packet);
if (strlen+4 == buflen)
{
stmtbuf = *p_readbuf;
*p_readbuf = NULL;
goto return_stmtbuf;
}
/** vraa :Multi-packet stmt is not supported as of 7.3.14 */
if (strlen-1 > buflen-5)
{
stmtbuf = NULL;
goto return_stmtbuf;
}
stmtbuf = gwbuf_clone_portion(*p_readbuf, 0, strlen+4);
*p_readbuf = gwbuf_consume(*p_readbuf, strlen+4);
return_stmtbuf:
return stmtbuf;
}

View File

@ -54,6 +54,7 @@ static void closeSession(ROUTER *instance, void *router_session);
static void freeSession(ROUTER *instance, void *router_session);
static int execute(ROUTER *instance, void *router_session, GWBUF *queue);
static void diagnostics(ROUTER *instance, DCB *dcb);
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
/** The module object definition */
static ROUTER_OBJECT MyObject = {
@ -64,7 +65,8 @@ static ROUTER_OBJECT MyObject = {
execute,
diagnostics,
NULL,
NULL
NULL,
getCapabilities
};
extern int execute_cmd(CLI_SESSION *cli);
@ -273,3 +275,10 @@ diagnostics(ROUTER *instance, DCB *dcb)
{
return; /* Nothing to do currently */
}
static uint8_t getCapabilities(
ROUTER* inst,
void* router_session)
{
return 0;
}

View File

@ -108,6 +108,8 @@ static void errorReply(
char *message,
DCB *backend_dcb,
int action);
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
/** The module object definition */
static ROUTER_OBJECT MyObject = {
@ -118,13 +120,14 @@ static ROUTER_OBJECT MyObject = {
routeQuery,
diagnostics,
clientReply,
errorReply
errorReply,
getCapabilities
};
static bool rses_begin_router_action(
static bool rses_begin_locked_router_action(
ROUTER_CLIENT_SES* rses);
static void rses_exit_router_action(
static void rses_end_locked_router_action(
ROUTER_CLIENT_SES* rses);
static SPINLOCK instlock;
@ -399,6 +402,8 @@ int master_host = -1;
}
}
client_rses->rses_capabilities = RCAP_TYPE_PACKET_INPUT;
/*
* We now have the server with the least connections.
* Bump the connection count for this server
@ -517,7 +522,7 @@ DCB* backend_dcb;
/**
* Lock router client session for secure read and update.
*/
if (rses_begin_router_action(router_cli_ses))
if (rses_begin_locked_router_action(router_cli_ses))
{
/* decrease server current connection counter */
atomic_add(&router_cli_ses->backend->server->stats.n_current, -1);
@ -526,7 +531,7 @@ DCB* backend_dcb;
router_cli_ses->backend_dcb = NULL;
router_cli_ses->rses_closed = true;
/** Unlock */
rses_exit_router_action(router_cli_ses);
rses_end_locked_router_action(router_cli_ses);
/**
* Close the backend server connection
@ -572,14 +577,14 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
/**
* Lock router client session for secure read of DCBs
*/
rses_is_closed = !(rses_begin_router_action(router_cli_ses));
rses_is_closed = !(rses_begin_locked_router_action(router_cli_ses));
}
if (!rses_is_closed)
{
backend_dcb = router_cli_ses->backend_dcb;
/** unlock */
rses_exit_router_action(router_cli_ses);
rses_end_locked_router_action(router_cli_ses);
}
if (rses_is_closed || backend_dcb == NULL)
@ -690,7 +695,7 @@ static void
errorReply(
ROUTER *instance,
void *router_session,
char *message,
char *message,
DCB *backend_dcb,
int action)
{
@ -717,7 +722,7 @@ errorReply(
* @details (write detailed description here)
*
*/
static bool rses_begin_router_action(
static bool rses_begin_locked_router_action(
ROUTER_CLIENT_SES* rses)
{
bool succp = false;
@ -752,9 +757,17 @@ return_succp:
* @details (write detailed description here)
*
*/
static void rses_exit_router_action(
static void rses_end_locked_router_action(
ROUTER_CLIENT_SES* rses)
{
CHK_CLIENT_RSES(rses);
spinlock_release(&rses->rses_lock);
}
static uint8_t getCapabilities(
ROUTER* inst,
void* router_session)
{
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ static void closeSession(ROUTER *instance, void *session);
static void freeSession(ROUTER *instance, void *session);
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
static void diagnostic(ROUTER *instance, DCB *dcb);
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
static ROUTER_OBJECT MyObject = {
@ -36,7 +37,8 @@ static ROUTER_OBJECT MyObject = {
routeQuery,
diagnostic,
NULL,
NULL
NULL,
getCapabilities
};
/**
@ -137,3 +139,10 @@ static void
diagnostic(ROUTER *instance, DCB *dcb)
{
}
static uint8_t getCapabilities(
ROUTER* inst,
void* router_session)
{
return 0;
}

View File

@ -117,7 +117,9 @@ typedef enum skygw_chk_t {
CHK_NUM_DCB,
CHK_NUM_PROTOCOL,
CHK_NUM_SESSION,
CHK_NUM_ROUTER_SES
CHK_NUM_ROUTER_SES,
CHK_NUM_MY_SESCMD,
CHK_NUM_ROUTER_PROPERTY
} skygw_chk_t;
# define STRBOOL(b) ((b) ? "true" : "false")
@ -214,7 +216,11 @@ typedef enum skygw_chk_t {
((r) == DCB_ROLE_REQUEST_HANDLER ? "DCB_ROLE_REQUEST_HANDLER" : \
"UNKNOWN DCB ROLE"))
#define STRBETYPE(t) ((t) == BE_MASTER ? "BE_MASTER" : \
((t) == BE_SLAVE ? "BE_SLAVE" : \
((t) == BE_UNDEFINED ? "BE_UNDEFINED" : \
"Unknown backend tpe")))
#define CHK_MLIST(l) { \
ss_info_dassert((l->mlist_chk_top == CHK_NUM_MLIST && \
l->mlist_chk_tail == CHK_NUM_MLIST), \
@ -428,7 +434,19 @@ typedef enum skygw_chk_t {
"Router client session has invalid check fields"); \
}
#define CHK_RSES_PROP(p) { \
ss_info_dassert((p)->rses_prop_chk_top == CHK_NUM_ROUTER_PROPERTY && \
(p)->rses_prop_chk_tail == CHK_NUM_ROUTER_PROPERTY, \
"Router property has invalid check fields"); \
}
#define CHK_MYSQL_SESCMD(s) { \
ss_info_dassert((s)->my_sescmd_chk_top == CHK_NUM_MY_SESCMD && \
(s)->my_sescmd_chk_tail == CHK_NUM_MY_SESCMD, \
"Session command has invalid check fields"); \
}
#if defined(SS_DEBUG)
bool conn_open[10240];
#endif