Merge branch 'binlog_server_wait_data' into binlog_server_waitdata_encryption

This commit is contained in:
MassimilianoPinto
2016-10-12 09:39:59 +03:00
15 changed files with 656 additions and 96 deletions

View File

@ -290,8 +290,29 @@ where,
* the _op_ can be `=`, `!=`, `like` or `unlike`, and
* the _value_ a string.
If _op_ is `=` or `!=` then _value_ is used verbatim; if it is `like`
or `unlike`, then _value_ is interpreted as a _pcre2_ regular expression.
If _op_ is `=` or `!=` then _value_ is interpreted as a MariaDB account
string, that is, `%` means indicates wildcard, but if _op_ is `like` or
`unlike` it is simply assumed _value_ is a pcre2 regular expression.
For instance, the following are equivalent:
```
{
"attribute": "user",
"op": "=",
"value": "'bob'@'%'"
}
{
"attribute": "user",
"op": "like",
"value": "bob@.*"
}
Note that if _op_ is `=` or `!=` then the usual assumptions apply,
that is, a value of `bob` is equivalent with `'bob'@'%'`. If _like_
or _unlike_ is used, then no assumptions apply, but the string is
used verbatim as a regular expression.
The objects in the `use` array are processed in order. If the result
of a comparison is _true_, no further processing will be made and the
@ -307,7 +328,8 @@ rule in the `store` array.
### Examples
Use data from the cache for all users except `admin`.
Use data from the cache for all users except `admin` (actually `'admin'@'%'`),
regardless of what host the `admin` user comes from.
```
{
"use": [
@ -319,6 +341,7 @@ Use data from the cache for all users except `admin`.
]
}
```
# Storage
## Storage RocksDB

View File

@ -1,6 +1,19 @@
#ifndef _HK_HEARTBEAT_H
#define _HK_HEARTBEAT_H
/*
* 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/bsl.
*
* 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.
*/
/**
* The global housekeeper heartbeat value. This value is incremented
* every 100 milliseconds and may be used for crude timing etc.

View File

@ -1,5 +1,19 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
/*
* 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/bsl.
*
* 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 <maxscale/poll.h>
#include <dcb.h>
#include <housekeeper.h>

View File

@ -5,3 +5,7 @@ set_target_properties(cache PROPERTIES LINK_FLAGS -Wl,-z,defs)
install_module(cache experimental)
add_subdirectory(storage)
if(BUILD_TESTS)
add_subdirectory(test)
endif()

View File

@ -63,10 +63,8 @@ static struct cache_attribute_mapping cache_use_attributes[] =
static bool cache_rule_attribute_get(struct cache_attribute_mapping *mapping,
const char *s,
cache_rule_attribute_t *attribute);
static const char *cache_rule_attribute_to_string(cache_rule_attribute_t attribute);
static bool cache_rule_op_get(const char *s, cache_rule_op_t *op);
static const char *cache_rule_op_to_string(cache_rule_op_t op);
static bool cache_rule_compare(CACHE_RULE *rule, const char *value);
static bool cache_rule_compare_n(CACHE_RULE *rule, const char *value, size_t length);
@ -104,6 +102,7 @@ static bool cache_rule_matches(CACHE_RULE *rule, const char *default_db, const G
static void cache_rules_add_store_rule(CACHE_RULES* self, CACHE_RULE* rule);
static void cache_rules_add_use_rule(CACHE_RULES* self, CACHE_RULE* rule);
static CACHE_RULES* cache_rules_create_from_json(json_t* root, uint32_t debug);
static bool cache_rules_parse_json(CACHE_RULES* self, json_t* root);
typedef bool (*cache_rules_parse_element_t)(CACHE_RULES *self, json_t *object, size_t index);
@ -113,10 +112,86 @@ static bool cache_rules_parse_array(CACHE_RULES *self, json_t *store, const char
static bool cache_rules_parse_store_element(CACHE_RULES *self, json_t *object, size_t index);
static bool cache_rules_parse_use_element(CACHE_RULES *self, json_t *object, size_t index);
static bool dequote_mysql(char *s);
typedef enum pcre_quote_approach
{
PCRE_QUOTE_VERBATIM,
PCRE_QUOTE_QUERY
} pcre_quote_approach_t;
typedef enum mysql_account_kind
{
MYSQL_ACCOUNT_WITH_WILDCARD,
MYSQL_ACCOUNT_WITHOUT_WILDCARD
} mysql_account_kind_t;
static mysql_account_kind_t mysql_to_pcre(char *pcre, const char *mysql, pcre_quote_approach_t approach);
/*
* API begin
*/
/**
* Returns a string representation of a attribute.
*
* @param attribute An attribute type.
*
* @return Corresponding string, not to be freed.
*/
const char *cache_rule_attribute_to_string(cache_rule_attribute_t attribute)
{
switch (attribute)
{
case CACHE_ATTRIBUTE_COLUMN:
return "column";
case CACHE_ATTRIBUTE_DATABASE:
return "database";
case CACHE_ATTRIBUTE_QUERY:
return "query";
case CACHE_ATTRIBUTE_TABLE:
return "table";
case CACHE_ATTRIBUTE_USER:
return "user";
default:
ss_dassert(!true);
return "<invalid>";
}
}
/**
* Returns a string representation of an operator.
*
* @param op An operator.
*
* @return Corresponding string, not to be freed.
*/
const char *cache_rule_op_to_string(cache_rule_op_t op)
{
switch (op)
{
case CACHE_OP_EQ:
return "=";
case CACHE_OP_NEQ:
return "!=";
case CACHE_OP_LIKE:
return "like";
case CACHE_OP_UNLIKE:
return "unlike";
default:
ss_dassert(!true);
return "<invalid>";
}
}
/**
* Create a default cache rules object.
@ -158,17 +233,7 @@ CACHE_RULES *cache_rules_load(const char *path, uint32_t debug)
if (root)
{
rules = cache_rules_create(debug);
if (rules)
{
if (!cache_rules_parse_json(rules, root))
{
cache_rules_free(rules);
rules = NULL;
}
}
rules = cache_rules_create_from_json(root, debug);
json_decref(root);
}
else
@ -190,6 +255,35 @@ CACHE_RULES *cache_rules_load(const char *path, uint32_t debug)
return rules;
}
/**
* Parses the caching rules from a string and returns corresponding object.
*
* @param json String containing json.
* @param debug The debug level.
*
* @return The corresponding rules object, or NULL in case of error.
*/
CACHE_RULES *cache_rules_parse(const char *json, uint32_t debug)
{
CACHE_RULES *rules = NULL;
json_error_t error;
json_t *root = json_loads(json, JSON_DISABLE_EOF_CHECK, &error);
if (root)
{
rules = cache_rules_create_from_json(root, debug);
json_decref(root);
}
else
{
MXS_ERROR("Parsing rules failed: (%d:%d): %s",
error.line, error.column, error.text);
}
return rules;
}
/**
* Frees the rules object.
*
@ -252,12 +346,27 @@ bool cache_rules_should_use(CACHE_RULES *self, const SESSION *session)
CACHE_RULE *rule = self->use_rules;
const char *user = session_getUser((SESSION*)session);
const char *host = session_get_remote((SESSION*)session);
if (rule && user)
if (!user)
{
user = "";
}
if (!host)
{
host = "";
}
if (rule)
{
char account[strlen(user) + 1 + strlen(host) + 1];
sprintf(account, "%s@%s", user, host);
while (rule && !should_use)
{
should_use = cache_rule_matches_user(rule, user);
should_use = cache_rule_matches_user(rule, account);
rule = rule->next;
}
}
@ -301,35 +410,6 @@ static bool cache_rule_attribute_get(struct cache_attribute_mapping *mapping,
return false;
}
/**
* Returns a string representation of a attribute.
*
* @param attribute An attribute type.
*
* @return Corresponding string, not to be freed.
*/
static const char *cache_rule_attribute_to_string(cache_rule_attribute_t attribute)
{
switch (attribute)
{
case CACHE_ATTRIBUTE_COLUMN:
return "column";
case CACHE_ATTRIBUTE_DATABASE:
return "database";
case CACHE_ATTRIBUTE_QUERY:
return "query";
case CACHE_ATTRIBUTE_TABLE:
return "table";
default:
ss_dassert(!true);
return "<invalid>";
}
}
/**
* Converts a string to an operator
*
@ -367,35 +447,6 @@ static bool cache_rule_op_get(const char *s, cache_rule_op_t *op)
return false;
}
/**
* Returns a string representation of an operator.
*
* @param op An operator.
*
* @return Corresponding string, not to be freed.
*/
static const char *cache_rule_op_to_string(cache_rule_op_t op)
{
switch (op)
{
case CACHE_OP_EQ:
return "=";
case CACHE_OP_NEQ:
return "!=";
case CACHE_OP_LIKE:
return "like";
case CACHE_OP_UNLIKE:
return "unlike";
default:
ss_dassert(!true);
return "<invalid>";
}
}
/**
* Creates a CACHE_RULE object doing regexp matching.
*
@ -464,6 +515,99 @@ static CACHE_RULE *cache_rule_create_regexp(cache_rule_attribute_t attribute,
return rule;
}
static CACHE_RULE *cache_rule_create_user(cache_rule_attribute_t attribute,
cache_rule_op_t op,
const char *cvalue,
uint32_t debug)
{
ss_dassert((op == CACHE_OP_EQ) || (op == CACHE_OP_NEQ));
CACHE_RULE *rule = NULL;
bool error = false;
size_t len = strlen(cvalue);
char value[strlen(cvalue) + 1];
strcpy(value, cvalue);
char *at = strchr(value, '@');
char *user = value;
char *host;
if (at)
{
*at = 0;
host = at + 1;
}
else
{
host = "%";
}
if (dequote_mysql(user))
{
char pcre_user[2 * len + 1]; // Surely enough
if (*user == 0)
{
strcpy(pcre_user, ".*");
}
else
{
mysql_to_pcre(pcre_user, user, PCRE_QUOTE_VERBATIM);
}
if (dequote_mysql(host))
{
char pcre_host[2 * len + 1]; // Surely enough
if (mysql_to_pcre(pcre_host, host, PCRE_QUOTE_QUERY) == MYSQL_ACCOUNT_WITH_WILDCARD)
{
op = (op == CACHE_OP_EQ ? CACHE_OP_LIKE : CACHE_OP_UNLIKE);
char regexp[strlen(pcre_user) + 1 + strlen(pcre_host) + 1];
sprintf(regexp, "%s@%s", pcre_user, pcre_host);
rule = cache_rule_create_regexp(attribute, op, regexp, debug);
}
else
{
// No wildcard, no need to use regexp.
rule = (CACHE_RULE*)MXS_CALLOC(1, sizeof(CACHE_RULE));
char *value = MXS_MALLOC(strlen(user) + 1 + strlen(host) + 1);
if (rule && value)
{
sprintf(value, "%s@%s", user, host);
rule->attribute = attribute;
rule->op = op;
rule->debug = debug;
rule->value = value;
}
else
{
MXS_FREE(rule);
MXS_FREE(value);
rule = NULL;
}
}
}
else
{
MXS_ERROR("Could not dequote host %s.", cvalue);
}
}
else
{
MXS_ERROR("Could not dequote user %s.", cvalue);
}
return rule;
}
/**
* Creates a CACHE_RULE object doing simple matching.
*
@ -481,21 +625,30 @@ static CACHE_RULE *cache_rule_create_simple(cache_rule_attribute_t attribute,
{
ss_dassert((op == CACHE_OP_EQ) || (op == CACHE_OP_NEQ));
CACHE_RULE *rule = (CACHE_RULE*)MXS_CALLOC(1, sizeof(CACHE_RULE));
CACHE_RULE *rule;
if (attribute == CACHE_ATTRIBUTE_USER)
{
rule = cache_rule_create_user(attribute, op, cvalue, debug);
}
else
{
rule = (CACHE_RULE*)MXS_CALLOC(1, sizeof(CACHE_RULE));
char *value = MXS_STRDUP(cvalue);
if (rule && value)
{
rule->attribute = attribute;
rule->op = op;
rule->value = value;
rule->debug = debug;
rule->value = value;
}
else
{
MXS_FREE(value);
MXS_FREE(rule);
MXS_FREE(value);
rule = NULL;
}
}
return rule;
@ -787,18 +940,42 @@ static bool cache_rule_matches_table(CACHE_RULE *self, const char *default_db, c
}
/**
* Returns boolean indicating whether the user rule matches the user or not.
* Returns boolean indicating whether the user rule matches the account or not.
*
* @param self The CACHE_RULE object.
* @param user The current default db.
* @param account The account.
*
* @return True, if the rule matches, false otherwise.
*/
static bool cache_rule_matches_user(CACHE_RULE *self, const char *user)
static bool cache_rule_matches_user(CACHE_RULE *self, const char *account)
{
ss_dassert(self->attribute == CACHE_ATTRIBUTE_USER);
return cache_rule_compare(self, user);
bool matches = cache_rule_compare(self, account);
if ((matches && (self->debug & CACHE_DEBUG_MATCHING)) ||
(!matches && (self->debug & CACHE_DEBUG_NON_MATCHING)))
{
const char *text;
if (matches)
{
text = "MATCHES";
}
else
{
text = "does NOT match";
}
MXS_NOTICE("Rule { \"attribute\": \"%s\", \"op\": \"%s\", \"value\": \"%s\" } %s \"%s\".",
cache_rule_attribute_to_string(self->attribute),
cache_rule_op_to_string(self->op),
self->value,
text,
account);
}
return matches;
}
/**
@ -921,6 +1098,32 @@ static void cache_rules_add_use_rule(CACHE_RULES* self, CACHE_RULE* rule)
self->use_rules = cache_rule_append(self->use_rules, rule);
}
/**
* Creates a rules object from a JSON object.
*
* @param root The root JSON object in the rules file.
* @param debug The debug level.
*
* @return A rules object if the json object could be parsed, NULL otherwise.
*/
static CACHE_RULES* cache_rules_create_from_json(json_t* root, uint32_t debug)
{
ss_dassert(root);
CACHE_RULES *rules = cache_rules_create(debug);
if (rules)
{
if (!cache_rules_parse_json(rules, root))
{
cache_rules_free(rules);
rules = NULL;
}
}
return rules;
}
/**
* Parses the JSON object used for configuring the rules.
*
@ -1106,3 +1309,138 @@ static bool cache_rules_parse_use_element(CACHE_RULES *self, json_t *object, siz
return rule != NULL;
}
/**
* Remove quote characters surrounding a string.
* 'abcd' => abcd
* "abcd" => abcd
* `abcd` => abcd
*
* @param s The string to be dequoted.
*
* @note The string is modified in place.
*/
static bool dequote_mysql(char *s)
{
bool dequoted = true;
char *i = s;
char *end = s + strlen(s);
// Remove space from the beginning
while (*i && isspace(*i))
{
++i;
}
if (*i)
{
// Remove space from the end
while (isspace(*(end - 1)))
{
*(end - 1) = 0;
--end;
}
ss_dassert(end > i);
char quote;
switch (*i)
{
case '\'':
case '"':
case '`':
quote = *i;
++i;
break;
default:
quote = 0;
}
if (quote)
{
--end;
if (*end == quote)
{
*end = 0;
memmove(s, i, end - i + 1);
}
else
{
dequoted = false;
}
}
else if (i != s)
{
memmove(s, i, end - i + 1);
}
}
else
{
*s = 0;
}
return dequoted;
}
/**
* Convert MySQL/MariaDB account string to a pcre compatible one.
*
* @param pcre The string to which the conversion should be copied.
* To be on the safe size, the buffer should be twice the
* size of 'mysql'.
* @param mysql The mysql account string.
* @param approach Whether % should be converted or not.
*
* @return Whether or not the account contains a wildcard.
*/
static mysql_account_kind_t mysql_to_pcre(char *pcre, const char *mysql, pcre_quote_approach_t approach)
{
mysql_account_kind_t rv = MYSQL_ACCOUNT_WITHOUT_WILDCARD;
while (*mysql)
{
switch (*mysql)
{
case '%':
if (approach == PCRE_QUOTE_QUERY)
{
*pcre = '.';
pcre++;
*pcre = '*';
}
rv = MYSQL_ACCOUNT_WITH_WILDCARD;
break;
case '\'':
case '^':
case '.':
case '$':
case '|':
case '(':
case ')':
case '[':
case ']':
case '*':
case '+':
case '?':
case '{':
case '}':
*pcre++ = '\\';
// Flowthrough
default:
*pcre = *mysql;
}
++pcre;
++mysql;
}
*pcre = 0;
return rv;
}

View File

@ -59,11 +59,14 @@ typedef struct cache_rules
CACHE_RULE *use_rules; // The rules for when to use data from the cache.
} CACHE_RULES;
const char *cache_rule_attribute_to_string(cache_rule_attribute_t attribute);
const char *cache_rule_op_to_string(cache_rule_op_t op);
CACHE_RULES *cache_rules_create(uint32_t debug);
void cache_rules_free(CACHE_RULES *rules);
CACHE_RULES *cache_rules_load(const char *path, uint32_t debug);
CACHE_RULES *cache_rules_parse(const char *json, uint32_t debug);
bool cache_rules_should_store(CACHE_RULES *rules, const char *default_db, const GWBUF* query);
bool cache_rules_should_use(CACHE_RULES *rules, const SESSION *session);

View File

@ -0,0 +1,5 @@
add_executable(testrules testrules.c ../rules.c)
include_directories(..)
target_link_libraries(testrules maxscale-common jansson)
add_test(TestCache_rules testrules)

View File

@ -0,0 +1,102 @@
/*
* 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/bsl.
*
* 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 <stdlib.h>
#include "rules.h"
#include <log_manager.h>
#if !defined(SS_DEBUG)
#define SS_DEBUG
#endif
#include <skygw_debug.h>
struct test_case
{
const char* json;
struct
{
cache_rule_op_t op;
const char *value;
} expect;
};
#define TEST_CASE(op_from, from, op_to, to) \
{ "{ \"use\": [ { \"attribute\": \"user\", \"op\": \"" #op_from "\", \"value\": \"" #from "\" } ] }",\
{ op_to, #to } }
const struct test_case test_cases[] =
{
TEST_CASE(=, bob, CACHE_OP_LIKE, bob@.*),
TEST_CASE(=, 'bob', CACHE_OP_LIKE, bob@.*),
TEST_CASE(=, bob@%, CACHE_OP_LIKE, bob@.*),
TEST_CASE(=, 'bob'@'%.52', CACHE_OP_LIKE, bob@.*\\.52),
TEST_CASE(=, bob@127.0.0.1, CACHE_OP_EQ, bob@127.0.0.1),
TEST_CASE(=, b*b@127.0.0.1, CACHE_OP_EQ, b*b@127.0.0.1),
TEST_CASE(=, b*b@%.0.0.1, CACHE_OP_LIKE, b\\*b@.*\\.0\\.0\\.1),
TEST_CASE(=, b*b@%.0.%.1, CACHE_OP_LIKE, b\\*b@.*\\.0\\..*\\.1),
};
const size_t n_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
int test()
{
int errors = 0;
for (int i = 0; i < n_test_cases; ++i)
{
const struct test_case *test_case = &test_cases[i];
CACHE_RULES *rules = cache_rules_parse(test_case->json, 0);
ss_dassert(rules);
CACHE_RULE *rule = rules->use_rules;
ss_dassert(rule);
if (rule->op != test_case->expect.op)
{
printf("%s\nExpected: %s,\nGot : %s\n",
test_case->json,
cache_rule_op_to_string(test_case->expect.op),
cache_rule_op_to_string(rule->op));
++errors;
}
if (strcmp(rule->value, test_case->expect.value) != 0)
{
printf("%s\nExpected: %s,\nGot : %s\n",
test_case->json,
test_case->expect.value,
rule->value);
++errors;
}
}
return errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
int main()
{
int rc = EXIT_FAILURE;
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
{
rc = test();
mxs_log_finish();
}
else
{
printf("error: Could not initialize log.");
}
return rc;
}

View File

@ -1657,7 +1657,9 @@ static char* create_parse_error(FW_INSTANCE* my_instance,
{
char *msg = NULL;
char format[] = "dbfwfilter: Query could not be %s and will hence be rejected";
char format[] =
"dbfwfilter: Query could not be %s and will hence be rejected. "
"Please ensure that the SQL syntax is correct";
size_t len = sizeof(format) + strlen(reason); // sizeof includes the trailing NULL as well.
char message[len];
sprintf(message, format, reason);

View File

@ -1,3 +1,16 @@
/*
* 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/bsl.
*
* 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.
*/
/**
* MaxScale AVRO router
*

View File

@ -1,7 +1,14 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* 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/bsl.
*
* 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.
*/
/*

View File

@ -1,3 +1,16 @@
/*
* 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/bsl.
*
* 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.
*/
#ifndef _SHARDING_COMMON_HG
#define _SHARDING_COMMON_HG

View File

@ -77,6 +77,7 @@ typedef struct
bool failover; /**< If simple failover is enabled */
int failcount; /**< How many monitoring cycles servers must be
down before failover is initiated */
bool warn_failover; /**< Log a warning when failover happens */
} MYSQL_MONITOR;
#endif

View File

@ -275,6 +275,7 @@ startMonitor(MONITOR *monitor, const CONFIG_PARAMETER* params)
handle->mysql51_replication = false;
handle->failover = false;
handle->failcount = MYSQLMON_DEFAULT_FAILCOUNT;
handle->warn_failover = true;
memset(handle->events, false, sizeof(handle->events));
spinlock_init(&handle->lock);
}
@ -1093,18 +1094,21 @@ void do_failover(MYSQL_MONITOR *handle, MONITOR_SERVERS *db)
{
if (SERVER_IS_RUNNING(db->server))
{
if (!SERVER_IS_MASTER(db->server))
if (!SERVER_IS_MASTER(db->server) && handle->warn_failover)
{
MXS_WARNING("Failover initiated, server '%s' is now the master. "
"All other servers are set into maintenance mode.",
db->server->unique_name);
handle->warn_failover = false;
}
server_clear_set_status(db->server, SERVER_SLAVE, SERVER_MASTER);
monitor_set_pending_status(db, SERVER_MASTER);
monitor_clear_pending_status(db, SERVER_SLAVE);
}
else
{
server_set_status(db->server, SERVER_MAINT);
monitor_set_pending_status(db, SERVER_MAINT);
}
db = db->next;
@ -1395,6 +1399,10 @@ monitorMain(void *arg)
/** Other servers have died, initiate a failover to the last remaining server */
do_failover(handle, mon->databases);
}
else
{
handle->warn_failover = true;
}
}
ptr = mon->databases;

View File

@ -1,5 +1,19 @@
#ifndef MAXSCALE_TEST_H
#define MAXSCALE_TEST_H
/*
* 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/bsl.
*
* 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.
*/
#define TEST_DIR "@CMAKE_BINARY_DIR@"
#define TEST_LOG_DIR "@CMAKE_BINARY_DIR@/log"
#define TEST_BIN_DIR "@CMAKE_BINARY_DIR@/bin"