Merge branch '2.1' into develop

This commit is contained in:
Johan Wikman
2017-03-03 13:37:14 +02:00
56 changed files with 2355 additions and 630 deletions

View File

@ -1,13 +1,13 @@
add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c config_runtime.c dcb.c filter.c filter.cc externcmd.c paths.c hashtable.c hint.c housekeeper.c load_utils.c log_manager.cc maxscale_pcre2.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c ssl.c mysql_utils.c mysql_binlog.c modulecmd.c )
target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl pthread crypt dl crypto inih z rt m stdc++)
if(WITH_JEMALLOC)
target_link_libraries(maxscale-common ${JEMALLOC_LIBRARIES})
elseif(WITH_TCMALLOC)
target_link_libraries(maxscale-common ${TCMALLOC_LIBRARIES})
endif()
target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl pthread crypt dl crypto inih z rt m stdc++)
add_dependencies(maxscale-common pcre2 connector-c)
set_target_properties(maxscale-common PROPERTIES VERSION "1.0.0")
install_module(maxscale-common core)

View File

@ -143,7 +143,7 @@ static struct option long_options[] =
static bool syslog_configured = false;
static bool maxlog_configured = false;
static bool log_to_shm_configured = false;
static int last_signal = 0;
static volatile sig_atomic_t last_signal = 0;
static int cnf_preparser(void* data, const char* section, const char* name, const char* value);
static void log_flush_shutdown(void);
@ -381,7 +381,7 @@ sigchld_handler (int i)
}
}
int fatal_handling = 0;
volatile sig_atomic_t fatal_handling = 0;
static int signal_set(int sig, void (*handler)(int));
@ -405,12 +405,12 @@ sigfatal_handler(int i)
{
void *addrs[128];
int n, count = backtrace(addrs, 128);
int count = backtrace(addrs, 128);
char** symbols = backtrace_symbols(addrs, count);
if (symbols)
{
for (n = 0; n < count; n++)
for (int n = 0; n < count; n++)
{
MXS_ALERT(" %s\n", symbols[n]);
}
@ -2023,6 +2023,9 @@ int main(int argc, char **argv)
unlock_pidfile();
unlink_pidfile();
ERR_free_strings();
EVP_cleanup();
return_main:
mxs_log_flush_sync();
@ -2277,6 +2280,12 @@ bool pid_file_exists()
static int write_pid_file()
{
if (!mxs_mkdir_all(get_piddir(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
{
MXS_ERROR("Failed to create PID directory.");
return 1;
}
char logbuf[STRING_BUFFER_SIZE + PATH_MAX];
char pidstr[STRING_BUFFER_SIZE];

View File

@ -567,10 +567,11 @@ hashtable_read_lock(HASHTABLE *table)
while (table->writelock)
{
spinlock_release(&table->spin);
while (table->writelock)
while (atomic_add(&table->writelock, 1) != 0)
{
;
atomic_add(&table->writelock, -1);
}
atomic_add(&table->writelock, -1);
spinlock_acquire(&table->spin);
}
atomic_add(&table->n_readers, 1);
@ -614,10 +615,11 @@ hashtable_write_lock(HASHTABLE *table)
spinlock_acquire(&table->spin);
do
{
while (table->n_readers)
while (atomic_add(&table->n_readers, 1) != 0)
{
;
atomic_add(&table->n_readers, -1);
}
atomic_add(&table->n_readers, -1);
available = atomic_add(&table->writelock, 1);
if (available != 0)
{

View File

@ -94,7 +94,7 @@ typedef struct fake_event
struct fake_event *next; /*< The next event */
} fake_event_t;
thread_local int thread_id; /**< This thread's ID */
thread_local int current_thread_id; /**< This thread's ID */
static int *epoll_fd; /*< The epoll file descriptor */
static int next_epoll_fd = 0; /*< Which thread handles the next DCB */
static fake_event_t **fake_events; /*< Thread-specific fake event queue */
@ -656,9 +656,11 @@ poll_waitevents(void *arg)
{
struct epoll_event events[MAX_EVENTS];
int i, nfds, timeout_bias = 1;
thread_id = (intptr_t)arg;
current_thread_id = (intptr_t)arg;
int poll_spins = 0;
int thread_id = current_thread_id;
thread_data[thread_id].state = THREAD_IDLE;
while (1)
@ -1613,7 +1615,7 @@ void poll_send_message(enum poll_message msg, void *data)
for (int i = 0; i < nthr; i++)
{
if (i != thread_id)
if (i != current_thread_id)
{
while (poll_msg[i] & msg)
{
@ -1628,6 +1630,8 @@ void poll_send_message(enum poll_message msg, void *data)
static void poll_check_message()
{
int thread_id = current_thread_id;
if (poll_msg[thread_id] & POLL_MSG_CLEAN_PERSISTENT)
{
SERVER *server = (SERVER*)poll_msg_data;

View File

@ -10,7 +10,6 @@ add_subdirectory(nullfilter)
add_subdirectory(qlafilter)
add_subdirectory(regexfilter)
add_subdirectory(tee)
add_subdirectory(testfilter)
add_subdirectory(topfilter)
add_subdirectory(tpmfilter)
add_subdirectory(masking)

View File

@ -15,8 +15,9 @@
#include "cachefiltersession.hh"
#include <new>
#include <maxscale/alloc.h>
#include <maxscale/query_classifier.h>
#include <maxscale/modutil.h>
#include <maxscale/mysql_utils.h>
#include <maxscale/query_classifier.h>
#include "storage.hh"
namespace
@ -34,6 +35,108 @@ inline bool cache_max_resultset_size_exceeded(const CACHE_CONFIG& config, uint64
}
namespace
{
const char* NON_CACHEABLE_FUNCTIONS[] =
{
"benchmark",
"connection_id",
"convert_tz",
"curdate",
"current_date",
"current_timestamp",
"curtime",
"database",
"encrypt",
"found_rows",
"get_lock",
"is_free_lock",
"is_used_lock",
"last_insert_id",
"load_file",
"localtime",
"localtimestamp",
"master_pos_wait",
"now",
"rand",
"release_lock",
"session_user",
"sleep",
"sysdate",
"system_user",
"unix_timestamp",
"user",
"uuid",
"uuid_short",
};
const char* NON_CACHEABLE_VARIABLES[] =
{
"current_date",
"current_timestamp",
"localtime",
"localtimestamp",
};
const size_t N_NON_CACHEABLE_FUNCTIONS = sizeof(NON_CACHEABLE_FUNCTIONS)/sizeof(NON_CACHEABLE_FUNCTIONS[0]);
const size_t N_NON_CACHEABLE_VARIABLES = sizeof(NON_CACHEABLE_VARIABLES)/sizeof(NON_CACHEABLE_VARIABLES[0]);
int compare_name(const void* pLeft, const void* pRight)
{
return strcasecmp((const char*)pLeft, *(const char**)pRight);
}
inline bool uses_name(const char* zName, const char** pzNames, size_t nNames)
{
return bsearch(zName, pzNames, nNames, sizeof(const char*), compare_name) != NULL;
}
bool uses_non_cacheable_function(GWBUF* pPacket)
{
bool rv = false;
const QC_FUNCTION_INFO* pInfo;
size_t nInfos;
qc_get_function_info(pPacket, &pInfo, &nInfos);
const QC_FUNCTION_INFO* pEnd = pInfo + nInfos;
while (!rv && (pInfo != pEnd))
{
rv = uses_name(pInfo->name, NON_CACHEABLE_FUNCTIONS, N_NON_CACHEABLE_FUNCTIONS);
++pInfo;
}
return rv;
}
bool uses_non_cacheable_variable(GWBUF* pPacket)
{
bool rv = false;
const QC_FIELD_INFO* pInfo;
size_t nInfos;
qc_get_field_info(pPacket, &pInfo, &nInfos);
const QC_FIELD_INFO* pEnd = pInfo + nInfos;
while (!rv && (pInfo != pEnd))
{
rv = uses_name(pInfo->column, NON_CACHEABLE_VARIABLES, N_NON_CACHEABLE_VARIABLES);
++pInfo;
}
return rv;
}
}
CacheFilterSession::CacheFilterSession(MXS_SESSION* pSession, Cache* pCache, char* zDefaultDb)
: maxscale::FilterSession(pSession)
, m_state(CACHE_EXPECTING_NOTHING)
@ -41,6 +144,7 @@ CacheFilterSession::CacheFilterSession(MXS_SESSION* pSession, Cache* pCache, cha
, m_zDefaultDb(zDefaultDb)
, m_zUseDb(NULL)
, m_refreshing(false)
, m_is_read_only(true)
{
memset(m_key.data, 0, CACHE_KEY_MAXLEN);
@ -129,105 +233,83 @@ int CacheFilterSession::routeQuery(GWBUF* pPacket)
break;
case MYSQL_COM_QUERY:
if (should_consult_cache(pPacket))
{
// We do not care whether the query was fully parsed or not.
// If a query cannot be fully parsed, the worst thing that can
// happen is that caching is not used, even though it would be
// possible.
if (qc_get_operation(pPacket) == QUERY_OP_SELECT)
if (m_pCache->should_store(m_zDefaultDb, pPacket))
{
MXS_SESSION *session = m_pSession;
if ((session_is_autocommit(session) && !session_trx_is_active(session)) ||
session_trx_is_read_only(session))
if (m_pCache->should_use(m_pSession))
{
if (m_pCache->should_store(m_zDefaultDb, pPacket))
GWBUF* pResponse;
cache_result_t result = get_cached_response(pPacket, &pResponse);
if (CACHE_RESULT_IS_OK(result))
{
if (m_pCache->should_use(m_pSession))
if (CACHE_RESULT_IS_STALE(result))
{
GWBUF* pResponse;
cache_result_t result = get_cached_response(pPacket, &pResponse);
// The value was found, but it was stale. Now we need to
// figure out whether somebody else is already fetching it.
if (CACHE_RESULT_IS_OK(result))
if (m_pCache->must_refresh(m_key, this))
{
if (CACHE_RESULT_IS_STALE(result))
// We were the first ones who hit the stale item. It's
// our responsibility now to fetch it.
if (log_decisions())
{
// The value was found, but it was stale. Now we need to
// figure out whether somebody else is already fetching it.
if (m_pCache->must_refresh(m_key, this))
{
// We were the first ones who hit the stale item. It's
// our responsibility now to fetch it.
if (log_decisions())
{
MXS_NOTICE("Cache data is stale, fetching fresh from server.");
}
// As we don't use the response it must be freed.
gwbuf_free(pResponse);
m_refreshing = true;
fetch_from_server = true;
}
else
{
// Somebody is already fetching the new value. So, let's
// use the stale value. No point in hitting the server twice.
if (log_decisions())
{
MXS_NOTICE("Cache data is stale but returning it, fresh "
"data is being fetched already.");
}
fetch_from_server = false;
}
MXS_NOTICE("Cache data is stale, fetching fresh from server.");
}
else
{
if (log_decisions())
{
MXS_NOTICE("Using fresh data from cache.");
}
fetch_from_server = false;
}
}
else
{
// As we don't use the response it must be freed.
gwbuf_free(pResponse);
m_refreshing = true;
fetch_from_server = true;
}
if (fetch_from_server)
{
m_state = CACHE_EXPECTING_RESPONSE;
}
else
{
m_state = CACHE_EXPECTING_NOTHING;
gwbuf_free(pPacket);
DCB *dcb = m_pSession->client_dcb;
// TODO: This is not ok. Any filters before this filter, will not
// TODO: see this data.
rv = dcb->func.write(dcb, pResponse);
// Somebody is already fetching the new value. So, let's
// use the stale value. No point in hitting the server twice.
if (log_decisions())
{
MXS_NOTICE("Cache data is stale but returning it, fresh "
"data is being fetched already.");
}
fetch_from_server = false;
}
}
else
{
if (log_decisions())
{
MXS_NOTICE("Using fresh data from cache.");
}
fetch_from_server = false;
}
}
else
{
m_state = CACHE_IGNORING_RESPONSE;
fetch_from_server = true;
}
}
else
{
if (log_decisions())
if (fetch_from_server)
{
MXS_NOTICE("autocommit = %s and transaction state %s => Not using or "
"storing to cache.",
session_is_autocommit(m_pSession) ? "ON" : "OFF",
session_trx_state_to_string(session_get_trx_state(m_pSession)));
m_state = CACHE_EXPECTING_RESPONSE;
}
else
{
m_state = CACHE_EXPECTING_NOTHING;
gwbuf_free(pPacket);
DCB *dcb = m_pSession->client_dcb;
// TODO: This is not ok. Any filters before this filter, will not
// TODO: see this data.
rv = dcb->func.write(dcb, pResponse);
}
}
}
else
{
m_state = CACHE_IGNORING_RESPONSE;
}
}
break;
@ -664,3 +746,125 @@ void CacheFilterSession::store_result()
m_refreshing = false;
}
}
/**
* Whether the cache should be consulted.
*
* @param pParam The GWBUF being handled.
*
* @return True, if the cache should be consulted, false otherwise.
*/
bool CacheFilterSession::should_consult_cache(GWBUF* pPacket)
{
bool consult_cache = false;
uint32_t type_mask = qc_get_type_mask(pPacket);
const char* zReason = NULL;
if (qc_query_is_type(type_mask, QUERY_TYPE_BEGIN_TRX))
{
// When a transaction is started, we initially assume it is read-only.
m_is_read_only = true;
}
else if (!qc_query_is_type(type_mask, QUERY_TYPE_READ))
{
// Thereafter, if there's any non-read statement we mark it as non-readonly.
// Note that the state of m_is_read_only is not consulted if there is no
// on-going transaction of if there is an explicitly read-only transaction.
m_is_read_only = false;
}
if (!session_trx_is_active(m_pSession))
{
if (log_decisions())
{
zReason = "no transaction";
}
consult_cache = true;
}
else if (session_trx_is_read_only(m_pSession))
{
if (log_decisions())
{
zReason = "explicitly read-only transaction";
}
consult_cache = true;
}
else if (m_is_read_only)
{
if (log_decisions())
{
zReason = "ordinary transaction that has so far been read-only";
}
consult_cache = true;
}
else
{
if (log_decisions())
{
zReason = "ordinary transaction with non-read statements";
}
}
if (consult_cache)
{
if (qc_get_operation(pPacket) == QUERY_OP_SELECT)
{
if (qc_query_is_type(type_mask, QUERY_TYPE_USERVAR_READ))
{
consult_cache = false;
zReason = "user variables are read";
}
else if (qc_query_is_type(type_mask, QUERY_TYPE_SYSVAR_READ))
{
consult_cache = false;
zReason = "system variables are read";
}
else if (uses_non_cacheable_function(pPacket))
{
consult_cache = false;
zReason = "uses non-cacheable function";
}
else if (uses_non_cacheable_variable(pPacket))
{
consult_cache = false;
zReason = "uses non-cacheable variable";
}
}
else
{
consult_cache = false;
zReason = "statement is not SELECT";
}
}
if (log_decisions())
{
char* pSql;
int length;
const int max_length = 40;
// At this point we know it's a COM_QUERY and that the buffer is contiguous
modutil_extract_SQL(pPacket, &pSql, &length);
const char* zFormat;
if (length <= max_length)
{
zFormat = "%s, \"%.*s\", %s.";
}
else
{
zFormat = "%s, \"%.*s...\", %s.";
length = max_length - 3; // strlen("...");
}
const char* zDecision = (consult_cache ? "CONSULT" : "IGNORE ");
ss_dassert(zReason);
MXS_NOTICE(zFormat, zDecision, length, pSql, zReason);
}
return consult_cache;
}

View File

@ -105,6 +105,8 @@ private:
void store_result();
bool should_consult_cache(GWBUF* pPacket);
private:
CacheFilterSession(MXS_SESSION* pSession, Cache* pCache, char* zDefaultDb);
@ -119,5 +121,6 @@ private:
char* m_zDefaultDb; /**< The default database. */
char* m_zUseDb; /**< Pending default database. Needs server response. */
bool m_refreshing; /**< Whether the session is updating a stale cache entry. */
bool m_is_read_only;/**< Whether the current trx has been read-only in pratice. */
};

View File

@ -147,6 +147,7 @@ typedef struct maxrows_response_state
size_t n_fields; /**< How many fields we have received, <= n_totalfields. */
size_t n_rows; /**< How many rows we have received. */
size_t offset; /**< Where we are in the response buffer. */
size_t rows_offset; /**< Offset to first row in result set */
} MAXROWS_RESPONSE_STATE;
static void maxrows_response_state_reset(MAXROWS_RESPONSE_STATE *state);
@ -174,7 +175,7 @@ static int handle_ignoring_response(MAXROWS_SESSION_DATA *csdata);
static bool process_params(char **options, MXS_CONFIG_PARAMETER *params, MAXROWS_CONFIG* config);
static int send_upstream(MAXROWS_SESSION_DATA *csdata);
static int send_ok_upstream(MAXROWS_SESSION_DATA *csdata);
static int send_eof_upstream(MAXROWS_SESSION_DATA *csdata, size_t offset);
/* API BEGIN */
@ -438,6 +439,7 @@ static void maxrows_response_state_reset(MAXROWS_RESPONSE_STATE *state)
state->n_fields = 0;
state->n_rows = 0;
state->offset = 0;
state->rows_offset = 0;
}
/**
@ -511,6 +513,15 @@ static int handle_expecting_fields(MAXROWS_SESSION_DATA *csdata)
{
case 0xfe: // EOF, the one after the fields.
csdata->res.offset += packetlen;
/* Now set the offset to the first resultset
* this could be used for empty response handler
*/
if (!csdata->res.rows_offset)
{
csdata->res.rows_offset = csdata->res.offset;
}
csdata->state = MAXROWS_EXPECTING_ROWS;
rv = handle_rows(csdata);
break;
@ -599,7 +610,7 @@ static int handle_expecting_response(MAXROWS_SESSION_DATA *csdata)
if (csdata->discard_resultset)
{
rv = send_ok_upstream(csdata);
rv = send_eof_upstream(csdata, csdata->res.rows_offset);
csdata->state = MAXROWS_EXPECTING_NOTHING;
}
else
@ -746,7 +757,7 @@ static int handle_rows(MAXROWS_SESSION_DATA *csdata)
// Send data in buffer or empty resultset
if (csdata->discard_resultset)
{
rv = send_ok_upstream(csdata);
rv = send_eof_upstream(csdata, csdata->res.rows_offset);
}
else
{
@ -779,7 +790,7 @@ static int handle_rows(MAXROWS_SESSION_DATA *csdata)
if (packetlen < MYSQL_EOF_PACKET_LEN)
{
MXS_ERROR("EOF packet has size of %lu instead of %d", packetlen, MYSQL_EOF_PACKET_LEN);
rv = send_ok_upstream(csdata);
rv = send_eof_upstream(csdata, csdata->res.rows_offset);
csdata->state = MAXROWS_EXPECTING_NOTHING;
break;
}
@ -800,7 +811,7 @@ static int handle_rows(MAXROWS_SESSION_DATA *csdata)
// Discard data or send data
if (csdata->discard_resultset)
{
rv = send_ok_upstream(csdata);
rv = send_eof_upstream(csdata, csdata->res.rows_offset);
}
else
{
@ -897,23 +908,65 @@ static int send_upstream(MAXROWS_SESSION_DATA *csdata)
}
/**
* Send OK packet data upstream.
* Send upstream the Response Buffer up to columns def in response
* including its EOF of the first result set
* An EOF packet for empty result set with no MULTI flags is added
* at the end.
*
* @param csdata Session data
* @param csdata Session data
* @param offset The offset to server reply pointing to
* next byte after column definitions EOF
* of the first result set.
*
* @return Whatever the upstream returns.
*/
static int send_ok_upstream(MAXROWS_SESSION_DATA *csdata)
static int send_eof_upstream(MAXROWS_SESSION_DATA *csdata, size_t offset)
{
/* Note: sequence id is always 01 (4th byte) */
uint8_t ok[MYSQL_OK_PACKET_MIN_LEN] = {07, 00, 00, 01, 00, 00, 00, 02, 00, 00, 00};
GWBUF *packet = gwbuf_alloc(MYSQL_OK_PACKET_MIN_LEN);
uint8_t *ptr = GWBUF_DATA(packet);
memcpy(ptr, &ok, MYSQL_OK_PACKET_MIN_LEN);
int rv = -1;
/* Sequence byte is #3 */
uint8_t eof[MYSQL_EOF_PACKET_LEN] = {05, 00, 00, 01, 0xfe, 00, 00, 02, 00};
GWBUF *new_pkt = NULL;
ss_dassert(csdata->res.data != NULL);
int rv = csdata->up.clientReply(csdata->up.instance, csdata->up.session, packet);
/* Data to send + added EOF */
uint8_t *new_result = MXS_MALLOC(offset + MYSQL_EOF_PACKET_LEN);
if (new_result)
{
/* Get contiguous data from beginning to specified offset */
gwbuf_copy_data(csdata->res.data, 0, offset, new_result);
/* Increment sequence number for the EOF being added for empty resultset:
* last one if found in EOF terminating column def
*/
eof[3] = new_result[offset - (MYSQL_EOF_PACKET_LEN - 3)] + 1;
/* Copy EOF data */
memcpy(new_result + offset, &eof, MYSQL_EOF_PACKET_LEN);
/* Create new packet */
new_pkt = gwbuf_alloc_and_load(offset + MYSQL_EOF_PACKET_LEN, new_result);
/* Free intermediate data */
MXS_FREE(new_result);
if (new_pkt)
{
/* new_pkt will be freed by write routine */
rv = csdata->up.clientReply(csdata->up.instance, csdata->up.session, new_pkt);
}
}
/* Abort client connection */
if (!(new_result && new_pkt))
{
/* Abort client connection */
poll_fake_hangup_event(csdata->session->client_dcb);
rv = 0;
}
/* Free full input buffer */
gwbuf_free(csdata->res.data);
csdata->res.data = NULL;

View File

@ -1,4 +0,0 @@
add_library(testfilter SHARED testfilter.c)
target_link_libraries(testfilter maxscale-common)
set_target_properties(testfilter PROPERTIES VERSION "1.0.0")
install_module(testfilter core)

View File

@ -1,261 +0,0 @@
/*
* 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: 2019-07-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.
*/
#include <stdio.h>
#include <maxscale/filter.h>
#include <maxscale/alloc.h>
#include <maxscale/modinfo.h>
#include <maxscale/modutil.h>
#include <maxscale/atomic.h>
/**
* @file testfilter.c - a very simple test filter.
* @verbatim
*
* This filter is a very simple example used to test the filter API,
* it merely counts the number of statements that flow through the
* filter pipeline.
*
* Reporting is done via the diagnostics print routine.
* @endverbatim
*/
static MXS_FILTER *createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params);
static MXS_FILTER_SESSION *newSession(MXS_FILTER *instance, MXS_SESSION *session);
static void closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session);
static void freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session);
static void setDownstream(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, MXS_DOWNSTREAM *downstream);
static int routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, GWBUF *queue);
static void diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb);
static uint64_t getCapabilities(MXS_FILTER* instance);
static void destroyInstance(MXS_FILTER *instance);
/**
* A dummy instance structure
*/
typedef struct
{
const char *name;
int sessions;
} TEST_INSTANCE;
/**
* A dummy session structure for this test filter
*/
typedef struct
{
MXS_DOWNSTREAM down;
int count;
} TEST_SESSION;
/**
* 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
* external entry points for this module.
*
* @return The module object
*/
MXS_MODULE* MXS_CREATE_MODULE()
{
static MXS_FILTER_OBJECT MyObject =
{
createInstance,
newSession,
closeSession,
freeSession,
setDownstream,
NULL, // No upstream requirement
routeQuery,
NULL, // No clientReply
diagnostic,
getCapabilities,
destroyInstance,
};
static MXS_MODULE info =
{
MXS_MODULE_API_FILTER,
MXS_MODULE_BETA_RELEASE,
MXS_FILTER_VERSION,
"A simple query counting filter",
"V2.0.0",
&MyObject,
NULL, /* Process init. */
NULL, /* Process finish. */
NULL, /* Thread init. */
NULL, /* Thread finish. */
{
{MXS_END_MODULE_PARAMS}
}
};
return &info;
}
/**
* Create an instance of the filter for a particular service
* within MaxScale.
*
* @param name The name of the instance (as defined in the config file).
* @param options The options for this filter
* @param params The array of name/value pair parameters for the filter
*
* @return The instance data for this new instance
*/
static MXS_FILTER *
createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
{
TEST_INSTANCE *my_instance;
if ((my_instance = MXS_CALLOC(1, sizeof(TEST_INSTANCE))) != NULL)
{
my_instance->sessions = 0;
my_instance->name = name;
}
return (MXS_FILTER *)my_instance;
}
/**
* Associate a new session with this instance of the filter.
*
* @param instance The filter instance data
* @param session The session itself
* @return Session specific data for this session
*/
static MXS_FILTER_SESSION *
newSession(MXS_FILTER *instance, MXS_SESSION *session)
{
TEST_INSTANCE *my_instance = (TEST_INSTANCE *)instance;
TEST_SESSION *my_session;
if ((my_session = MXS_CALLOC(1, sizeof(TEST_SESSION))) != NULL)
{
atomic_add(&my_instance->sessions, 1);
my_session->count = 0;
}
return (MXS_FILTER_SESSION*)my_session;
}
/**
* Close a session with the filter, this is the mechanism
* by which a filter may cleanup data structure etc.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
{
}
/**
* Free the memory associated with this filter session.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
{
MXS_FREE(session);
}
/**
* Set the downstream component for this filter.
*
* @param instance The filter instance data
* @param session The session being closed
* @param downstream The downstream filter or router
*/
static void
setDownstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_DOWNSTREAM *downstream)
{
TEST_SESSION *my_session = (TEST_SESSION *)session;
my_session->down = *downstream;
}
/**
* The routeQuery entry point. This is passed the query buffer
* to which the filter should be applied. Once applied the
* query shoudl normally be passed to the downstream component
* (filter or router) in the filter chain.
*
* @param instance The filter instance data
* @param session The filter session
* @param queue The query data
*/
static int
routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
{
TEST_SESSION *my_session = (TEST_SESSION *)session;
if (modutil_is_SQL(queue))
{
my_session->count++;
}
return my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
}
/**
* Diagnostics routine
*
* If fsession is NULL then print diagnostics on the filter
* instance as a whole, otherwise print diagnostics for the
* particular session.
*
* @param instance The filter instance
* @param fsession Filter session, may be NULL
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb)
{
TEST_INSTANCE *my_instance = (TEST_INSTANCE *)instance;
TEST_SESSION *my_session = (TEST_SESSION *)fsession;
if (my_session)
dcb_printf(dcb, "\t\tNo. of queries routed by filter: %d\n",
my_session->count);
else
dcb_printf(dcb, "\t\tNo. of sessions created: %d\n",
my_instance->sessions);
}
/**
* Capability routine.
*
* @return The capabilities of the filter.
*/
static uint64_t getCapabilities(MXS_FILTER* instance)
{
return RCAP_TYPE_NONE;
}
/**
* destroyInstance routine.
*
* @param The filter instance.
*/
static void destroyInstance(MXS_FILTER *instance)
{
TEST_INSTANCE *cinstance = (TEST_INSTANCE *)instance;
MXS_INFO("Destroying filter %s", cinstance->name);
}

View File

@ -127,7 +127,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
{"multimaster", MXS_MODULE_PARAM_BOOL, "false"},
{"failover", MXS_MODULE_PARAM_BOOL, "false"},
{"failcount", MXS_MODULE_PARAM_COUNT, "5"},
{"failover_recovery", MXS_MODULE_PARAM_BOOL, "false"},
{"failover_recovery", MXS_MODULE_PARAM_BOOL, "true"},
{
"script",
MXS_MODULE_PARAM_PATH,
@ -973,7 +973,7 @@ bool failover_required(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *db)
candidates++;
MYSQL_SERVER_INFO *server_info = hashtable_fetch(handle->server_info, db->server->unique_name);
if (server_info->read_only || candidates > 1)
if (server_info->read_only || server_info->slave_configured || candidates > 1)
{
return false;
}

View File

@ -8,6 +8,3 @@ add_subdirectory(maxscaled)
add_subdirectory(MySQL)
add_subdirectory(telnetd)
if(BUILD_TESTS)
add_subdirectory(testprotocol)
endif()

View File

@ -189,12 +189,10 @@ static int gw_create_backend_connection(DCB *backend_dcb,
/** Copy client flags to backend protocol */
if (backend_dcb->session->client_dcb->protocol)
{
/** Copy client flags to backend protocol */
protocol->client_capabilities =
((MySQLProtocol *)(backend_dcb->session->client_dcb->protocol))->client_capabilities;
/** Copy client charset to backend protocol */
protocol->charset =
((MySQLProtocol *)(backend_dcb->session->client_dcb->protocol))->charset;
MySQLProtocol *client = (MySQLProtocol*)backend_dcb->session->client_dcb->protocol;
protocol->client_capabilities = client->client_capabilities;
protocol->charset = client->charset;
protocol->extra_capabilities = client->extra_capabilities;
}
else
{

View File

@ -224,16 +224,25 @@ int MySQLSendHandshake(DCB* dcb)
uint8_t mysql_server_language = 8;
uint8_t mysql_server_status[2];
uint8_t mysql_scramble_len = 21;
uint8_t mysql_filler_ten[10];
uint8_t mysql_filler_ten[10] = {};
/* uint8_t mysql_last_byte = 0x00; not needed */
char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1] = "";
char *version_string;
int len_version_string = 0;
int id_num;
bool is_maria = false;
if (dcb->service->dbref)
{
mysql_server_language = dcb->service->dbref->server->charset;
if (dcb->service->dbref->server->server_string &&
strstr(dcb->service->dbref->server->server_string, "10.2."))
{
/** The backend servers support the extended capabilities */
is_maria = true;
}
}
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
@ -256,9 +265,15 @@ int MySQLSendHandshake(DCB* dcb)
// copy back to the caller
memcpy(protocol->scramble, server_scramble, GW_MYSQL_SCRAMBLE_SIZE);
// fill the handshake packet
memset(mysql_filler_ten, 0x00, sizeof(mysql_filler_ten));
if (is_maria)
{
/**
* The new 10.2 capability flags are stored in the last 4 bytes of the
* 10 byte filler block.
*/
uint32_t new_flags = MXS_MARIA_CAP_STMT_BULK_OPERATIONS;
memcpy(mysql_filler_ten + 6, &new_flags, sizeof(new_flags));
}
// thread id, now put thePID
id_num = getpid() + dcb->fd;
@ -324,11 +339,19 @@ int MySQLSendHandshake(DCB* dcb)
mysql_handshake_payload++;
// write server capabilities part one
mysql_server_capabilities_one[0] = GW_MYSQL_SERVER_CAPABILITIES_BYTE1;
mysql_server_capabilities_one[1] = GW_MYSQL_SERVER_CAPABILITIES_BYTE2;
mysql_server_capabilities_one[0] = (uint8_t)GW_MYSQL_CAPABILITIES_SERVER;
mysql_server_capabilities_one[1] = (uint8_t)(GW_MYSQL_CAPABILITIES_SERVER >> 8);
// Check that we match the old values
ss_dassert(mysql_server_capabilities_one[0] = 0xff);
ss_dassert(mysql_server_capabilities_one[1] = 0xf7);
mysql_server_capabilities_one[0] &= ~(int)GW_MYSQL_CAPABILITIES_COMPRESS;
if (is_maria)
{
/** A MariaDB 10.2 server doesn't send the CLIENT_MYSQL capability
* to signal that it supports extended capabilities */
mysql_server_capabilities_one[0] &= ~(uint8_t)GW_MYSQL_CAPABILITIES_CLIENT_MYSQL;
}
if (ssl_required_by_dcb(dcb))
{
@ -349,8 +372,13 @@ int MySQLSendHandshake(DCB* dcb)
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_status);
//write server capabilities part two
mysql_server_capabilities_two[0] = 15;
mysql_server_capabilities_two[1] = 128;
mysql_server_capabilities_two[0] = (uint8_t)(GW_MYSQL_CAPABILITIES_SERVER >> 16);
mysql_server_capabilities_two[1] = (uint8_t)(GW_MYSQL_CAPABILITIES_SERVER >> 24);
// Check that we match the old values
ss_dassert(mysql_server_capabilities_two[0] == 15);
/** NOTE: pre-2.1 versions sent the fourth byte of the capabilities as
the value 128 even though there's no such capability. */
memcpy(mysql_handshake_payload, mysql_server_capabilities_two, sizeof(mysql_server_capabilities_two));
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_capabilities_two);
@ -531,6 +559,13 @@ static void store_client_information(DCB *dcb, GWBUF *buffer)
proto->client_capabilities = gw_mysql_get_byte4(data + MYSQL_CLIENT_CAP_OFFSET);
proto->charset = data[MYSQL_CHARSET_OFFSET];
/** MariaDB 10.2 compatible clients don't set the first bit to signal that
* there are extra capabilities stored in the last 4 bytes of the 23 byte filler. */
if ((proto->client_capabilities & GW_MYSQL_CAPABILITIES_CLIENT_MYSQL) == 0)
{
proto->extra_capabilities = gw_mysql_get_byte4(data + MARIADB_CAP_OFFSET);
}
if (len > MYSQL_AUTH_PACKET_BASE_SIZE)
{
strcpy(ses->user, (char*)data + MYSQL_AUTH_PACKET_BASE_SIZE);
@ -825,7 +860,12 @@ static bool process_client_commands(DCB* dcb, int bytes_available, GWBUF** buffe
}
MySQLProtocol *proto = (MySQLProtocol*)dcb->protocol;
proto->current_command = cmd;
if (dcb->protocol_packet_length - MYSQL_HEADER_LEN != GW_MYSQL_MAX_PACKET_LEN)
{
/** We're processing the first packet of a command */
proto->current_command = cmd;
}
dcb->protocol_packet_length = pktlen + MYSQL_HEADER_LEN;
dcb->protocol_bytes_processed = 0;
}

View File

@ -103,6 +103,7 @@ MySQLProtocol* mysql_protocol_init(DCB* dcb, int fd)
p->protocol_command.scom_nresponse_packets = 0;
p->protocol_command.scom_nbytes_to_read = 0;
p->stored_query = NULL;
p->extra_capabilities = 0;
#if defined(SS_DEBUG)
p->protocol_chk_top = CHK_NUM_PROTOCOL;
p->protocol_chk_tail = CHK_NUM_PROTOCOL;
@ -1364,8 +1365,12 @@ mxs_auth_state_t gw_send_backend_auth(DCB *dcb)
payload++;
// 23 bytes of 0
payload += 23;
// 19 filler bytes of 0
payload += 19;
// Either MariaDB 10.2 extra capabilities or 4 bytes filler
memcpy(payload, &conn->extra_capabilities, sizeof(conn->extra_capabilities));
payload += 4;
if (dcb->server->server_ssl && dcb->ssl_state != SSL_ESTABLISHED)
{

View File

@ -1,3 +0,0 @@
add_library(testprotocol SHARED testprotocol.c)
set_target_properties(testprotocol PROPERTIES VERSION "1.0.0")
install_module(testprotocol core)

View File

@ -1,131 +0,0 @@
/*
* 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: 2019-07-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 testprotocol.c - Testing protocol module
*
* Not intended for actual use. This protocol module does nothing useful and
* is only meant to test that the module loading works.
*
* @verbatim
* Revision History
* Date Who Description
* 20/02/2015 Markus Mäkelä Initial implementation
*
* @endverbatim
*/
#include <maxscale/modinfo.h>
#include <maxscale/dcb.h>
#include <maxscale/buffer.h>
#include <maxscale/protocol.h>
static int test_read(DCB* dcb)
{
return 1;
}
static int test_write(DCB *dcb, GWBUF* buf)
{
return 1;
}
static int test_write_ready(DCB *dcb)
{
return 1;
}
static int test_error(DCB *dcb)
{
return 1;
}
static int test_hangup(DCB *dcb)
{
return 1;
}
static int test_accept(DCB *dcb)
{
return 1;
}
static int test_connect(struct dcb *dcb, struct server *srv, struct session *ses)
{
return 1;
}
static int test_close(DCB *dcb)
{
return 1;
}
static int test_listen(DCB *dcb, char *config)
{
return 1;
}
static int test_auth(DCB* dcb, struct server *srv, struct session *ses, GWBUF *buf)
{
return 1;
}
static int test_session(DCB *dcb, void* data)
{
return 1;
}
static char *test_default_auth()
{
return "NullAuthAllow";
}
static int test_connection_limit(DCB *dcb, int limit)
{
return 0;
}
/**
* 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
* external entry points for this module.
*
* @return The module object
*/
MXS_MODULE* MXS_CREATE_MODULE()
{
static MXS_PROTOCOL MyObject =
{
test_read, /**< Read - EPOLLIN handler */
test_write, /**< Write - data from gateway */
test_write_ready, /**< WriteReady - EPOLLOUT handler */
test_error, /**< Error - EPOLLERR handler */
test_hangup, /**< HangUp - EPOLLHUP handler */
test_accept, /**< Accept */
test_connect, /**< Connect */
test_close, /**< Close */
test_listen, /**< Create a listener */
test_auth, /**< Authentication */
test_session, /**< Session */
test_default_auth, /**< Default authenticator */
test_connection_limit /**< Connection limit */
};
static MXS_MODULE info =
{
MXS_MODULE_API_PROTOCOL,
MXS_MODULE_IN_DEVELOPMENT,
MXS_PROTOCOL_VERSION,
"Test protocol",
"V1.1.0",
&MyObject,
NULL, /* Process init. */
NULL, /* Process finish. */
NULL, /* Thread init. */
NULL, /* Thread finish. */
{
{MXS_END_MODULE_PARAMS}
}
};
return &info;
}

View File

@ -12,6 +12,4 @@ add_subdirectory(readconnroute)
add_subdirectory(readwritesplit)
add_subdirectory(schemarouter)
if(BUILD_TESTS)
add_subdirectory(testroute)
endif()

View File

@ -72,14 +72,14 @@ static const char* alter_table_regex =
/* The router entry points */
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *router_session);
static void freeSession(MXS_ROUTER *instance, void *router_session);
static int routeQuery(MXS_ROUTER *instance, void *router_session, GWBUF *queue);
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static int routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static void clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *queue,
static void clientReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue,
DCB *backend_dcb);
static void errorReply(MXS_ROUTER *instance, void *router_session, GWBUF *message,
static void errorReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *message,
DCB *backend_dcb, mxs_error_action_t action, bool *succp);
static uint64_t getCapabilities(MXS_ROUTER* instance);
extern int MaxScaleUptime();
@ -630,7 +630,7 @@ createInstance(SERVICE *service, char **options)
* @param session The session itself
* @return Session specific data for this session
*/
static void *
static MXS_ROUTER_SESSION *
newSession(MXS_ROUTER *instance, MXS_SESSION *session)
{
AVRO_INSTANCE *inst = (AVRO_INSTANCE *) instance;
@ -703,7 +703,7 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
* @param router_cli_ses The particular session to free
*
*/
static void freeSession(MXS_ROUTER* router_instance, void* router_client_ses)
static void freeSession(MXS_ROUTER* router_instance, MXS_ROUTER_SESSION* router_client_ses)
{
AVRO_INSTANCE *router = (AVRO_INSTANCE *) router_instance;
AVRO_CLIENT *client = (AVRO_CLIENT *) router_client_ses;
@ -752,7 +752,7 @@ static void freeSession(MXS_ROUTER* router_instance, void* router_client_ses)
* @param instance The router instance data
* @param router_session The session being closed
*/
static void closeSession(MXS_ROUTER *instance, void *router_session)
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
{
AVRO_INSTANCE *router = (AVRO_INSTANCE *) instance;
AVRO_CLIENT *client = (AVRO_CLIENT *) router_session;
@ -784,7 +784,7 @@ static void closeSession(MXS_ROUTER *instance, void *router_session)
* @return 1 on success, 0 on error
*/
static int
routeQuery(MXS_ROUTER *instance, void *router_session, GWBUF *queue)
routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue)
{
AVRO_INSTANCE *router = (AVRO_INSTANCE *) instance;
AVRO_CLIENT *client = (AVRO_CLIENT *) router_session;
@ -960,7 +960,7 @@ diagnostics(MXS_ROUTER *router, DCB *dcb)
* @param queue The GWBUF with reply data
*/
static void
clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *queue, DCB *backend_dcb)
clientReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue, DCB *backend_dcb)
{
/** We should never end up here */
ss_dassert(false);
@ -1002,7 +1002,7 @@ extract_message(GWBUF *errpkt)
*
*/
static void
errorReply(MXS_ROUTER *instance, void *router_session, GWBUF *message, DCB *backend_dcb,
errorReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *message, DCB *backend_dcb,
mxs_error_action_t action,
bool *succp)
{

View File

@ -30,7 +30,7 @@ static bool warn_large_enumset = false; /**< Remove when support for ENUM/SET va
uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create,
avro_value_t *record, uint8_t *ptr,
uint8_t *columns_present);
uint8_t *columns_present, uint8_t *end);
void notify_all_clients(AVRO_INSTANCE *router);
void add_used_table(AVRO_INSTANCE* router, const char* table);
@ -309,9 +309,10 @@ bool handle_row_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr)
while (ptr - start < hdr->event_size - BINLOG_EVENT_HDR_LEN)
{
/** Add the current GTID and timestamp */
uint8_t *end = ptr + hdr->event_size;
int event_type = get_event_type(hdr->event_type);
prepare_record(router, hdr, event_type, &record);
ptr = process_row_event_data(map, create, &record, ptr, col_present);
ptr = process_row_event_data(map, create, &record, ptr, col_present, end);
avro_file_writer_append_value(table->avro_file, &record);
/** Update rows events have the before and after images of the
@ -320,7 +321,7 @@ bool handle_row_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr)
if (event_type == UPDATE_EVENT)
{
prepare_record(router, hdr, UPDATE_EVENT_AFTER, &record);
ptr = process_row_event_data(map, create, &record, ptr, col_present);
ptr = process_row_event_data(map, create, &record, ptr, col_present, end);
avro_file_writer_append_value(table->avro_file, &record);
}
@ -497,7 +498,7 @@ int get_metadata_len(uint8_t type)
* @return Pointer to the first byte after the current row event
*/
uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value_t *record,
uint8_t *ptr, uint8_t *columns_present)
uint8_t *ptr, uint8_t *columns_present, uint8_t *end)
{
int npresent = 0;
avro_value_t field;
@ -507,10 +508,12 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
/** BIT type values use the extra bits in the row event header */
int extra_bits = (((ncolumns + 7) / 8) * 8) - ncolumns;
ss_dassert(ptr < end);
/** Store the null value bitmap */
uint8_t *null_bitmap = ptr;
ptr += (ncolumns + 7) / 8;
ss_dassert(ptr < end);
for (long i = 0; i < map->columns && npresent < ncolumns; i++)
{
@ -544,6 +547,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
}
avro_value_set_string(&field, strval);
ptr += bytes;
ss_dassert(ptr < end);
}
else
{
@ -553,6 +557,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
str[bytes] = '\0';
avro_value_set_string(&field, str);
ptr += bytes + 1;
ss_dassert(ptr < end);
}
}
else if (column_is_bit(map->column_types[i]))
@ -572,21 +577,36 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
}
avro_value_set_int(&field, value);
ptr += bytes;
ss_dassert(ptr < end);
}
else if (column_is_decimal(map->column_types[i]))
{
double f_value = 0.0;
ptr += unpack_decimal_field(ptr, metadata + metadata_offset, &f_value);
avro_value_set_double(&field, f_value);
ss_dassert(ptr < end);
}
else if (column_is_variable_string(map->column_types[i]))
{
size_t sz;
char *str = mxs_lestr_consume(&ptr, &sz);
int bytes = metadata[metadata_offset] | metadata[metadata_offset + 1] << 8;
if (bytes > 255)
{
sz = gw_mysql_get_byte2(ptr);
ptr += 2;
}
else
{
sz = *ptr;
ptr++;
}
char buf[sz + 1];
memcpy(buf, str, sz);
memcpy(buf, ptr, sz);
buf[sz] = '\0';
ptr += sz;
avro_value_set_string(&field, buf);
ss_dassert(ptr < end);
}
else if (column_is_blob(map->column_types[i]))
{
@ -596,6 +616,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
ptr += bytes;
avro_value_set_bytes(&field, ptr, len);
ptr += len;
ss_dassert(ptr < end);
}
else if (column_is_temporal(map->column_types[i]))
{
@ -604,6 +625,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
ptr += unpack_temporal_value(map->column_types[i], ptr, &metadata[metadata_offset], &tm);
format_temporal_value(buf, sizeof(buf), map->column_types[i], &tm);
avro_value_set_string(&field, buf);
ss_dassert(ptr < end);
}
/** All numeric types (INT, LONG, FLOAT etc.) */
else
@ -613,6 +635,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
ptr += unpack_numeric_field(ptr, map->column_types[i],
&metadata[metadata_offset], lval);
set_numeric_field_value(&field, map->column_types[i], &metadata[metadata_offset], lval);
ss_dassert(ptr < end);
}
ss_dassert(metadata_offset <= map->column_metadata_size);
metadata_offset += get_metadata_len(map->column_types[i]);

View File

@ -449,6 +449,10 @@ static const char *extract_field_name(const char* ptr, char* dest, size_t size)
while (*ptr && (isspace(*ptr) || (bt = *ptr == '`')))
{
ptr++;
if (bt)
{
break;
}
}
if (strncasecmp(ptr, "constraint", 10) == 0 || strncasecmp(ptr, "index", 5) == 0 ||
@ -481,11 +485,6 @@ static const char *extract_field_name(const char* ptr, char* dest, size_t size)
/** Valid identifier */
size_t bytes = ptr - start;
if (bt)
{
bytes--;
}
memcpy(dest, start, bytes);
dest[bytes] = '\0';

View File

@ -90,21 +90,21 @@
/* The router entry points */
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void free_instance(ROUTER_INSTANCE *instance);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *router_session);
static void freeSession(MXS_ROUTER *instance, void *router_session);
static int routeQuery(MXS_ROUTER *instance, void *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static void clientReply(MXS_ROUTER *instance,
void *router_session,
GWBUF *queue,
DCB *backend_dcb);
static void errorReply(MXS_ROUTER *instance,
void *router_session,
GWBUF *message,
DCB *backend_dcb,
mxs_error_action_t action,
bool *succp);
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static int routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static void clientReply(MXS_ROUTER *instance,
MXS_ROUTER_SESSION *router_session,
GWBUF *queue,
DCB *backend_dcb);
static void errorReply(MXS_ROUTER *instance,
MXS_ROUTER_SESSION *router_session,
GWBUF *message,
DCB *backend_dcb,
mxs_error_action_t action,
bool *succp);
static uint64_t getCapabilities(MXS_ROUTER* instance);
static int blr_handler_config(void *userdata, const char *section, const char *name, const char *value);
@ -952,7 +952,7 @@ free_instance(ROUTER_INSTANCE *instance)
* @param session The session itself
* @return Session specific data for this session
*/
static void *
static MXS_ROUTER_SESSION *
newSession(MXS_ROUTER *instance, MXS_SESSION *session)
{
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance;
@ -1022,7 +1022,7 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
*
*/
static void freeSession(MXS_ROUTER* router_instance,
void* router_client_ses)
MXS_ROUTER_SESSION* router_client_ses)
{
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_instance;
ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_client_ses;
@ -1090,7 +1090,7 @@ static void freeSession(MXS_ROUTER* router_instance,
* @param router_session The session being closed
*/
static void
closeSession(MXS_ROUTER *instance, void *router_session)
closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
{
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session;
@ -1176,7 +1176,7 @@ closeSession(MXS_ROUTER *instance, void *router_session)
* @return The number of bytes sent
*/
static int
routeQuery(MXS_ROUTER *instance, void *router_session, GWBUF *queue)
routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue)
{
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session;
@ -1680,7 +1680,7 @@ diagnostics(MXS_ROUTER *router, DCB *dcb)
* @param queue The GWBUF with reply data
*/
static void
clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *queue, DCB *backend_dcb)
clientReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue, DCB *backend_dcb)
{
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
@ -1726,7 +1726,7 @@ extract_message(GWBUF *errpkt)
*/
static void
errorReply(MXS_ROUTER *instance,
void *router_session,
MXS_ROUTER_SESSION *router_session,
GWBUF *message,
DCB *backend_dcb,
mxs_error_action_t action,

View File

@ -2463,6 +2463,7 @@ blr_file_write_master_config(ROUTER_INSTANCE *router, char *error)
if (chmod(tmp_file, S_IRUSR | S_IWUSR) < 0)
{
fclose(config_file);
snprintf(error, BINLOG_ERROR_MSG_LEN, "%s, errno %u",
strerror_r(errno, err_msg, sizeof(err_msg)), errno);
return 2;

View File

@ -44,11 +44,11 @@
/* The router entry points */
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *router_session);
static void freeSession(MXS_ROUTER *instance, void *router_session);
static int execute(MXS_ROUTER *instance, void *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static int execute(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static uint64_t getCapabilities(MXS_ROUTER* instance);
extern int execute_cmd(CLI_SESSION *cli);
@ -156,7 +156,7 @@ createInstance(SERVICE *service, char **options)
* @param session The session itself
* @return Session specific data for this session
*/
static void *
static MXS_ROUTER_SESSION *
newSession(MXS_ROUTER *instance, MXS_SESSION *session)
{
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
@ -188,7 +188,7 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
* @param router_session The session being closed
*/
static void
closeSession(MXS_ROUTER *instance, void *router_session)
closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
{
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
CLI_SESSION *session = (CLI_SESSION *)router_session;
@ -224,9 +224,8 @@ closeSession(MXS_ROUTER *instance, void *router_session)
* @param router_instance The router session
* @param router_client_session The router session as returned from newSession
*/
static void freeSession(
MXS_ROUTER* router_instance,
void* router_client_session)
static void freeSession(MXS_ROUTER* router_instance,
MXS_ROUTER_SESSION* router_client_session)
{
MXS_FREE(router_client_session);
return;
@ -243,7 +242,7 @@ static void freeSession(
* @return The number of bytes sent
*/
static int
execute(MXS_ROUTER *instance, void *router_session, GWBUF *queue)
execute(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue)
{
CLI_SESSION *session = (CLI_SESSION *)router_session;

View File

@ -43,11 +43,11 @@
/* The router entry points */
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *router_session);
static void freeSession(MXS_ROUTER *instance, void *router_session);
static int execute(MXS_ROUTER *instance, void *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static int execute(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static uint64_t getCapabilities(MXS_ROUTER* instance);
extern int execute_cmd(CLI_SESSION *cli);
@ -147,7 +147,7 @@ createInstance(SERVICE *service, char **options)
* @param session The session itself
* @return Session specific data for this session
*/
static void *
static MXS_ROUTER_SESSION *
newSession(MXS_ROUTER *instance, MXS_SESSION *session)
{
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
@ -182,7 +182,7 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
* @param router_session The session being closed
*/
static void
closeSession(MXS_ROUTER *instance, void *router_session)
closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
{
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
CLI_SESSION *session = (CLI_SESSION *)router_session;
@ -218,9 +218,8 @@ closeSession(MXS_ROUTER *instance, void *router_session)
* @param router_instance The router session
* @param router_client_session The router session as returned from newSession
*/
static void freeSession(
MXS_ROUTER* router_instance,
void* router_client_session)
static void freeSession(MXS_ROUTER* router_instance,
MXS_ROUTER_SESSION* router_client_session)
{
MXS_FREE(router_client_session);
return;
@ -237,7 +236,7 @@ static void freeSession(
* @return The number of bytes sent
*/
static int
execute(MXS_ROUTER *instance, void *router_session, GWBUF *queue)
execute(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue)
{
CLI_SESSION *session = (CLI_SESSION *)router_session;

View File

@ -65,18 +65,18 @@ static int handle_url(INFO_INSTANCE *instance, INFO_SESSION *router_session, GWB
/* The router entry points */
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *router_session);
static void freeSession(MXS_ROUTER *instance, void *router_session);
static int execute(MXS_ROUTER *instance, void *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static int execute(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static uint64_t getCapabilities(MXS_ROUTER* instance);
static void handleError(MXS_ROUTER *instance,
void *router_session,
GWBUF *errbuf,
DCB *backend_dcb,
mxs_error_action_t action,
bool *succp);
static void handleError(MXS_ROUTER *instance,
MXS_ROUTER_SESSION *router_session,
GWBUF *errbuf,
DCB *backend_dcb,
mxs_error_action_t action,
bool *succp);
static SPINLOCK instlock;
static INFO_INSTANCE *instances;
@ -180,7 +180,7 @@ createInstance(SERVICE *service, char **options)
* @param session The session itself
* @return Session specific data for this session
*/
static void *
static MXS_ROUTER_SESSION *
newSession(MXS_ROUTER *instance, MXS_SESSION *session)
{
INFO_INSTANCE *inst = (INFO_INSTANCE *)instance;
@ -212,7 +212,7 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
* @param router_session The session being closed
*/
static void
closeSession(MXS_ROUTER *instance, void *router_session)
closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
{
INFO_INSTANCE *inst = (INFO_INSTANCE *)instance;
INFO_SESSION *session = (INFO_SESSION *)router_session;
@ -249,7 +249,7 @@ closeSession(MXS_ROUTER *instance, void *router_session)
* @param router_client_session The router session as returned from newSession
*/
static void freeSession(MXS_ROUTER* router_instance,
void* router_client_session)
MXS_ROUTER_SESSION* router_client_session)
{
MXS_FREE(router_client_session);
return;
@ -268,12 +268,12 @@ static void freeSession(MXS_ROUTER* router_instance,
* @param succp Result of action: true iff router can continue
*
*/
static void handleError(MXS_ROUTER *instance,
void *router_session,
GWBUF *errbuf,
DCB *backend_dcb,
static void handleError(MXS_ROUTER *instance,
MXS_ROUTER_SESSION *router_session,
GWBUF *errbuf,
DCB *backend_dcb,
mxs_error_action_t action,
bool *succp)
bool *succp)
{
DCB *client_dcb;
@ -316,7 +316,7 @@ static void handleError(MXS_ROUTER *instance,
* @return The number of bytes sent
*/
static int
execute(MXS_ROUTER *rinstance, void *router_session, GWBUF *queue)
execute(MXS_ROUTER *rinstance, MXS_ROUTER_SESSION *router_session, GWBUF *queue)
{
INFO_INSTANCE *instance = (INFO_INSTANCE *)rinstance;
INFO_SESSION *session = (INFO_SESSION *)router_session;

View File

@ -89,14 +89,14 @@
/* The router entry points */
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *router_session);
static void freeSession(MXS_ROUTER *instance, void *router_session);
static int routeQuery(MXS_ROUTER *instance, void *router_session, GWBUF *queue);
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
static int routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static void clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *queue,
static void clientReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue,
DCB *backend_dcb);
static void handleError(MXS_ROUTER *instance, void *router_session, GWBUF *errbuf,
static void handleError(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *errbuf,
DCB *problem_dcb, mxs_error_action_t action, bool *succp);
static uint64_t getCapabilities(MXS_ROUTER* instance);
static bool rses_begin_locked_router_action(ROUTER_CLIENT_SES* rses);
@ -257,7 +257,7 @@ createInstance(SERVICE *service, char **options)
* @param session The session itself
* @return Session specific data for this session
*/
static void *
static MXS_ROUTER_SESSION *
newSession(MXS_ROUTER *instance, MXS_SESSION *session)
{
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *) instance;
@ -450,7 +450,7 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
* @details (write detailed description here)
*
*/
static void freeSession(MXS_ROUTER* router_instance, void* router_client_ses)
static void freeSession(MXS_ROUTER* router_instance, MXS_ROUTER_SESSION* router_client_ses)
{
ROUTER_INSTANCE* router = (ROUTER_INSTANCE *) router_instance;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *) router_client_ses;
@ -469,7 +469,7 @@ static void freeSession(MXS_ROUTER* router_instance, void* router_client_ses)
* @param router_session The session being closed
*/
static void
closeSession(MXS_ROUTER *instance, void *router_session)
closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
{
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *) router_session;
DCB* backend_dcb;
@ -537,7 +537,7 @@ static void log_closed_session(mysql_server_cmd_t mysql_command, bool is_closed,
* @return if succeed 1, otherwise 0
*/
static int
routeQuery(MXS_ROUTER *instance, void *router_session, GWBUF *queue)
routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue)
{
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *) instance;
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *) router_session;
@ -656,7 +656,7 @@ diagnostics(MXS_ROUTER *router, DCB *dcb)
* @param queue The GWBUF with reply data
*/
static void
clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *queue, DCB *backend_dcb)
clientReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue, DCB *backend_dcb)
{
ss_dassert(backend_dcb->session->client_dcb != NULL);
MXS_SESSION_ROUTE_REPLY(backend_dcb->session, queue);
@ -675,7 +675,7 @@ clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *queue, DCB *backe
* @param succp Result of action: true if router can continue
*
*/
static void handleError(MXS_ROUTER *instance, void *router_session, GWBUF *errbuf,
static void handleError(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *errbuf,
DCB *problem_dcb, mxs_error_action_t action, bool *succp)
{

View File

@ -69,14 +69,14 @@
*/
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *session);
static void freeSession(MXS_ROUTER *instance, void *session);
static int routeQuery(MXS_ROUTER *instance, void *session, GWBUF *queue);
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *session);
static int routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *session, GWBUF *queue);
static void diagnostics(MXS_ROUTER *instance, DCB *dcb);
static void clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *queue,
static void clientReply(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue,
DCB *backend_dcb);
static void handleError(MXS_ROUTER *instance, void *router_session,
static void handleError(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session,
GWBUF *errmsgbuf, DCB *backend_dcb,
mxs_error_action_t action, bool *succp);
static uint64_t getCapabilities(MXS_ROUTER* instance);
@ -250,7 +250,6 @@ static MXS_ROUTER *createInstance(SERVICE *service, char **options)
return NULL;
}
router->service = service;
spinlock_init(&router->lock);
/*
* Until we know otherwise assume we have some available slaves.
@ -313,7 +312,7 @@ static MXS_ROUTER *createInstance(SERVICE *service, char **options)
* @param session The MaxScale session (generic connection data)
* @return Session specific data for this session, i.e. a router session
*/
static void *newSession(MXS_ROUTER *router_inst, MXS_SESSION *session)
static MXS_ROUTER_SESSION *newSession(MXS_ROUTER *router_inst, MXS_SESSION *session)
{
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_inst;
ROUTER_CLIENT_SES *client_rses = (ROUTER_CLIENT_SES *)MXS_CALLOC(1, sizeof(ROUTER_CLIENT_SES));
@ -331,7 +330,6 @@ static void *newSession(MXS_ROUTER *router_inst, MXS_SESSION *session)
client_rses->client_dcb = session->client_dcb;
client_rses->have_tmp_tables = false;
client_rses->forced_node = NULL;
spinlock_init(&client_rses->rses_lock);
memcpy(&client_rses->rses_config, &router->rwsplit_config, sizeof(client_rses->rses_config));
int router_nservers = router->service->n_dbref;
@ -404,12 +402,12 @@ static void *newSession(MXS_ROUTER *router_inst, MXS_SESSION *session)
* @param instance The router instance data
* @param session The router session being closed
*/
static void closeSession(MXS_ROUTER *instance, void *router_session)
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
{
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
CHK_CLIENT_RSES(router_cli_ses);
if (!router_cli_ses->rses_closed && rses_begin_locked_router_action(router_cli_ses))
if (!router_cli_ses->rses_closed)
{
/**
* Mark router session as closed. @c rses_closed is checked at the start
@ -465,8 +463,6 @@ static void closeSession(MXS_ROUTER *instance, void *router_session)
}
}
}
rses_end_locked_router_action(router_cli_ses);
}
}
@ -480,7 +476,7 @@ static void closeSession(MXS_ROUTER *instance, void *router_session)
* @param router_client_session Client session
*
*/
static void freeSession(MXS_ROUTER *router_instance, void *router_client_session)
static void freeSession(MXS_ROUTER *router_instance, MXS_ROUTER_SESSION *router_client_session)
{
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session;
@ -553,7 +549,7 @@ void close_failed_bref(backend_ref_t *bref, bool fatal)
* @param querybuf Buffer containing the query
* @return 1 on success, 0 on error
*/
static int routeQuery(MXS_ROUTER *instance, void *router_session, GWBUF *querybuf)
static int routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *querybuf)
{
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *) instance;
ROUTER_CLIENT_SES *rses = (ROUTER_CLIENT_SES *) router_session;
@ -665,34 +661,27 @@ static void diagnostics(MXS_ROUTER *instance, DCB *dcb)
* @param backend_dcb The backend DCB
* @param queue The GWBUF with reply data
*/
static void clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *writebuf,
static void clientReply(MXS_ROUTER *instance,
MXS_ROUTER_SESSION *router_session,
GWBUF *writebuf,
DCB *backend_dcb)
{
DCB *client_dcb;
ROUTER_INSTANCE *router_inst;
ROUTER_CLIENT_SES *router_cli_ses;
sescmd_cursor_t *scur = NULL;
backend_ref_t *bref;
ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
ROUTER_INSTANCE *router_inst = (ROUTER_INSTANCE *)instance;
DCB *client_dcb = backend_dcb->session->client_dcb;
router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
router_inst = (ROUTER_INSTANCE *)instance;
CHK_CLIENT_RSES(router_cli_ses);
/**
* Lock router client session for secure read of router session members.
* Note that this could be done without lock by using version #
*/
if (!rses_begin_locked_router_action(router_cli_ses))
if (router_cli_ses->rses_closed)
{
gwbuf_free(writebuf);
goto lock_failed;
return;
}
/** Holding lock ensures that router session remains open */
ss_dassert(backend_dcb->session != NULL);
client_dcb = backend_dcb->session->client_dcb;
/** Unlock */
rses_end_locked_router_action(router_cli_ses);
/**
* 1. Check if backend received reply to sescmd.
* 2. Check sescmd's state whether OK_PACKET has been
@ -702,32 +691,10 @@ static void clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *write
* 3. If reply for this sescmd is sent, lock property cursor
* and
*/
if (client_dcb == NULL)
{
gwbuf_free(writebuf);
/** Log that client was closed before reply */
goto lock_failed;
}
/** Lock router session */
if (!rses_begin_locked_router_action(router_cli_ses))
{
/** Log to debug that router was closed */
goto lock_failed;
}
bref = get_bref_from_dcb(router_cli_ses, backend_dcb);
#if !defined(FOR_BUG548_FIX_ONLY)
/** This makes the issue becoming visible in poll.c */
if (bref == NULL)
{
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
goto lock_failed;
}
#endif
backend_ref_t *bref = get_bref_from_dcb(router_cli_ses, backend_dcb);
CHK_BACKEND_REF(bref);
scur = &bref->bref_sescmd_cur;
sescmd_cursor_t *scur = &bref->bref_sescmd_cur;
/** Statement was successfully executed, free the stored statement */
session_clear_stmt(backend_dcb->session);
@ -788,15 +755,7 @@ static void clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *write
/** Write reply to client DCB */
MXS_SESSION_ROUTE_REPLY(backend_dcb->session, writebuf);
}
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
/** Lock router session */
if (!rses_begin_locked_router_action(router_cli_ses))
{
/** Log to debug that router was closed */
goto lock_failed;
}
/** There is one pending session command to be executed. */
if (sescmd_cursor_is_active(scur))
{
@ -847,11 +806,6 @@ static void clientReply(MXS_ROUTER *instance, void *router_session, GWBUF *write
gwbuf_free(bref->bref_pending_cmd);
bref->bref_pending_cmd = NULL;
}
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
lock_failed:
return;
}
@ -876,64 +830,6 @@ static uint64_t getCapabilities(MXS_ROUTER* instance)
* functions are not intended for use outside the read write split router.
*/
/**
* @brief Acquires lock to router client session if it is not closed.
*
* Parameters:
* @param rses - in, use
*
*
* @return true if router session was not closed. If return value is true
* it means that router is locked, and must be unlocked later. False, if
* router was closed before lock was acquired.
*
*/
bool rses_begin_locked_router_action(ROUTER_CLIENT_SES *rses)
{
bool succp = false;
if (rses == NULL)
{
return false;
}
CHK_CLIENT_RSES(rses);
if (rses->rses_closed)
{
goto return_succp;
}
spinlock_acquire(&rses->rses_lock);
if (rses->rses_closed)
{
spinlock_release(&rses->rses_lock);
goto return_succp;
}
succp = true;
return_succp:
return succp;
}
/** to be inline'd */
/**
* @brief Releases router client session lock.
*
* Parameters:
* @param rses - <usage>
* <description>
*
* @return void
*
*/
void rses_end_locked_router_action(ROUTER_CLIENT_SES *rses)
{
CHK_CLIENT_RSES(rses);
spinlock_release(&rses->rses_lock);
}
/*
* @brief Clear one or more bits in the backend reference state
*
@ -1135,28 +1031,21 @@ int rses_get_max_replication_lag(ROUTER_CLIENT_SES *rses)
*/
backend_ref_t *get_bref_from_dcb(ROUTER_CLIENT_SES *rses, DCB *dcb)
{
backend_ref_t *bref;
int i = 0;
ss_dassert(dcb->dcb_role == DCB_ROLE_BACKEND_HANDLER);
CHK_DCB(dcb);
CHK_CLIENT_RSES(rses);
bref = rses->rses_backend_ref;
while (i < rses->rses_nbackends)
for (int i = 0; i < rses->rses_nbackends; i++)
{
if (bref->bref_dcb == dcb)
if (rses->rses_backend_ref[i].bref_dcb == dcb)
{
break;
return &rses->rses_backend_ref[i];
}
bref++;
i += 1;
}
if (i == rses->rses_nbackends)
{
bref = NULL;
}
return bref;
/** We should always have a valid backend reference */
ss_dassert(false);
return NULL;
}
/**
@ -1284,17 +1173,19 @@ static bool rwsplit_process_router_options(ROUTER_INSTANCE *router,
* Even if succp == true connecting to new slave may have failed. succp is to
* tell whether router has enough master/slave connections to continue work.
*/
static void handleError(MXS_ROUTER *instance, void *router_session,
GWBUF *errmsgbuf, DCB *problem_dcb,
mxs_error_action_t action, bool *succp)
static void handleError(MXS_ROUTER *instance,
MXS_ROUTER_SESSION *router_session,
GWBUF *errmsgbuf,
DCB *problem_dcb,
mxs_error_action_t action,
bool *succp)
{
MXS_SESSION *session;
ROUTER_INSTANCE *inst = (ROUTER_INSTANCE *)instance;
ROUTER_CLIENT_SES *rses = (ROUTER_CLIENT_SES *)router_session;
CHK_CLIENT_RSES(rses);
CHK_DCB(problem_dcb);
if (!rses_begin_locked_router_action(rses))
if (rses->rses_closed)
{
/** Session is already closed */
problem_dcb->dcb_errhandle_called = true;
@ -1311,32 +1202,24 @@ static void handleError(MXS_ROUTER *instance, void *router_session,
* be safe with the code as it stands on 9 Sept 2015 - MNB
*/
*succp = true;
rses_end_locked_router_action(rses);
return;
}
else
{
problem_dcb->dcb_errhandle_called = true;
}
session = problem_dcb->session;
backend_ref_t *bref = get_bref_from_dcb(rses, problem_dcb);
MXS_SESSION *session = problem_dcb->session;
ss_dassert(session);
if (session == NULL)
{
MXS_ERROR("Session of DCB %p is NULL, won't close the DCB.", problem_dcb);
ss_dassert(false);
*succp = false;
}
else if (DCB_ROLE_CLIENT_HANDLER == problem_dcb->dcb_role)
if (problem_dcb->dcb_role == DCB_ROLE_CLIENT_HANDLER)
{
dcb_close(problem_dcb);
*succp = false;
}
else
{
CHK_SESSION(session);
CHK_CLIENT_RSES(rses);
backend_ref_t *bref = get_bref_from_dcb(rses, problem_dcb);
switch (action)
{
@ -1449,8 +1332,6 @@ static void handleError(MXS_ROUTER *instance, void *router_session,
break;
}
}
rses_end_locked_router_action(rses);
}
/**
@ -1570,7 +1451,6 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
bool succp;
myrses = *rses;
ss_dassert(SPINLOCK_IS_LOCKED(&myrses->rses_lock));
ses = backend_dcb->session;
CHK_SESSION(ses);

View File

@ -307,7 +307,6 @@ struct router_client_session
#if defined(SS_DEBUG)
skygw_chk_t rses_chk_top;
#endif
SPINLOCK rses_lock; /*< protects rses_deleted */
bool rses_closed; /*< true when closeSession is called */
rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT]; /*< Properties listed by their type */
backend_ref_t* rses_master_ref;
@ -349,7 +348,6 @@ typedef struct
typedef struct router_instance
{
SERVICE* service; /*< Pointer to service */
SPINLOCK lock; /*< Lock for the instance data */
rwsplit_config_t rwsplit_config; /*< expanded config info from SERVICE */
int rwsplit_version; /*< version number for router's config */
ROUTER_STATS stats; /*< Statistics for this router */

View File

@ -56,7 +56,6 @@ bool handle_target_is_all(route_target_t route_target,
GWBUF *querybuf, int packet_type, qc_query_type_t qtype);
int determine_packet_type(GWBUF *querybuf, bool *non_empty_packet);
void log_transaction_status(ROUTER_CLIENT_SES *rses, GWBUF *querybuf, qc_query_type_t qtype);
void session_lock_failure_handling(GWBUF *querybuf, int packet_type, qc_query_type_t qtype);
bool is_packet_a_one_way_message(int packet_type);
sescmd_cursor_t *backend_ref_get_sescmd_cursor(backend_ref_t *bref);
bool is_packet_a_query(int packet_type);
@ -65,8 +64,6 @@ bool send_readonly_error(DCB *dcb);
/*
* The following are implemented in readwritesplit.c
*/
bool rses_begin_locked_router_action(ROUTER_CLIENT_SES *rses);
void rses_end_locked_router_action(ROUTER_CLIENT_SES *rses);
void bref_clear_state(backend_ref_t *bref, bref_state_t state);
void bref_set_state(backend_ref_t *bref, bref_state_t state);
int router_handle_state_switch(DCB *dcb, DCB_REASON reason, void *data);

View File

@ -287,32 +287,6 @@ handle_target_is_all(route_target_t route_target,
return result;
}
/* This is MySQL specific */
/**
* @brief Write an error message to the log indicating failure
*
* Used when an attempt to lock the router session fails.
*
* @param querybuf Query buffer containing packet
* @param packet_type Integer (enum) indicating type of packet
* @param qtype Query type
*/
void
session_lock_failure_handling(GWBUF *querybuf, int packet_type, qc_query_type_t qtype)
{
if (packet_type != MYSQL_COM_QUIT)
{
/* NOTE: modutil_get_query is MySQL specific */
char *query_str = modutil_get_query(querybuf);
MXS_ERROR("Can't route %s:%s:\"%s\" to "
"backend server. Router is closed.",
STRPACKETTYPE(packet_type), STRQTYPE(qtype),
(query_str == NULL ? "(empty)" : query_str));
MXS_FREE(query_str);
}
}
/*
* Probably MySQL specific because of modutil function
*/

View File

@ -70,16 +70,10 @@ bool route_single_stmt(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
/* packet_type is a problem as it is MySQL specific */
packet_type = determine_packet_type(querybuf, &non_empty_packet);
qtype = determine_query_type(querybuf, packet_type, non_empty_packet);
if (non_empty_packet)
{
/** This might not be absolutely necessary as some parts of the code
* can only be executed by one thread at a time. */
if (!rses_begin_locked_router_action(rses))
{
return false;
}
handle_multi_temp_and_load(rses, querybuf, packet_type, (int *)&qtype);
rses_end_locked_router_action(rses);
if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO))
{
@ -116,7 +110,7 @@ bool route_single_stmt(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
{
succp = handle_target_is_all(route_target, inst, rses, querybuf, packet_type, qtype);
}
else if (rses_begin_locked_router_action(rses))
else
{
/* Now we have a lock on the router session */
bool store_stmt = false;
@ -152,12 +146,6 @@ bool route_single_stmt(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses,
ss_dassert(!store_stmt || TARGET_IS_SLAVE(route_target));
handle_got_target(inst, rses, querybuf, target_dcb, store_stmt);
}
rses_end_locked_router_action(rses);
}
else
{
session_lock_failure_handling(querybuf, packet_type, qtype);
succp = false;
}
return succp;
@ -214,12 +202,6 @@ bool route_session_write(ROUTER_CLIENT_SES *router_cli_ses,
{
int rc;
/** Lock router session */
if (!rses_begin_locked_router_action(router_cli_ses))
{
goto return_succp;
}
for (i = 0; i < router_cli_ses->rses_nbackends; i++)
{
DCB *dcb = backend_ref[i].bref_dcb;
@ -244,15 +226,9 @@ bool route_session_write(ROUTER_CLIENT_SES *router_cli_ses,
}
}
}
rses_end_locked_router_action(router_cli_ses);
gwbuf_free(querybuf);
goto return_succp;
}
/** Lock router session */
if (!rses_begin_locked_router_action(router_cli_ses))
{
goto return_succp;
}
if (router_cli_ses->rses_nbackends <= 0)
{
@ -318,7 +294,6 @@ bool route_session_write(ROUTER_CLIENT_SES *router_cli_ses,
if ((prop = rses_property_init(RSES_PROP_TYPE_SESCMD)) == NULL)
{
MXS_ERROR("Router session property initialization failed");
rses_end_locked_router_action(router_cli_ses);
return false;
}
@ -328,7 +303,6 @@ bool route_session_write(ROUTER_CLIENT_SES *router_cli_ses,
if (rses_property_add(router_cli_ses, prop) != 0)
{
MXS_ERROR("Session property addition failed.");
rses_end_locked_router_action(router_cli_ses);
return false;
}
@ -387,9 +361,6 @@ bool route_session_write(ROUTER_CLIENT_SES *router_cli_ses,
atomic_add(&router_cli_ses->rses_nsescmd, 1);
/** Unlock router session */
rses_end_locked_router_action(router_cli_ses);
return_succp:
/**
* Routing must succeed to all backends that are used.
@ -1364,7 +1335,6 @@ int rses_property_add(ROUTER_CLIENT_SES *rses, rses_property_t *prop)
CHK_CLIENT_RSES(rses);
CHK_RSES_PROP(prop);
ss_dassert(SPINLOCK_IS_LOCKED(&rses->rses_lock));
prop->rses_prop_rsession = rses;
p = rses->rses_properties[prop->rses_prop_type];

View File

@ -60,8 +60,6 @@ mysql_sescmd_t *rses_property_get_sescmd(rses_property_t *prop)
}
CHK_RSES_PROP(prop);
ss_dassert(prop->rses_prop_rsession == NULL ||
SPINLOCK_IS_LOCKED(&prop->rses_prop_rsession->rses_lock));
sescmd = &prop->rses_prop_data.sescmd;
@ -134,14 +132,9 @@ GWBUF *sescmd_cursor_process_replies(GWBUF *replybuf,
backend_ref_t *bref,
bool *reconnect)
{
mysql_sescmd_t *scmd;
sescmd_cursor_t *scur;
ROUTER_CLIENT_SES *ses;
scur = &bref->bref_sescmd_cur;
ss_dassert(SPINLOCK_IS_LOCKED(&(scur->scmd_cur_rses->rses_lock)));
scmd = sescmd_cursor_get_command(scur);
ses = (*scur->scmd_cur_ptr_property)->rses_prop_rsession;
sescmd_cursor_t *scur = &bref->bref_sescmd_cur;
mysql_sescmd_t *scmd = sescmd_cursor_get_command(scur);
ROUTER_CLIENT_SES *ses = (*scur->scmd_cur_ptr_property)->rses_prop_rsession;
CHK_GWBUF(replybuf);
/**
@ -273,7 +266,6 @@ mysql_sescmd_t *sescmd_cursor_get_command(sescmd_cursor_t *scur)
{
mysql_sescmd_t *scmd;
ss_dassert(SPINLOCK_IS_LOCKED(&(scur->scmd_cur_rses->rses_lock)));
scur->scmd_cur_cmd = rses_property_get_sescmd(*scur->scmd_cur_ptr_property);
CHK_MYSQL_SESCMD(scur->scmd_cur_cmd);
@ -293,7 +285,6 @@ bool sescmd_cursor_is_active(sescmd_cursor_t *sescmd_cursor)
MXS_ERROR("[%s] Error: NULL parameter.", __FUNCTION__);
return false;
}
ss_dassert(SPINLOCK_IS_LOCKED(&sescmd_cursor->scmd_cur_rses->rses_lock));
succp = sescmd_cursor->scmd_cur_active;
return succp;
@ -303,7 +294,6 @@ bool sescmd_cursor_is_active(sescmd_cursor_t *sescmd_cursor)
void sescmd_cursor_set_active(sescmd_cursor_t *sescmd_cursor,
bool value)
{
ss_dassert(SPINLOCK_IS_LOCKED(&sescmd_cursor->scmd_cur_rses->rses_lock));
/** avoid calling unnecessarily */
ss_dassert(sescmd_cursor->scmd_cur_active != value);
sescmd_cursor->scmd_cur_active = value;
@ -420,8 +410,6 @@ static bool sescmd_cursor_next(sescmd_cursor_t *scur)
ss_dassert(scur != NULL);
ss_dassert(*(scur->scmd_cur_ptr_property) != NULL);
ss_dassert(SPINLOCK_IS_LOCKED(
&(*(scur->scmd_cur_ptr_property))->rses_prop_rsession->rses_lock));
/** Illegal situation */
if (scur == NULL || *scur->scmd_cur_ptr_property == NULL ||

View File

@ -58,23 +58,23 @@
*/
static MXS_ROUTER* createInstance(SERVICE *service, char **options);
static void* newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *session);
static void freeSession(MXS_ROUTER *instance, void *session);
static int routeQuery(MXS_ROUTER *instance, void *session, GWBUF *queue);
static void diagnostic(MXS_ROUTER *instance, DCB *dcb);
static MXS_ROUTER_SESSION* newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *session);
static void freeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *session);
static int routeQuery(MXS_ROUTER *instance, MXS_ROUTER_SESSION *session, GWBUF *queue);
static void diagnostic(MXS_ROUTER *instance, DCB *dcb);
static void clientReply(MXS_ROUTER* instance,
void* router_session,
GWBUF* queue,
DCB* backend_dcb);
MXS_ROUTER_SESSION* router_session,
GWBUF* queue,
DCB* backend_dcb);
static void handleError(MXS_ROUTER* instance,
void* router_session,
GWBUF* errmsgbuf,
DCB* backend_dcb,
static void handleError(MXS_ROUTER* instance,
MXS_ROUTER_SESSION* router_session,
GWBUF* errmsgbuf,
DCB* backend_dcb,
mxs_error_action_t action,
bool* succp);
bool* succp);
static backend_ref_t* get_bref_from_dcb(ROUTER_CLIENT_SES* rses, DCB* dcb);
static route_target_t get_shard_route_target(qc_query_type_t qtype,
@ -847,7 +847,7 @@ enum shard_map_state shard_map_update_state(shard_map_t *self, ROUTER_INSTANCE*
* @param session The session itself
* @return Session specific data for this session
*/
static void* newSession(MXS_ROUTER* router_inst, MXS_SESSION* session)
static MXS_ROUTER_SESSION* newSession(MXS_ROUTER* router_inst, MXS_SESSION* session)
{
backend_ref_t* backend_ref; /*< array of backend references (DCB, BACKEND, cursor) */
ROUTER_CLIENT_SES* client_rses = NULL;
@ -1040,7 +1040,7 @@ static void* newSession(MXS_ROUTER* router_inst, MXS_SESSION* session)
* @param instance The router instance data
* @param session The session being closed
*/
static void closeSession(MXS_ROUTER* instance, void* router_session)
static void closeSession(MXS_ROUTER* instance, MXS_ROUTER_SESSION* router_session)
{
ROUTER_CLIENT_SES* router_cli_ses;
ROUTER_INSTANCE* inst;
@ -1135,7 +1135,7 @@ static void closeSession(MXS_ROUTER* instance, void* router_session)
}
}
static void freeSession(MXS_ROUTER* router_instance, void* router_client_session)
static void freeSession(MXS_ROUTER* router_instance, MXS_ROUTER_SESSION* router_client_session)
{
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session;
@ -1612,7 +1612,7 @@ bool send_database_list(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
*
*/
static int routeQuery(MXS_ROUTER* instance,
void* router_session,
MXS_ROUTER_SESSION* router_session,
GWBUF* qbuf)
{
qc_query_type_t qtype = QUERY_TYPE_UNKNOWN;
@ -2238,7 +2238,7 @@ static void diagnostic(MXS_ROUTER *instance, DCB *dcb)
* @param queue The GWBUF with reply data
*/
static void clientReply(MXS_ROUTER* instance,
void* router_session,
MXS_ROUTER_SESSION* router_session,
GWBUF* buffer,
DCB* backend_dcb)
{
@ -3550,12 +3550,12 @@ return_succp:
* Even if succp == true connecting to new slave may have failed. succp is to
* tell whether router has enough master/slave connections to continue work.
*/
static void handleError(MXS_ROUTER* instance,
void* router_session,
GWBUF* errmsgbuf,
DCB* problem_dcb,
static void handleError(MXS_ROUTER* instance,
MXS_ROUTER_SESSION* router_session,
GWBUF* errmsgbuf,
DCB* problem_dcb,
mxs_error_action_t action,
bool* succp)
bool* succp)
{
MXS_SESSION* session;
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;

View File

@ -1,4 +0,0 @@
add_library(testroute SHARED testroute.c)
target_link_libraries(testroute maxscale-common)
set_target_properties(testroute PROPERTIES VERSION "1.0.0")
install_module(testroute core)

View File

@ -1,167 +0,0 @@
/*
* 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: 2019-07-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.
*/
#include <stdio.h>
#include <maxscale/alloc.h>
#include <maxscale/router.h>
#include <maxscale/modinfo.h>
static MXS_ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(MXS_ROUTER *instance, MXS_SESSION *session);
static void closeSession(MXS_ROUTER *instance, void *session);
static void freeSession(MXS_ROUTER *instance, void *session);
static int routeQuery(MXS_ROUTER *instance, void *session, GWBUF *queue);
static void clientReply(MXS_ROUTER *instance, void *session, GWBUF *queue, DCB*);
static void diagnostic(MXS_ROUTER *instance, DCB *dcb);
static uint64_t getCapabilities(MXS_ROUTER* instance);
static void handleError(MXS_ROUTER *instance,
void *router_session,
GWBUF *errbuf,
DCB *backend_dcb,
mxs_error_action_t action,
bool *succp);
typedef struct
{
} TESTROUTER;
typedef struct
{
} TESTSESSION;
/**
* 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
* external entry points for this module.
*
* @return The module object
*/
MXS_MODULE* MXS_CREATE_MODULE()
{
static MXS_ROUTER_OBJECT MyObject =
{
createInstance,
newSession,
closeSession,
freeSession,
routeQuery,
diagnostic,
clientReply,
handleError,
getCapabilities,
NULL
};
static MXS_MODULE info =
{
MXS_MODULE_API_ROUTER,
MXS_MODULE_IN_DEVELOPMENT,
MXS_ROUTER_VERSION,
"A test router - not for use in real systems",
"V1.0.0",
&MyObject,
NULL, /* Process init. */
NULL, /* Process finish. */
NULL, /* Thread init. */
NULL, /* Thread finish. */
{
{MXS_END_MODULE_PARAMS}
}
};
return &info;
}
/**
* Create an instance of the router for a particular service
* within the gateway.
*
* @param service The service this router is being create for
* @param options The options for this query router
*
* @return The instance data for this new instance
*/
static MXS_ROUTER *
createInstance(SERVICE *service, char **options)
{
return (MXS_ROUTER*)MXS_MALLOC(sizeof(TESTROUTER));
}
/**
* Associate a new session with this instance of the router.
*
* @param instance The router instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(MXS_ROUTER *instance, MXS_SESSION *session)
{
return (MXS_SESSION*)MXS_MALLOC(sizeof(TESTSESSION));
}
/**
* Close a session with the router, this is the mechanism
* by which a router may cleanup data structure etc.
*
* @param instance The router instance data
* @param session The session being closed
*/
static void
closeSession(MXS_ROUTER *instance, void *session)
{
}
static void freeSession(
MXS_ROUTER* router_instance,
void* router_client_session)
{
MXS_FREE(router_client_session);
}
static int
routeQuery(MXS_ROUTER *instance, void *session, GWBUF *queue)
{
return 0;
}
void clientReply(MXS_ROUTER* instance, void* session, GWBUF* queue, DCB* dcb)
{
}
/**
* Diagnostics routine
*
* @param instance The router instance
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(MXS_ROUTER *instance, DCB *dcb)
{
}
static uint64_t getCapabilities(MXS_ROUTER* instance)
{
return 0;
}
static void handleError(
MXS_ROUTER *instance,
void *router_session,
GWBUF *errbuf,
DCB *backend_dcb,
mxs_error_action_t action,
bool *succp)
{
}