
* MXS-199: Support Causal Read in Read Write Splitting * move most causal read logic into rwsplit router and get server type from monitor * misc fix: remove new line * refactor, move config to right place, replace ltrim with gwbuf_consume * refacter a little for previous commit * fix code style
439 lines
14 KiB
C
439 lines
14 KiB
C
#pragma once
|
|
/*
|
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
|
*
|
|
* Use of this software is governed by the Business Source License included
|
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
*
|
|
* Change Date: 2020-01-01
|
|
*
|
|
* On the date above, in accordance with the Business Source License, use
|
|
* of this software will be governed by version 2 or later of the General
|
|
* Public License.
|
|
*/
|
|
|
|
/**
|
|
* @file buffer.h Definitions relating the gateway buffer manipulation facilities.
|
|
*
|
|
* These are used to store all data coming in form or going out to the client and the
|
|
* backend structures.
|
|
*
|
|
* The buffers are designed to be used in linked lists and such that they may be passed
|
|
* from one side of the gateway to another without the need to copy data. It may be the case
|
|
* that not all of the data in the buffer is valid, to this end a start and end pointer are
|
|
* included that point to the first valid byte in the buffer and the first byte after the
|
|
* last valid byte. This allows data to be consumed from either end of the buffer whilst
|
|
* still allowing for the copy free semantics of the buffering system.
|
|
*/
|
|
|
|
#include <maxscale/cdefs.h>
|
|
#include <string.h>
|
|
#include <maxscale/debug.h>
|
|
#include <maxscale/hint.h>
|
|
#include <maxscale/spinlock.h>
|
|
#include <stdint.h>
|
|
|
|
MXS_BEGIN_DECLS
|
|
|
|
struct server;
|
|
|
|
/**
|
|
* Buffer properties - used to store properties related to the buffer
|
|
* contents. This may be added at any point during the processing of the
|
|
* data, especially in the protocol stage of the processing.
|
|
*/
|
|
typedef struct buf_property
|
|
{
|
|
char *name;
|
|
char *value;
|
|
struct buf_property *next;
|
|
} BUF_PROPERTY;
|
|
|
|
typedef enum
|
|
{
|
|
GWBUF_TYPE_UNDEFINED = 0x00,
|
|
GWBUF_TYPE_SESCMD_RESPONSE = 0x01,
|
|
GWBUF_TYPE_RESPONSE_END = 0x02,
|
|
GWBUF_TYPE_SESCMD = 0x04,
|
|
GWBUF_TYPE_HTTP = 0x08,
|
|
GWBUF_TYPE_IGNORABLE = 0x10,
|
|
GWBUF_TYPE_COLLECT_RESULT = 0x20,
|
|
GWBUF_TYPE_RESULT = 0x40,
|
|
GWBUF_TYPE_REPLY_OK = 0x80,
|
|
} gwbuf_type_t;
|
|
|
|
#define GWBUF_IS_TYPE_UNDEFINED(b) (b->gwbuf_type == 0)
|
|
#define GWBUF_IS_TYPE_SESCMD_RESPONSE(b) (b->gwbuf_type & GWBUF_TYPE_SESCMD_RESPONSE)
|
|
#define GWBUF_IS_TYPE_RESPONSE_END(b) (b->gwbuf_type & GWBUF_TYPE_RESPONSE_END)
|
|
#define GWBUF_IS_TYPE_SESCMD(b) (b->gwbuf_type & GWBUF_TYPE_SESCMD)
|
|
#define GWBUF_IS_IGNORABLE(b) (b->gwbuf_type & GWBUF_TYPE_IGNORABLE)
|
|
#define GWBUF_IS_COLLECTED_RESULT(b) (b->gwbuf_type & GWBUF_TYPE_RESULT)
|
|
#define GWBUF_SHOULD_COLLECT_RESULT(b) (b->gwbuf_type & GWBUF_TYPE_COLLECT_RESULT)
|
|
#define GWBUF_IS_REPLY_OK(b) (b->gwbuf_type & GWBUF_TYPE_REPLY_OK)
|
|
|
|
typedef enum
|
|
{
|
|
GWBUF_INFO_NONE = 0x0,
|
|
GWBUF_INFO_PARSED = 0x1
|
|
} gwbuf_info_t;
|
|
|
|
#define GWBUF_IS_PARSED(b) (b->sbuf->info & GWBUF_INFO_PARSED)
|
|
|
|
/**
|
|
* A structure for cleaning up memory allocations of structures which are
|
|
* referred to by GWBUF and deallocated in gwbuf_free but GWBUF doesn't
|
|
* know what they are.
|
|
* All functions on the list are executed before freeing memory of GWBUF struct.
|
|
*/
|
|
typedef enum
|
|
{
|
|
GWBUF_PARSING_INFO
|
|
} bufobj_id_t;
|
|
|
|
typedef struct buffer_object_st buffer_object_t;
|
|
|
|
struct buffer_object_st
|
|
{
|
|
bufobj_id_t bo_id;
|
|
void* bo_data;
|
|
void (*bo_donefun_fp)(void *);
|
|
buffer_object_t* bo_next;
|
|
};
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
typedef struct
|
|
{
|
|
buffer_object_t *bufobj; /*< List of objects referred to by GWBUF */
|
|
int32_t refcount; /*< Reference count on the buffer */
|
|
uint32_t info; /*< Info bits */
|
|
unsigned char data[1]; /*< Actual memory that was allocated */
|
|
} SHARED_BUF;
|
|
|
|
/**
|
|
* The buffer structure used by the descriptor control blocks.
|
|
*
|
|
* Linked lists of buffers are created as data is read from a descriptor
|
|
* or written to a descriptor. The use of linked lists of buffers with
|
|
* flexible data pointers is designed to minimise the need for data to
|
|
* be copied within the gateway.
|
|
*/
|
|
typedef struct gwbuf
|
|
{
|
|
SPINLOCK gwbuf_lock;
|
|
struct gwbuf *next; /*< Next buffer in a linked chain of buffers */
|
|
struct gwbuf *tail; /*< Last 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 */
|
|
uint32_t gwbuf_type; /*< buffer's data type information */
|
|
HINT *hint; /*< Hint data for this buffer */
|
|
BUF_PROPERTY *properties; /*< Buffer properties */
|
|
struct server *server; /*< The target server where the buffer is executed */
|
|
} GWBUF;
|
|
|
|
/*<
|
|
* Macros to access the data in the buffers
|
|
*/
|
|
/*< First valid, unconsumed byte in the buffer */
|
|
#define GWBUF_DATA(b) ((uint8_t*)(b)->start)
|
|
|
|
/*< Number of bytes in the individual buffer */
|
|
#define GWBUF_LENGTH(b) ((size_t)((char *)(b)->end - (char *)(b)->start))
|
|
|
|
/*< Return the byte at offset byte from the start of the unconsumed portion of the buffer */
|
|
#define GWBUF_DATA_CHAR(b, byte) (GWBUF_LENGTH(b) < ((byte)+1) ? -1 : *(((char *)(b)->start)+4))
|
|
|
|
/*< Check that the data in a buffer has the SQL marker*/
|
|
#define GWBUF_IS_SQL(b) (0x03 == GWBUF_DATA_CHAR(b,4))
|
|
|
|
/*< Check whether the buffer is contiguous*/
|
|
#define GWBUF_IS_CONTIGUOUS(b) (((b) == NULL) || ((b)->next == NULL))
|
|
|
|
/*< True if all bytes in the buffer have been consumed */
|
|
#define GWBUF_EMPTY(b) ((char *)(b)->start >= (char *)(b)->end)
|
|
|
|
/*< Consume a number of bytes in the buffer */
|
|
#define GWBUF_CONSUME(b, bytes) ((b)->start = bytes > ((char *)(b)->end - (char *)(b)->start) ? (b)->end : (void *)((char *)(b)->start + (bytes)));
|
|
|
|
/*< Check if a given pointer is within the buffer */
|
|
#define GWBUF_POINTER_IN_BUFFER (ptr, b)\
|
|
((char *)(ptr) >= (char *)(b)->start && (char *)(ptr) < (char *)(b)->end)
|
|
|
|
/*< Consume a complete buffer */
|
|
#define GWBUF_CONSUME_ALL(b) gwbuf_consume((b), GWBUF_LENGTH((b)))
|
|
|
|
#define GWBUF_RTRIM(b, bytes)\
|
|
((b)->end = bytes > ((char *)(b)->end - (char *)(b)->start) ? (b)->start : \
|
|
(void *)((char *)(b)->end - (bytes)));
|
|
|
|
#define GWBUF_TYPE(b) (b)->gwbuf_type
|
|
/*<
|
|
* Function prototypes for the API to maniplate the buffers
|
|
*/
|
|
|
|
/**
|
|
* Allocate a new gateway buffer of specified size.
|
|
*
|
|
* @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.
|
|
*/
|
|
extern GWBUF *gwbuf_alloc(unsigned int size);
|
|
|
|
/**
|
|
* Allocate a new gateway buffer structure of specified size and load with data.
|
|
*
|
|
* @param size The size in bytes of the data area required
|
|
* @param data Pointer to the data (size bytes) to be loaded
|
|
*
|
|
* @return Pointer to the buffer structure or NULL if memory could not
|
|
* be allocated.
|
|
*/
|
|
extern GWBUF *gwbuf_alloc_and_load(unsigned int size, const void *data);
|
|
|
|
/**
|
|
* Free a chain of gateway buffers
|
|
*
|
|
* @param buf The head of the list of buffers to free
|
|
*/
|
|
extern void gwbuf_free(GWBUF *buf);
|
|
|
|
/**
|
|
* Clone a GWBUF. Note that if the GWBUF is actually a list of
|
|
* GWBUFs, then every GWBUF in the list will be cloned. Note that but
|
|
* for the GWBUF structure itself, the data is shared.
|
|
*
|
|
* @param buf The GWBUF to be cloned.
|
|
*
|
|
* @return The cloned GWBUF, or NULL if @buf was NULL or if any part
|
|
* of @buf could not be cloned.
|
|
*/
|
|
extern GWBUF *gwbuf_clone(GWBUF *buf);
|
|
|
|
/**
|
|
* @brief Deep clone a GWBUF
|
|
*
|
|
* Clone the data inside a GWBUF into a new buffer. The created buffer has its
|
|
* own internal buffer and any modifications to the deep cloned buffer will not
|
|
* reflect on the original one. Any buffer objects attached to the original buffer
|
|
* will not be copied. Only the buffer type of the original buffer will be copied
|
|
* over to the cloned buffer.
|
|
*
|
|
* @param buf Buffer to clone
|
|
*
|
|
* @return Deep copy of @c buf or NULL on error
|
|
*/
|
|
extern GWBUF* gwbuf_deep_clone(const GWBUF* buf);
|
|
|
|
/**
|
|
* Compare two GWBUFs. Two GWBUFs are considered identical if their
|
|
* content is identical, irrespective of whether one is segmented and
|
|
* the other is not.
|
|
*
|
|
* @param lhs One GWBUF
|
|
* @param rhs Another GWBUF
|
|
*
|
|
* @return 0 if the content is identical,
|
|
* -1 if @c lhs is less than @c rhs, and
|
|
* 1 if @c lhs is more than @c rhs.
|
|
*
|
|
* @attention A NULL @c GWBUF is considered to be less than a non-NULL one,
|
|
* and a shorter @c GWBUF less than a longer one. Otherwise the
|
|
* the sign of the return value is determined by the sign of the
|
|
* difference between the first pair of bytes (interpreted as
|
|
* unsigned char) that differ in lhs and rhs.
|
|
*/
|
|
extern int gwbuf_compare(const GWBUF* lhs, const GWBUF* rhs);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
extern GWBUF *gwbuf_append(GWBUF *head, GWBUF *tail);
|
|
|
|
/**
|
|
* @brief Consume data from buffer chain
|
|
*
|
|
* Data is consumed from @p head until either @p length bytes have been
|
|
* processed or @p head is empty. If @p head points to a chain of buffers,
|
|
* those buffers are counted as a part of @p head and will also be consumed if
|
|
* @p length exceeds the size of the first buffer.
|
|
*
|
|
* @param head The head of the linked list
|
|
* @param length Number of bytes to consume
|
|
*
|
|
* @return The head of the linked list or NULL if everything was consumed
|
|
*/
|
|
extern GWBUF *gwbuf_consume(GWBUF *head, unsigned int length);
|
|
|
|
/**
|
|
* Trim bytes from the end of a GWBUF structure that may be the first
|
|
* in a list. If the buffer has n_bytes or less then it will be freed and
|
|
* the next buffer in the list will be returned, or if none, NULL.
|
|
*
|
|
* @param head The buffer to trim
|
|
* @param n_bytes The number of bytes to trim off
|
|
*
|
|
* @return The buffer chain or NULL if buffer chain now empty
|
|
*/
|
|
extern GWBUF *gwbuf_rtrim(GWBUF *head, unsigned int length);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
extern unsigned int gwbuf_length(const GWBUF *head);
|
|
|
|
/**
|
|
* Return the number of individual buffers in the linked list.
|
|
*
|
|
* Currently not used, provided mainly for use during debugging sessions.
|
|
*
|
|
* @param head The current head of the linked list
|
|
*
|
|
* @return The number of bytes of data in the linked list
|
|
*/
|
|
extern int gwbuf_count(const GWBUF *head);
|
|
|
|
/**
|
|
* @brief Copy bytes from a buffer
|
|
*
|
|
* Copy bytes from a chain of buffers. Supports copying data from buffers where
|
|
* the data is spread across multiple buffers.
|
|
*
|
|
* @param buffer Buffer to copy from
|
|
* @param offset Offset into the buffer
|
|
* @param bytes Number of bytes to copy
|
|
* @param dest Destination where the bytes are copied
|
|
*
|
|
* @return Number of bytes copied.
|
|
*/
|
|
extern size_t gwbuf_copy_data(const GWBUF *buffer, size_t offset, size_t bytes, uint8_t* dest);
|
|
|
|
/**
|
|
* @brief Split a buffer in two
|
|
*
|
|
* The returned value will be @c length bytes long. If the length of @c buf
|
|
* exceeds @c length, the remaining buffers are stored in @buf.
|
|
*
|
|
* @param buf Buffer chain to split
|
|
* @param length Number of bytes that the returned buffer should contain
|
|
*
|
|
* @return Head of the buffer chain.
|
|
*/
|
|
extern GWBUF *gwbuf_split(GWBUF **buf, size_t length);
|
|
|
|
/**
|
|
* Set given type to all buffers on the list.
|
|
* *
|
|
* @param buf The shared buffer
|
|
* @param type Type to be added, mask of @c gwbuf_type_t values.
|
|
*/
|
|
extern void gwbuf_set_type(GWBUF *head, uint32_t type);
|
|
|
|
/**
|
|
* Add a property to a buffer.
|
|
*
|
|
* @param buf The buffer to add the property to
|
|
* @param name The property name
|
|
* @param value The property value
|
|
*
|
|
* @return True on success, false otherwise.
|
|
*/
|
|
extern bool gwbuf_add_property(GWBUF *buf, const char *name, const char *value);
|
|
|
|
/**
|
|
* Return the value of a buffer property
|
|
*
|
|
* @param buf The buffer itself
|
|
* @param name The name of the property to return
|
|
*
|
|
* @return The property value or NULL if the property was not found.
|
|
*/
|
|
extern char *gwbuf_get_property(GWBUF *buf, const char *name);
|
|
|
|
/**
|
|
* Convert a chain of GWBUF structures into a single GWBUF structure
|
|
*
|
|
* @param orig The chain to convert
|
|
*
|
|
* @return NULL if @c buf is NULL or if a memory allocation fails,
|
|
* @c buf if @c buf already is contiguous, and otherwise
|
|
* a contigious copy of @c buf.
|
|
*
|
|
* @attention If a non-NULL value is returned, the @c buf should no
|
|
* longer be used as it may have been freed.
|
|
*/
|
|
extern GWBUF *gwbuf_make_contiguous(GWBUF *buf);
|
|
|
|
/**
|
|
* Add hint to a buffer.
|
|
*
|
|
* @param buf The buffer to add the hint to
|
|
* @param hint The hint. Note that the ownership of @c hint is transferred
|
|
* to @c buf.
|
|
*/
|
|
extern void gwbuf_add_hint(GWBUF *buf, HINT *hint);
|
|
|
|
/**
|
|
* Add a buffer object to GWBUF buffer.
|
|
*
|
|
* @param buf GWBUF where object is added
|
|
* @param id Type identifier for object
|
|
* @param data Object data
|
|
* @param donefun_fp Clean-up function to be executed before buffer is freed.
|
|
*/
|
|
void gwbuf_add_buffer_object(GWBUF* buf,
|
|
bufobj_id_t id,
|
|
void* data,
|
|
void (*donefun_fp)(void *));
|
|
|
|
/**
|
|
* Search buffer object which matches with the id.
|
|
*
|
|
* @param buf GWBUF to be searched
|
|
* @param id Identifier for the object
|
|
*
|
|
* @return Searched buffer object or NULL if not found
|
|
*/
|
|
void *gwbuf_get_buffer_object_data(GWBUF* buf, bufobj_id_t id);
|
|
#if defined(BUFFER_TRACE)
|
|
extern void dprintAllBuffers(void *pdcb);
|
|
#endif
|
|
|
|
/**
|
|
* Debug function for dumping buffer contents to INFO log
|
|
*
|
|
* @param buffer Buffer to dump
|
|
*/
|
|
void gwbuf_hexdump(GWBUF* buffer);
|
|
|
|
/**
|
|
* Return pointer of the byte at offset from start of chained buffer
|
|
* Warning: It not guaranteed to point to a contiguous segment of memory,
|
|
* it is only safe to modify the first byte this pointer point to.
|
|
*
|
|
* @param buffer one or more chained buffer
|
|
* @param offset Offset into the buffer
|
|
* @return if total buffer length is bigger than offset then return
|
|
* the offset byte pointer, otherwise return null
|
|
*/
|
|
extern uint8_t *gwbuf_byte_pointer(GWBUF* buffer, size_t offset);
|
|
|
|
MXS_END_DECLS
|