MXS-1075: Sqlite db for GTID - file, pos mapping

Use sqlite3 database instead of hash tables for GTID - file, pos mapping
This commit is contained in:
MassimilianoPinto
2017-03-28 10:23:09 +02:00
parent 69ae85c400
commit a236b14eef
6 changed files with 247 additions and 120 deletions

View File

@ -1,11 +1,11 @@
add_library(binlogrouter SHARED blr.c blr_master.c blr_cache.c blr_slave.c blr_file.c) add_library(binlogrouter SHARED blr.c blr_master.c blr_cache.c blr_slave.c blr_file.c)
set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_LIBDIR} VERSION "2.0.0") set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${MAXSCALE_LIBDIR} VERSION "2.0.0")
set_target_properties(binlogrouter PROPERTIES LINK_FLAGS -Wl,-z,defs) set_target_properties(binlogrouter PROPERTIES LINK_FLAGS -Wl,-z,defs)
target_link_libraries(binlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid) target_link_libraries(binlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid sqlite3)
install_module(binlogrouter core) install_module(binlogrouter core)
add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c) add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c)
target_link_libraries(maxbinlogcheck maxscale-common ${PCRE_LINK_FLAGS} uuid) target_link_libraries(maxbinlogcheck maxscale-common ${PCRE_LINK_FLAGS} uuid sqlite3)
install_executable(maxbinlogcheck core) install_executable(maxbinlogcheck core)

View File

@ -117,8 +117,7 @@ static void destroyInstance(MXS_ROUTER *instance);
bool blr_extract_key(const char *linebuf, int nline, ROUTER_INSTANCE *router); bool blr_extract_key(const char *linebuf, int nline, ROUTER_INSTANCE *router);
bool blr_get_encryption_key(ROUTER_INSTANCE *router); bool blr_get_encryption_key(ROUTER_INSTANCE *router);
int blr_parse_key_file(ROUTER_INSTANCE *router); int blr_parse_key_file(ROUTER_INSTANCE *router);
static MARIADB_GTID_INFO *mariadb_gtid_info_dup(const MARIADB_GTID_INFO *in); static bool blr_open_gtid_maps_storage(ROUTER_INSTANCE *inst);
static void mariadb_gtid_info_free(MARIADB_GTID_INFO *in);
static void stats_func(void *); static void stats_func(void *);
@ -390,6 +389,11 @@ createInstance(SERVICE *service, char **options)
defuuid[8], defuuid[9], defuuid[10], defuuid[11], defuuid[8], defuuid[9], defuuid[10], defuuid[11],
defuuid[12], defuuid[13], defuuid[14], defuuid[15]); defuuid[12], defuuid[13], defuuid[14], defuuid[15]);
} }
else
{
free_instance(inst);
return NULL;
}
} }
/* /*
@ -642,38 +646,6 @@ createInstance(SERVICE *service, char **options)
return NULL; return NULL;
} }
/* Enable MariaDB GTID repo */
if (inst->mariadb10_compat &&
inst->mariadb_gtid)
{
if (!inst->trx_safe)
{
MXS_ERROR("MariaDB GTID can be enabled only"
" with Transaction Safety feature."
" Please enable it with option 'transaction_safety = on'");
free_instance(inst);
return NULL;
}
if ((inst->gtid_repo = hashtable_alloc(1000,
hashtable_item_strhash,
hashtable_item_strcmp)) == NULL)
{
MXS_ERROR("Service %s, cannot allocate MariaDB GTID hashtable", service->name);
free_instance(inst);
return NULL;
}
hashtable_memory_fns(inst->gtid_repo,
hashtable_item_strdup,
(HASHCOPYFN)mariadb_gtid_info_dup,
hashtable_item_free,
(HASHFREEFN)mariadb_gtid_info_free);
MXS_NOTICE("%s: Service has MariaDB GTID otion set to ON",
service->name);
}
if (inst->serverid <= 0) if (inst->serverid <= 0)
{ {
MXS_ERROR("Service %s, server-id is not configured. " MXS_ERROR("Service %s, server-id is not configured. "
@ -712,6 +684,27 @@ createInstance(SERVICE *service, char **options)
} }
} }
/* Enable MariaDB the GTID maps store */
if (inst->mariadb10_compat &&
inst->mariadb_gtid)
{
if (!inst->trx_safe)
{
MXS_ERROR("MariaDB GTID can be enabled only"
" with Transaction Safety feature."
" Please enable it with option 'transaction_safety = on'");
free_instance(inst);
return NULL;
}
/* Create/Open R/W GTID sqlite3 storage */
if (!blr_open_gtid_maps_storage(inst))
{
free_instance(inst);
return NULL;
}
}
/* Dynamically allocate master_host server struct, not written in any cnf file */ /* Dynamically allocate master_host server struct, not written in any cnf file */
if (service->dbref == NULL) if (service->dbref == NULL)
{ {
@ -724,6 +717,7 @@ createInstance(SERVICE *service, char **options)
MXS_ERROR("%s: Error for server_alloc in createInstance", MXS_ERROR("%s: Error for server_alloc in createInstance",
inst->service->name); inst->service->name);
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst); free_instance(inst);
return NULL; return NULL;
} }
@ -736,7 +730,7 @@ createInstance(SERVICE *service, char **options)
server_free(service->dbref->server); server_free(service->dbref->server);
MXS_FREE(service->dbref); MXS_FREE(service->dbref);
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst); free_instance(inst);
return NULL; return NULL;
} }
@ -828,7 +822,7 @@ createInstance(SERVICE *service, char **options)
server_free(service->dbref->server); server_free(service->dbref->server);
MXS_FREE(service->dbref); MXS_FREE(service->dbref);
service->dbref = NULL; service->dbref = NULL;
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst); free_instance(inst);
return NULL; return NULL;
} }
@ -873,6 +867,7 @@ createInstance(SERVICE *service, char **options)
service->dbref = NULL; service->dbref = NULL;
} }
sqlite3_close_v2(inst->gtid_maps);
free_instance(inst); free_instance(inst);
return NULL; return NULL;
} }
@ -1044,6 +1039,8 @@ newSession(MXS_ROUTER *instance, MXS_SESSION *session)
slave->encryption_ctx = NULL; slave->encryption_ctx = NULL;
slave->mariadb_gtid = NULL; slave->mariadb_gtid = NULL;
slave->gtid_maps = NULL;
/** /**
* Add this session to the list of active sessions. * Add this session to the list of active sessions.
*/ */
@ -2501,6 +2498,8 @@ destroyInstance(MXS_ROUTER *instance)
inst->master_state = BLRM_SLAVE_STOPPED; inst->master_state = BLRM_SLAVE_STOPPED;
} }
spinlock_release(&inst->lock);
if (inst->client) if (inst->client)
{ {
if (inst->client->state == DCB_STATE_POLLING) if (inst->client->state == DCB_STATE_POLLING)
@ -2525,10 +2524,8 @@ destroyInstance(MXS_ROUTER *instance)
inst->service->name, inst->binlog_name, inst->current_pos, inst->binlog_position); inst->service->name, inst->binlog_name, inst->current_pos, inst->binlog_position);
} }
/* Free GTID hashtable */ /* Close GTID maps database */
hashtable_free(inst->gtid_repo); sqlite3_close_v2(inst->gtid_maps);
spinlock_release(&inst->lock);
} }
/** /**
@ -2724,42 +2721,49 @@ int blr_parse_key_file(ROUTER_INSTANCE *router)
} }
/** /**
* Free routine for GTID repo hashtable * Create / Open R/W GTID maps database
* *
* @param in The data to free
*/ */
static void mariadb_gtid_info_free(MARIADB_GTID_INFO *in) static bool blr_open_gtid_maps_storage(ROUTER_INSTANCE *inst)
{ {
if (in) char dbpath[PATH_MAX + 1];
{ snprintf(dbpath, sizeof(dbpath), "/%s/%s",
MXS_FREE(in->gtid); inst->binlogdir, GTID_MAPS_DB);
MXS_FREE(in->file);
MXS_FREE(in);
}
}
/** /* Open/Create the GTID maps database */
* Copy routine for GTID repo hashtable if (sqlite3_open_v2(dbpath,
* &inst->gtid_maps,
* @param in The data to copy SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
* @return New allocated value or NULL NULL) != SQLITE_OK)
*/
static MARIADB_GTID_INFO *mariadb_gtid_info_dup(const MARIADB_GTID_INFO *in)
{
MARIADB_GTID_INFO *rval = (MARIADB_GTID_INFO *) MXS_CALLOC(1, sizeof(MARIADB_GTID_INFO));
char *gtid = MXS_STRDUP(in->gtid);
char *file = MXS_STRDUP(in->file);
if (!gtid || !rval)
{ {
MXS_FREE(rval); MXS_ERROR("Failed to open GTID maps SQLite database '%s': %s", dbpath,
MXS_FREE(gtid); sqlite3_errmsg(inst->gtid_maps));
MXS_FREE(file); return false;
return NULL;
} }
rval->gtid = gtid;
rval->file = file;
rval->start = in-> start;
rval->end = in->end;
return (void *) rval; char* errmsg;
/* Create the gtid_maps table */
int rc = sqlite3_exec(inst->gtid_maps,
"CREATE TABLE IF NOT EXISTS "
"gtid_maps(gtid varchar(255), "
"binlog_file varchar(255), "
"start_pos bigint, "
"end_pos bigint, "
"primary key(gtid));",
NULL, NULL, &errmsg);
if (rc != SQLITE_OK)
{
MXS_ERROR("Service %s, failed to create GTID index table 'gtid_maps': %s",
inst->service->name,
sqlite3_errmsg(inst->gtid_maps));
sqlite3_free(errmsg);
/* Close GTID maps database */
sqlite3_close_v2(inst->gtid_maps);
return false;
}
MXS_NOTICE("%s: Service has MariaDB GTID otion set to ON",
inst->service->name);
return true;
} }

View File

@ -57,6 +57,7 @@
#include <maxscale/thread.h> #include <maxscale/thread.h>
#include <maxscale/protocol/mysql.h> #include <maxscale/protocol/mysql.h>
#include <maxscale/secrets.h> #include <maxscale/secrets.h>
#include <maxscale/sqlite3.h>
MXS_BEGIN_DECLS MXS_BEGIN_DECLS
@ -92,6 +93,11 @@ MXS_BEGIN_DECLS
/* MariaDB GTID string len */ /* MariaDB GTID string len */
#define GTID_MAX_LEN 42 #define GTID_MAX_LEN 42
/* GTID slite3 query buffer size */
#define GTID_SQL_BUFFER_SIZE 1024
/* GTID slite3 database name */
#define GTID_MAPS_DB "gtid_maps.db"
/** /**
* Supported Encryption algorithms * Supported Encryption algorithms
* *
@ -451,6 +457,7 @@ typedef struct router_slave
void *encryption_ctx; /*< Encryption context */ void *encryption_ctx; /*< Encryption context */
bool gtid_strict_mode;/*< MariaDB 10 Slave sets gtid_strict_mode */ bool gtid_strict_mode;/*< MariaDB 10 Slave sets gtid_strict_mode */
char *mariadb_gtid; /*< MariaDB 10 Slave connects with GTID */ char *mariadb_gtid; /*< MariaDB 10 Slave connects with GTID */
sqlite3 *gtid_maps; /*< GTID storage client handle, read only*/
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
skygw_chk_t rses_chk_tail; skygw_chk_t rses_chk_tail;
#endif #endif
@ -637,6 +644,7 @@ typedef struct router_instance
* This allows MariaDB 10 slave servers * This allows MariaDB 10 slave servers
* connecting with GTID */ * connecting with GTID */
HASHTABLE *gtid_repo; /*< Storage for MariaDB GTIDs */ HASHTABLE *gtid_repo; /*< Storage for MariaDB GTIDs */
sqlite3 *gtid_maps; /*< GTID storage */
struct router_instance *next; struct router_instance *next;
} ROUTER_INSTANCE; } ROUTER_INSTANCE;
@ -881,6 +889,9 @@ extern int blr_check_encryption_algorithm(char *);
extern const char *blr_encryption_algorithm_list(void); extern const char *blr_encryption_algorithm_list(void);
extern bool blr_get_encryption_key(ROUTER_INSTANCE *); extern bool blr_get_encryption_key(ROUTER_INSTANCE *);
extern const char *blr_skip_leading_sql_comments(const char *); extern const char *blr_skip_leading_sql_comments(const char *);
extern bool blr_fetch_mariadb_gtid(ROUTER_SLAVE *,
const char *,
MARIADB_GTID_INFO *);
MXS_END_DECLS MXS_END_DECLS

View File

@ -3297,7 +3297,14 @@ static void blr_report_checksum(REP_HEADER hdr, const uint8_t *buffer, char *out
*/ */
bool blr_save_mariadb_gtid(ROUTER_INSTANCE *inst) bool blr_save_mariadb_gtid(ROUTER_INSTANCE *inst)
{ {
static const char insert_tpl[] = "INSERT OR IGNORE INTO gtid_maps("
"gtid, "
"binlog_file, "
"start_pos, end_pos) "
"VALUES (\"%s\", \"%s\", %lu, %lu);";
MARIADB_GTID_INFO gtid_info; MARIADB_GTID_INFO gtid_info;
char *errmsg;
char insert_sql[GTID_SQL_BUFFER_SIZE];
gtid_info.gtid = inst->pending_transaction.gtid; gtid_info.gtid = inst->pending_transaction.gtid;
gtid_info.file = inst->binlog_name; gtid_info.file = inst->binlog_name;
@ -3305,41 +3312,107 @@ bool blr_save_mariadb_gtid(ROUTER_INSTANCE *inst)
gtid_info.end = inst->pending_transaction.end_pos; gtid_info.end = inst->pending_transaction.end_pos;
/* Save GTID into repo */ /* Save GTID into repo */
if (!hashtable_add(inst->gtid_repo, snprintf(insert_sql,
inst->pending_transaction.gtid, GTID_SQL_BUFFER_SIZE,
&gtid_info)) insert_tpl,
{ gtid_info.gtid,
MXS_ERROR("Service %s: error saving mariadb GTID %s into repo", gtid_info.file,
inst->service->name, gtid_info.start,
inst->pending_transaction.gtid); gtid_info.end);
return false;
}
MXS_DEBUG("Saved MariaDB GTID '%s', %s:%lu:%lu", /* Save GTID into repo */
if (sqlite3_exec(inst->gtid_maps, insert_sql, NULL, NULL,
&errmsg) != SQLITE_OK)
{
MXS_ERROR("Service %s: failed to insert GTID %s for %s:%lu,%lu "
"into gtid_maps database: %s",
inst->service->name,
gtid_info.gtid,
gtid_info.file,
gtid_info.start,
gtid_info.end,
errmsg);
return false;
}
sqlite3_free(errmsg);
MXS_DEBUG("Saved MariaDB GTID '%s', %s:%lu,%lu, insert SQL [%s]",
gtid_info.gtid, gtid_info.gtid,
inst->binlog_name, inst->binlog_name,
gtid_info.start, gtid_info.start,
gtid_info.end); gtid_info.end,
insert_sql);
return true; return true;
} }
/** /**
* Get MariaDB GTID frim repo * GTID select callbck for sqlite3 database
* *
* @param inst The router instance * @param data Data pointer from caller
* @return Found data or NULL * @param cols Number of columns
* @param values The values
* @param names The column names
*
* @return 0 on success, 1 otherwise
*/
static int gtid_select_cb(void *data, int cols, char** values, char** names)
{
MARIADB_GTID_INFO *result = (MARIADB_GTID_INFO *)data;
ss_dassert(cols >= 4);
if (values[0] &&
values[1] &&
values[2] &&
values[3])
{
result->gtid = MXS_STRDUP_A(values[0]);
result->file = MXS_STRDUP_A(values[1]);
result->start = atol(values[2]);
result->end = atol(values[3]);
}
return 0;
}
/**
* Get MariaDB GTID from repo
*
* @param slave The current slave instance
* @param gtid The GTID to look for
* @param result The (allocated) ouput data to fill
* @return True if with found GTID or false
*/ */
MARIADB_GTID_INFO *blr_fetch_mariadb_gtid(ROUTER_INSTANCE *inst, char *gtid) bool blr_fetch_mariadb_gtid(ROUTER_SLAVE *slave,
const char *gtid,
MARIADB_GTID_INFO *result)
{ {
if (!gtid) char *errmsg = NULL;
{ char select_query[GTID_SQL_BUFFER_SIZE];
return NULL; static const char select_tpl[] = "SELECT gtid, binlog_file, start_pos, end_pos "
} "FROM gtid_maps "
else "WHERE gtid = '%s' LIMIT 1;";
{ ss_dassert(gtid != NULL);
return (MARIADB_GTID_INFO *)hashtable_fetch(inst->gtid_repo,
gtid); snprintf(select_query,
} GTID_SQL_BUFFER_SIZE,
select_tpl,
gtid);
/* Find the GTID */
if (sqlite3_exec(slave->gtid_maps,
select_query,
gtid_select_cb,
result,
&errmsg) != SQLITE_OK)
{
MXS_ERROR("Failed to select GTID %s from GTID maps DB: %s, select [%s]",
gtid, errmsg, select_query);
sqlite3_free(errmsg);
return false;
}
return result->gtid ? true : false;
} }

View File

@ -166,7 +166,6 @@ static int blr_slave_send_heartbeat(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave
static int blr_set_master_ssl(ROUTER_INSTANCE *router, CHANGE_MASTER_OPTIONS config, char *error_message); static int blr_set_master_ssl(ROUTER_INSTANCE *router, CHANGE_MASTER_OPTIONS config, char *error_message);
static int blr_slave_read_ste(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, uint32_t fde_end_pos); static int blr_slave_read_ste(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, uint32_t fde_end_pos);
static GWBUF *blr_slave_read_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static GWBUF *blr_slave_read_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave);
extern MARIADB_GTID_INFO *blr_fetch_mariadb_gtid(ROUTER_INSTANCE *inst, char *gtid);
static bool blr_handle_select_smt(ROUTER_INSTANCE *router, static bool blr_handle_select_smt(ROUTER_INSTANCE *router,
ROUTER_SLAVE *slave, ROUTER_SLAVE *slave,
char *select_stmt); char *select_stmt);
@ -2118,7 +2117,7 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
return 1; return 1;
} }
/* Set the received filename */ /* Set the received filename: it could be changed later */
memcpy(slave->binlogfile, (char *)ptr, binlognamelen); memcpy(slave->binlogfile, (char *)ptr, binlognamelen);
slave->binlogfile[binlognamelen] = 0; slave->binlogfile[binlognamelen] = 0;
} }
@ -2144,15 +2143,39 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
} }
else else
{ {
/* TODO */ MARIADB_GTID_INFO f_gtid = {};
/* Shall we avoid the lookup if file & pos is set? */ char dbpath[PATH_MAX + 1];
snprintf(dbpath, sizeof(dbpath), "/%s/%s",
router->binlogdir, GTID_MAPS_DB);
/* Fetch the GTID from the storage */ /* Result set init */
MARIADB_GTID_INFO *f_gtid = blr_fetch_mariadb_gtid(router, f_gtid.gtid = NULL;
slave->mariadb_gtid);
/* Open GTID maps read-only database */
if (sqlite3_open_v2(dbpath,
&slave->gtid_maps,
SQLITE_OPEN_READONLY,
NULL) != SQLITE_OK)
{
MXS_ERROR("Slave %lu: failed to open GTID maps db '%s': %s",
(unsigned long)slave->serverid,
dbpath,
sqlite3_errmsg(slave->gtid_maps));
slave->gtid_maps = NULL;
}
else
{
/* Fetch the GTID from the maps storage */
blr_fetch_mariadb_gtid(slave, slave->mariadb_gtid, &f_gtid);
/* Close GTID maps database */
sqlite3_close_v2(slave->gtid_maps);
slave->gtid_maps = NULL;
}
/* Not Found */ /* Not Found */
if (!f_gtid) if (!f_gtid.gtid)
{ {
MXS_WARNING("Requested MariaDB GTID '%s' by server %lu" MXS_WARNING("Requested MariaDB GTID '%s' by server %lu"
" has not been found", " has not been found",
@ -2178,7 +2201,7 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
else else
{ {
/** /**
* Right now: use current router binlog file pos 4 * Right now: just use current router binlog file pos 4
*/ */
} }
} }
@ -2188,8 +2211,8 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
MXS_INFO("Found GTID '%s' for slave %lu at %s:%lu", MXS_INFO("Found GTID '%s' for slave %lu at %s:%lu",
slave->mariadb_gtid, slave->mariadb_gtid,
(unsigned long)slave->serverid, (unsigned long)slave->serverid,
f_gtid->file, f_gtid.file,
f_gtid->end); f_gtid.end);
/** /**
* Check whether GTID request has file & pos: * Check whether GTID request has file & pos:
@ -2201,30 +2224,46 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue
*/ */
/* Set binlog file to the GTID one */ /* Set binlog file to the GTID one */
strcpy(slave->binlogfile, f_gtid->file); strcpy(slave->binlogfile, f_gtid.file);
/** /**
* Set position to GTID event pos: * Set position to GTID event pos:
* i.e the next pos of COMMIT) * i.e the next pos of COMMIT)
*/ */
slave->binlog_pos = f_gtid->end; slave->binlog_pos = f_gtid.end;
} }
else else
{ {
/** /**
* The slave has requested a GTID while * The slave has requested a GTID with
* requesting a file & pos which can be different * binlog file & position.
* from file @ pos of that GTID:
* *
* i.e: Log file is different (after rotate) * The log file which GTID belongs to
* or other events (if any) could have been * could be different: this could happen
* written to binlog after GTID. * after a rotate event or other non GTID events
* could have been written to binlog after that GTID.
* *
* Binlog file is the requested one, so just set * If the GTID file is the requested one, use GTID info,
* the pos to the requested one * otherwise just set the pos to the requested one.
* In this case the file is the one from the request.
*/ */
slave->binlog_pos = requested_pos; if (strcmp(slave->binlogfile, f_gtid.file) == 0)
{
/* Set binlog file to the GTID one */
strcpy(slave->binlogfile, f_gtid.file);
/* Set position to GTID event next_pos */
slave->binlog_pos = f_gtid.end;
}
else
{
/* Set the requested pos */
slave->binlog_pos = requested_pos;
}
} }
/* Free gtid and file from result */
MXS_FREE(f_gtid.gtid);
MXS_FREE(f_gtid.file);
} }
} }
} }

View File

@ -1,5 +1,5 @@
if(BUILD_TESTS) if(BUILD_TESTS)
add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c) add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c)
target_link_libraries(testbinlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid) target_link_libraries(testbinlogrouter maxscale-common ${PCRE_LINK_FLAGS} uuid sqlite3)
add_test(NAME TestBinlogRouter COMMAND ./testbinlogrouter WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME TestBinlogRouter COMMAND ./testbinlogrouter WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif() endif()