Added mysql_common.c for mysql common routines

First attempt to split MySQL routines in client and backend libraries.
Compiled on Centos but not tested

mysql_protocol.h header file is coming soon.
This commit is contained in:
Massimiliano Pinto 2013-06-17 16:25:22 +02:00
parent 1300c5d089
commit aa517205b7
4 changed files with 1351 additions and 63 deletions

View File

@ -16,14 +16,15 @@
#
# Revision History
# Date Who Description
# 13/06/13 Mark Riddoch Initial protocol module development
# 13/06/2013 Mark Riddoch Initial protocol module development
# 17/06/2013 Massimiliano Pinto Added mysql_common top both libraries
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include
LDFLAGS=-shared
MYSQLCLIENTSRCS=mysql_client.c
MYSQLCLIENTSRCS=mysql_client.c mysql_common.c
MYSQLCLIENTOBJ=$(MYSQLCLIENTSRCS:.c=.o)
MYSQLBACKENDSRCS=mysql_backend.c
MYSQLBACKENDSRCS=mysql_backend.c mysql_common.c
MYSQLBACKENDOBJ=$(MYSQLBACKENDSRCS:.c=.o)
SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS)
OBJ=$(SRCS:.c=.o)
@ -45,11 +46,3 @@ clean:
tags:
ctags $(SRCS) $(HDRS)
depend: depend.mk
depend.mk: $(SRCS)
@rm -f depend.mk
cc -M $(CFLAGS) $(SRCS) > depend.mk
include depend.mk

View File

@ -15,46 +15,38 @@
*
* Copyright SkySQL Ab 2013
*/
#include <stdio.h>
#include <dcb.h>
#include <buffer.h>
/**
* @file mysql_backend.c - protcol module to connect to a backend MySQL database
*
#include "mysql_protocol.h"
/*
* MySQL Protocol module for handling the protocol between the gateway
* and the backend MySQL database.
*
* @verbatim
* Revision History
* Date Who Description
* 14/06/2013 Mark Riddoch Initial version
*
* @endverbatim
* 17/06/2013 Massimiliano Pinto Added Gateway To Backends routines
*/
static char *version_str = "V1.0.0";
extern int gw_read_backend_event(DCB* dcb, int epfd);
extern int gw_write_backend_event(DCB *dcb, int epfd);
extern int MySQLWrite(DCB *dcb, GWBUF *queue);
extern int handle_event_errors_backend(DCB *dcb, int event);
static int gw_read_backend_event(DCB* dcb, int epfd);
static int gw_write_backend_event(DCB *dcb, int epfd);
static int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue);
static int gw_error_backend_event(DCB *dcb, int epfd, int event);
/**
* The module object that defines the entry points to the protocol module.
*/
static GWPROTOCOL MyObject = {
gw_read_backend_event, /**< Read - EPOLLIN handler */
MySQLWrite, /**< Write - data from gateway */
gw_write_backend_event, /**< WriteReady - EPOLLOUT handler */
handle_event_errors_backend, /**< Error - EPOLLERR handler */
NULL, /**< HangUp - EPOLLHUP handler */
NULL, /**< Accept */
NULL, /**< Connect */
NULL /**< Close */
gw_read_backend_event, /* Read - EPOLLIN handler */
gw_MySQLWrite_backend, /* Write - data from gateway */
gw_write_backend_event, /* WriteReady - EPOLLOUT handler */
gw_error_backend_event, /* Error - EPOLLERR handler */
NULL, /* HangUp - EPOLLHUP handler */
NULL, /* Accept */
NULL, /* Connect */
NULL /* Close */
};
/**
/*
* Implementation of the mandatory version entry point
*
* @return version string of the module
@ -65,7 +57,7 @@ version()
return version_str;
}
/**
/*
* The module initialisation routine, called when the module
* is first loaded.
*/
@ -75,7 +67,7 @@ ModuleInit()
fprintf(stderr, "Initial MySQL Client Protcol module.\n");
}
/**
/*
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
* "module object", this is a structure with the set of
@ -88,3 +80,188 @@ GetModuleObject()
{
return &MyObject;
}
//////////////////////////////////////////
//backend read event triggered by EPOLLIN
//////////////////////////////////////////
int gw_read_backend_event(DCB *dcb, int epfd) {
int n;
MySQLProtocol *client_protocol = NULL;
if (dcb)
if(dcb->session)
client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol);
#ifdef GW_DEBUG_READ_EVENT
fprintf(stderr, "Backend ready! Read from Backend %i, write to client %i, client state %i\n", dcb->fd, dcb->session->client->fd, client_protocol->state);
#endif
if ((client_protocol->state == MYSQL_WAITING_RESULT) || (client_protocol->state == MYSQL_IDLE)) {
struct epoll_event new_event;
int w;
int b = -1;
int tot_b = -1;
uint8_t *ptr_buffer;
GWBUF *buffer, *head;
if (ioctl(dcb->fd, FIONREAD, &b)) {
fprintf(stderr, "Backend Ioctl FIONREAD error %i, %s\n", errno , strerror(errno));
} else {
//fprintf(stderr, "Backend IOCTL FIONREAD bytes to read = %i\n", b);
}
/*
* Read all the data that is available into a chain of buffers
*/
head = NULL;
while (b > 0)
{
int bufsize = b < MAX_BUFFER_SIZE ? b : MAX_BUFFER_SIZE;
if ((buffer = gwbuf_alloc(bufsize)) == NULL)
{
/* Bad news, we have run out of memory */
return 0;
}
GW_NOINTR_CALL(n = read(dcb->fd, GWBUF_DATA(buffer), bufsize); dcb->stats.n_reads++);
if (n < 0)
{
// if eerno == EAGAIN || EWOULDBLOCK is missing
// do the right task, not just break
break;
}
head = gwbuf_append(head, buffer);
// how many bytes left
b -= n;
}
// write the gwbuffer to client
dcb->session->client->func.write(dcb->session->client, head);
return 1;
}
return 1;
}
//////////////////////////////////////////
//backend write event triggered by EPOLLOUT
//////////////////////////////////////////
int gw_write_backend_event(DCB *dcb, int epfd) {
//fprintf(stderr, ">>> gw_write_backend_event for %i\n", dcb->fd);
return 0;
}
/*
* Write function for client DCB
*
* @param dcb The DCB of the client
* @param queue Queue of buffers to write
*/
int
gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
{
int w, saved_errno = 0;
spinlock_acquire(&dcb->writeqlock);
if (dcb->writeq)
{
/*
* We have some queued data, so add our data to
* the write queue and return.
* The assumption is that there will be an EPOLLOUT
* event to drain what is already queued. We are protected
* by the spinlock, which will also be acquired by the
* the routine that drains the queue data, so we should
* not have a race condition on the event.
*/
dcb->writeq = gwbuf_append(dcb->writeq, queue);
dcb->stats.n_buffered++;
}
else
{
int len;
/*
* Loop over the buffer chain that has been passed to us
* from the reading side.
* Send as much of the data in that chain as possible and
* add any balance to the write queue.
*/
while (queue != NULL)
{
len = GWBUF_LENGTH(queue);
GW_NOINTR_CALL(w = write(dcb->fd, GWBUF_DATA(queue), len); dcb->stats.n_writes++);
saved_errno = errno;
if (w < 0)
{
break;
}
/*
* Pull the number of bytes we have written from
* queue with have.
*/
queue = gwbuf_consume(queue, w);
if (w < len)
{
/* We didn't write all the data */
}
}
/* Buffer the balance of any data */
dcb->writeq = queue;
if (queue)
{
dcb->stats.n_buffered++;
}
}
spinlock_release(&dcb->writeqlock);
if (queue && (saved_errno != EAGAIN || saved_errno != EWOULDBLOCK))
{
/* We had a real write failure that we must deal with */
return 0;
}
return 1;
}
int gw_error_backend_event(DCB *dcb, int epfd, int event) {
struct epoll_event ed;
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
fprintf(stderr, "#### Handle Backend error function for %i\n", dcb->fd);
#ifdef GW_EVENT_DEBUG
if (event != -1) {
fprintf(stderr, ">>>>>> Backend DCB state %i, Protocol State %i: event %i, %i\n", dcb->state, dcb->proto_state, event & EPOLLERR, event & EPOLLHUP);
if(event & EPOLLHUP)
fprintf(stderr, "EPOLLHUP\n");
if(event & EPOLLERR)
fprintf(stderr, "EPOLLERR\n");
if(event & EPOLLPRI)
fprintf(stderr, "EPOLLPRI\n");
}
#endif
if (dcb->state != DCB_STATE_LISTENING) {
if (epoll_ctl(epfd, EPOLL_CTL_DEL, dcb->fd, &ed) == -1) {
fprintf(stderr, "Backend epoll_ctl_del: from events check failed to delete %i, [%i]:[%s]\n", dcb->fd, errno, strerror(errno));
}
#ifdef GW_EVENT_DEBUG
fprintf(stderr, "Backend closing fd [%i]=%i, from events check\n", dcb->fd, protocol->fd);
#endif
if (dcb->fd) {
dcb->state = DCB_STATE_DISCONNECTED;
fprintf(stderr, "Freeing backend MySQL conn %p, %p\n", dcb->protocol, &dcb->protocol);
gw_mysql_close((MySQLProtocol **)&dcb->protocol);
fprintf(stderr, "Freeing backend MySQL conn %p, %p\n", dcb->protocol, &dcb->protocol);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
/*
* This file is distributed as part of the SkySQL Gateway. It is free
* software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright SkySQL Ab 2013
*/
/*
* MySQL Protocol common routines for client to gateway and gateway to backend
*
* Revision History
* Date Who Description
* 17/06/2013 Massimiliano Pinto Common MySQL protocol routines
*/
#include "mysql_protocol.h"
static char *version_str = "V1.0.0";
///////////////////////////////////////
MySQLProtocol *gw_mysql_init(MySQLProtocol *data) {
int rv = -1;
MySQLProtocol *input = NULL;
if (input == NULL) {
// structure allocation
input = calloc(1, sizeof(MySQLProtocol));
if (input == NULL)
return NULL;
}
#ifdef MYSQL_CONN_DEBUG
fprintf(stderr, "gw_mysql_init() called\n");
#endif
return input;
}
//////////////////////////////////////
// close a connection if opened
// free data scructure
//////////////////////////////////////
void gw_mysql_close(MySQLProtocol **ptr) {
MySQLProtocol *conn = *ptr;
if (*ptr == NULL)
return;
#ifdef MYSQL_CONN_DEBUG
fprintf(stderr, "Closing MySQL connection %i, [%s]\n", conn->fd, conn->scramble);
#endif
if (conn->fd > 0) {
//COM_QUIT will not be sent here, but from the caller of this routine!
#ifdef MYSQL_CONN_DEBUG
fprintf(stderr, "mysqlgw_mysql_close() called for %i\n", conn->fd);
#endif
close(conn->fd);
} else {
#ifdef MYSQL_CONN_DEBUG
fprintf(stderr, "mysqlgw_mysql_close() called, no socket %i\n", conn->fd);
#endif
}
free(*ptr);
*ptr = NULL;
#ifdef MYSQL_CONN_DEBUG
fprintf(stderr, "mysqlgw_mysql_close() free(conn) done\n");
#endif
}