
Added flags to those backend references which have sent something to backend which causes the backend to send results or reply back. Didn't add removal of the flag since there's currently no way to tell whether response from backend contains anything else than session command reply - which aren't counted when BREF_WAITING_RESULT is set and cleared.
324 lines
8.1 KiB
C
324 lines
8.1 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
/**
|
|
* @file buffer.h - The Gateway buffer management functions
|
|
*
|
|
* The buffer management is based on the principle of a linked list
|
|
* of variable size buffer, the intention beign to allow longer
|
|
* content to be buffered in a list and minimise any need to copy
|
|
* data between buffers.
|
|
*
|
|
* @verbatim
|
|
* Revision History
|
|
*
|
|
* Date Who Description
|
|
* 10/06/13 Mark Riddoch Initial implementation
|
|
* 11/07/13 Mark Riddoch Add reference count mechanism
|
|
* 16/07/2013 Massimiliano Pinto Added command type to gwbuf struct
|
|
*
|
|
* @endverbatim
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <buffer.h>
|
|
#include <atomic.h>
|
|
#include <skygw_debug.h>
|
|
|
|
/**
|
|
* Allocate a new gateway buffer structure of size bytes.
|
|
*
|
|
* For now we allocate memory directly from malloc for buffer the management
|
|
* structure and the actual data buffer itself. We may swap at a future date
|
|
* to a more effecient mechanism.
|
|
*
|
|
* @param size The size in bytes of the data area required
|
|
* @return Pointer to the buffer structure or NULL if memory could not
|
|
* be allocated.
|
|
*/
|
|
GWBUF *
|
|
gwbuf_alloc(unsigned int size)
|
|
{
|
|
GWBUF *rval;
|
|
SHARED_BUF *sbuf;
|
|
|
|
// Allocate the buffer header
|
|
if ((rval = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Allocate the shared data buffer
|
|
if ((sbuf = (SHARED_BUF *)malloc(sizeof(SHARED_BUF))) == NULL)
|
|
{
|
|
free(rval);
|
|
return NULL;
|
|
}
|
|
|
|
// Allocate the space for the actual data
|
|
if ((sbuf->data = (unsigned char *)malloc(size)) == NULL)
|
|
{
|
|
free(rval);
|
|
free(sbuf);
|
|
return NULL;
|
|
}
|
|
rval->start = sbuf->data;
|
|
rval->end = rval->start + size;
|
|
sbuf->refcount = 1;
|
|
rval->sbuf = sbuf;
|
|
rval->next = NULL;
|
|
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
|
|
rval->command = 0;
|
|
CHK_GWBUF(rval);
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* Free a gateway buffer
|
|
*
|
|
* @param buf The buffer to free
|
|
*/
|
|
void
|
|
gwbuf_free(GWBUF *buf)
|
|
{
|
|
CHK_GWBUF(buf);
|
|
if (atomic_add(&buf->sbuf->refcount, -1) == 1)
|
|
{
|
|
free(buf->sbuf->data);
|
|
free(buf->sbuf);
|
|
}
|
|
free(buf);
|
|
}
|
|
|
|
/**
|
|
* Increment the usage count of a gateway buffer. This gets a new
|
|
* GWBUF structure that shares the actual data with the existing
|
|
* GWBUF structure but allows for the data copy to be avoided and
|
|
* also for each GWBUF to point to different portions of the same
|
|
* SHARED_BUF.
|
|
*
|
|
* @param buf The buffer to use
|
|
* @return A new GWBUF structure
|
|
*/
|
|
GWBUF *
|
|
gwbuf_clone(GWBUF *buf)
|
|
{
|
|
GWBUF *rval;
|
|
|
|
if ((rval = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
atomic_add(&buf->sbuf->refcount, 1);
|
|
rval->sbuf = buf->sbuf;
|
|
rval->start = buf->start;
|
|
rval->end = buf->end;
|
|
rval->gwbuf_type = buf->gwbuf_type;
|
|
rval->next = NULL;
|
|
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.
|
|
*
|
|
* This call should be made with the caller holding the lock for the linked
|
|
* list.
|
|
*
|
|
* @param head The current head of the linked list
|
|
* @param tail The new buffer to make the tail of the linked list
|
|
* @return The new head of the linked list
|
|
*/
|
|
GWBUF *
|
|
gwbuf_append(GWBUF *head, GWBUF *tail)
|
|
{
|
|
GWBUF *ptr = head;
|
|
|
|
if (!head)
|
|
return tail;
|
|
CHK_GWBUF(head);
|
|
CHK_GWBUF(tail);
|
|
while (ptr->next)
|
|
{
|
|
ptr = ptr->next;
|
|
}
|
|
ptr->next = tail;
|
|
return head;
|
|
}
|
|
|
|
/**
|
|
* Consume data from a buffer in the linked list. The assumption is to consume
|
|
* n bytes from the buffer chain.
|
|
*
|
|
* If after consuming the bytes from the first buffer that buffer becomes
|
|
* empty it will be freed and the linked list updated.
|
|
*
|
|
* The return value is the new head of the linked list.
|
|
*
|
|
* This call should be made with the caller holding the lock for the linked
|
|
* list.
|
|
*
|
|
* @param head The head of the linked list
|
|
* @param length The amount of data to consume
|
|
* @return The head of the linked list
|
|
*/
|
|
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;
|
|
gwbuf_free(head);
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes of data in the linked list.
|
|
*
|
|
* @param head The current head of the linked list
|
|
* @return The number of bytes of data in the linked list
|
|
*/
|
|
unsigned int
|
|
gwbuf_length(GWBUF *head)
|
|
{
|
|
int rval = 0;
|
|
|
|
if (head)
|
|
{
|
|
CHK_GWBUF(head);
|
|
}
|
|
while (head)
|
|
{
|
|
rval += GWBUF_LENGTH(head);
|
|
head = head->next;
|
|
}
|
|
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;
|
|
}
|
|
|
|
|
|
|