Merge branch '2.2' into develop

This commit is contained in:
Markus Mäkelä
2018-01-16 09:51:13 +02:00
24 changed files with 954 additions and 88 deletions

3
.gitignore vendored
View File

@ -59,4 +59,5 @@ nbproject/
# RBCommons # RBCommons
.reviewboardrc .reviewboardrc
# vscode
.vscode

26
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,26 @@
# Contributing to Maxscale
## Prerequisites
Basically, in order for us to be able to accept a contribution, it must be
licensed under [BSD-new](http://en.wikipedia.org/wiki/BSD_licenses). Upon
request, we can also provide a _contributor agreement_ for you to sign.
When you submit a pull request, add the following comment to your pull request.
> I am contributing the new code of the whole pull request, including one or
several files that are either new files or modified ones, under the BSD-new
license.
Without this comment, the pull request will not be accepted.
## Practicalities
* Please ensure that your pull-request has been made against the correct
branch. For bug fixes or minor improvements, use the default branch (at the
time of writing `2.1`). For new features, use the `develop` branch.
* Please ensure that your code follows our [Coding Style](https://github.com/mariadb-corporation/MaxScale/wiki/Coding-Style-and-Guidelines).
All new code should be formatted with the
[Astyle configuration](https://github.com/mariadb-corporation/MaxScale/wiki/Coding-Style-and-Guidelines#tldr)
provided with the MaxScale source code.

3
PULL_REQUEST_TEMPLATE.md Normal file
View File

@ -0,0 +1,3 @@
I am contributing the new code of the whole pull request, including one or
several files that are either new files or modified ones, under the BSD-new
license.

View File

@ -5,7 +5,7 @@
set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version") set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version")
set(MAXSCALE_VERSION_MINOR "1" CACHE STRING "Minor version") set(MAXSCALE_VERSION_MINOR "1" CACHE STRING "Minor version")
set(MAXSCALE_VERSION_PATCH "13" CACHE STRING "Patch version") set(MAXSCALE_VERSION_PATCH "14" CACHE STRING "Patch version")
# This should only be incremented if a package is rebuilt # This should only be incremented if a package is rebuilt
set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number") set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number")

View File

@ -58,19 +58,25 @@ find_program(DEBBUILD dpkg-buildpackage)
if(TARBALL) if(TARBALL)
include(cmake/package_tgz.cmake) include(cmake/package_tgz.cmake)
elseif (NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) OR NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" )) elseif(${RPMBUILD} MATCHES "NOTFOUND" AND ${DEBBUILD} MATCHES "NOTFOUND")
if(NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) ) message(FATAL_ERROR "Could not automatically resolve the package generator and no generators "
"defined on the command line. Please install distribution specific packaging software or "
"define -DTARBALL=Y to build tar.gz packages.")
else()
if(${DEBBUILD} MATCHES "NOTFOUND")
# No DEB packaging tools found, must be an RPM system
include(cmake/package_rpm.cmake) include(cmake/package_rpm.cmake)
else()
# We have DEB packaging tools, must be a DEB system
if (NOT ${RPMBUILD} MATCHES "NOTFOUND")
# Debian based systems can have both RPM and DEB packaging tools
message(WARNING "Found both DEB and RPM packaging tools, generating DEB packages. If this is not correct, "
"remove the packaging tools for the package type you DO NOT want to create.")
endif() endif()
if(NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" ) )
include(cmake/package_deb.cmake) include(cmake/package_deb.cmake)
endif() endif()
message(STATUS "You can install startup scripts and system configuration files for MaxScale by running the 'postinst' shell script located at ${CMAKE_INSTALL_PREFIX}.") message(STATUS "You can install startup scripts and system configuration files for MaxScale by running the 'postinst' shell script located at ${CMAKE_INSTALL_PREFIX}.")
message(STATUS "To remove these installed files, run the 'postrm' shell script located in the same folder.") message(STATUS "To remove these installed files, run the 'postrm' shell script located in the same folder.")
else()
message(FATAL_ERROR "Could not automatically resolve the package generator and no generators "
"defined on the command line. Please install distribution specific packaging software or "
"define -DTARBALL=Y to build tar.gz packages.")
endif() endif()

View File

@ -122,6 +122,7 @@ mysqlmon_failover_manual2_2
mysqlmon_failover_rejoin_old_slave mysqlmon_failover_rejoin_old_slave
mysqlmon_failover_rolling_master mysqlmon_failover_rolling_master
mysqlmon_failover_rolling_restart_slaves mysqlmon_failover_rolling_restart_slaves
mysqlmon_failover_stress
mysqlmon_switchover_bad_master mysqlmon_switchover_bad_master
mysqlmon_switchover mysqlmon_switchover
mxs1045 mxs1045

View File

@ -290,6 +290,8 @@ add_test_executable(mysqlmon_failover_rejoin_old_slave.cpp mysqlmon_failover_rej
# MySQL Monitor rolling restart slaves # MySQL Monitor rolling restart slaves
add_test_executable(mysqlmon_failover_rolling_restart_slaves.cpp mysqlmon_failover_rolling_restart_slaves mysqlmon_failover_rolling_restart_slaves LABELS mysqlmon REPL_BACKEND) add_test_executable(mysqlmon_failover_rolling_restart_slaves.cpp mysqlmon_failover_rolling_restart_slaves mysqlmon_failover_rolling_restart_slaves LABELS mysqlmon REPL_BACKEND)
add_test_executable(mysqlmon_failover_stress.cpp mysqlmon_failover_stress mysqlmon_failover_stress LABELS mysqlmon REPL_BACKEND)
# Test monitor state change events when manually clearing server bits # Test monitor state change events when manually clearing server bits
add_test_executable(false_monitor_state_change.cpp false_monitor_state_change replication LABELS mysqlmon REPL_BACKEND) add_test_executable(false_monitor_state_change.cpp false_monitor_state_change replication LABELS mysqlmon REPL_BACKEND)
@ -567,6 +569,10 @@ add_test_executable(mxs1516.cpp mxs1516 replication LABELS REPL_BACKEND)
# https://jira.mariadb.org/browse/MXS-1542 # https://jira.mariadb.org/browse/MXS-1542
add_test_executable(mxs1542.cpp mxs1542 avro LABELS REPL_BACKEND) add_test_executable(mxs1542.cpp mxs1542 avro LABELS REPL_BACKEND)
# MXS-1543: Avrorouter doesn't detect MIXED or STATEMENT format replication
# https://jira.mariadb.org/browse/MXS-1543
add_test_executable(mxs1543.cpp mxs1543 avro LABELS REPL_BACKEND)
# MXS-1585: Crash in MaxScale 2.1.12 # MXS-1585: Crash in MaxScale 2.1.12
# https://jira.mariadb.org/browse/MXS-1585 # https://jira.mariadb.org/browse/MXS-1585
add_test_executable(mxs1585.cpp mxs1585 mxs1585 LABELS REPL_BACKEND) add_test_executable(mxs1585.cpp mxs1585 mxs1585 LABELS REPL_BACKEND)

View File

@ -0,0 +1,94 @@
[maxscale]
threads=###threads###
[MySQL-Monitor]
type=monitor
module=mysqlmon
servers= server1, server2, server3, server4
user=maxskysql
passwd= skysql
monitor_interval=1000
allow_cluster_recovery=true
detect_standalone_master=true
auto_failover=true
auto_rejoin=true
replication_user=repl
replication_password=repl
backend_connect_timeout=5
backend_read_timeout=5
backend_write_timeout=5
[RW-Split-Router]
type=service
router= readwritesplit
servers=server1, server2, server3, server4
user=maxskysql
passwd=skysql
[Read-Connection-Router-Slave]
type=service
router=readconnroute
router_options= slave
servers=server1, server2, server3, server4
user=maxskysql
passwd=skysql
[Read-Connection-Router-Master]
type=service
router=readconnroute
router_options=master
servers=server1, server2, server3, server4
user=maxskysql
passwd=skysql
[RW-Split-Listener]
type=listener
service=RW-Split-Router
protocol=MySQLClient
port=4006
[Read-Connection-Listener-Slave]
type=listener
service=Read-Connection-Router-Slave
protocol=MySQLClient
port=4009
[Read-Connection-Listener-Master]
type=listener
service=Read-Connection-Router-Master
protocol=MySQLClient
port=4008
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
[server1]
type=server
address=###node_server_IP_1###
port=###node_server_port_1###
protocol=MySQLBackend
[server2]
type=server
address=###node_server_IP_2###
port=###node_server_port_2###
protocol=MySQLBackend
[server3]
type=server
address=###node_server_IP_3###
port=###node_server_port_3###
protocol=MySQLBackend
[server4]
type=server
address=###node_server_IP_4###
port=###node_server_port_4###
protocol=MySQLBackend

View File

@ -188,7 +188,7 @@ int Mariadb_nodes::read_env()
} }
else else
{ {
sprintf(start_db_command[i], "%s", "service mysql stop"); sprintf(stop_db_command[i], "%s", "service mysql stop");
} }
//reading cleanup_db_command //reading cleanup_db_command

View File

@ -70,7 +70,7 @@ do
else else
${mdbci_dir}/mdbci ssh --command 'echo \"/usr/sbin/mysqld \$* 2> stderr.log > stdout.log &\" > mysql_start.sh; echo \"sleep 20\" >> mysql_start.sh; echo \"disown\" >> mysql_start.sh; chmod a+x mysql_start.sh' $config_name/node_$num --silent ${mdbci_dir}/mdbci ssh --command 'echo \"/usr/sbin/mysqld \$* 2> stderr.log > stdout.log &\" > mysql_start.sh; echo \"sleep 20\" >> mysql_start.sh; echo \"disown\" >> mysql_start.sh; chmod a+x mysql_start.sh' $config_name/node_$num --silent
eval 'export $start_cmd_var="/home/$au/mysql_start.sh "' eval 'export $start_cmd_var="/home/$au/mysql_start.sh "'
eval 'export $start_cmd_var="killall mysqld "' eval 'export $stop_cmd_var="killall mysqld "'
fi fi
else else
eval 'export $start_cmd_var="$mysql_exe start "' eval 'export $start_cmd_var="$mysql_exe start "'

View File

@ -0,0 +1,32 @@
/**
* MXS-1543: https://jira.mariadb.org/browse/MXS-1543
*
* Avrorouter doesn't detect MIXED or STATEMENT format replication
*/
#include "testconnections.h"
int main(int argc, char** argv)
{
TestConnections::skip_maxscale_start(true);
TestConnections::check_nodes(false);
TestConnections test(argc, argv);
test.replicate_from_master();
test.repl->connect();
execute_query(test.repl->nodes[0], "CREATE OR REPLACE TABLE t1 (data VARCHAR(30))");
execute_query(test.repl->nodes[0], "INSERT INTO t1 VALUES ('ROW')");
execute_query(test.repl->nodes[0], "SET binlog_format=STATEMENT");
execute_query(test.repl->nodes[0], "FLUSH LOGS");
execute_query(test.repl->nodes[0], "INSERT INTO t1 VALUES ('STATEMENT')");
execute_query(test.repl->nodes[0], "SET binlog_format=ROW");
execute_query(test.repl->nodes[0], "FLUSH LOGS");
execute_query(test.repl->nodes[0], "INSERT INTO t1 VALUES ('ROW2')");
// Wait for the avrorouter to process the data
sleep(10);
test.check_log_err("Possible STATEMENT or MIXED", true);
return test.global_result;
}

View File

@ -0,0 +1,576 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <iostream>
#include <iterator>
#include <string>
#include <sstream>
#include <thread>
#include <vector>
#include "testconnections.h"
#include "fail_switch_rejoin_common.cpp"
using namespace std;
// How often the monitor checks the server state.
// NOTE: Ensure this is identical with the value in the configuration file.
const time_t MONITOR_INTERVAL = 1;
// After how many seconds should the failover/rejoin operation surely have
// been performed. Not very critical.
const time_t FAILOVER_DURATION = 5;
// How long should we keep in running.
const time_t TEST_DURATION = 90;
#define CMESSAGE(msg) \
do {\
stringstream ss;\
ss << "client(" << m_id << ") : " << msg << "\n";\
cout << ss.str() << flush;\
} while (false)
#if !defined(NDEBUG)
#define ss_dassert(x) do { if (!(x)) { fprintf(stderr, "Assertion failed: %s", #x); abort(); } } while(false)
#define ss_debug(x) x
#else
#define ss_dassert(s)
#define ss_debug(x)
#endif
namespace
{
class Client
{
public:
enum
{
DEFAULT_N_CLIENTS = 4,
DEFAULT_N_ROWS = 100
};
static void init(TestConnections& test, size_t nClients, size_t nRows)
{
s_nClients = nClients;
s_nRows = nRows;
if (create_tables(test))
{
if (insert_data(test))
{
cout << "\nSyncing slaves." << endl;
test.repl->sync_slaves();
}
}
}
static void start(bool verbose,
const char* zHost, int port, const char* zUser, const char* zPassword)
{
for (size_t i = 0; i < s_nClients; ++i)
{
s_threads.push_back(std::thread(&Client::thread_main,
i, verbose, zHost, port, zUser, zPassword));
}
}
static void stop()
{
s_shutdown = true;
for (size_t i = 0; i < s_nClients; ++i)
{
s_threads[i].join();
}
}
private:
Client(int id, bool verbose)
: m_id(id)
, m_verbose(verbose)
, m_value(1)
{
ss_debug(int rv);
unsigned int seed = (time(NULL) << m_id);
ss_debug(rv =) initstate_r(seed, m_initstate, sizeof(m_initstate), &m_random_data);
ss_dassert(rv == 0);
ss_debug(rv=) srandom_r(seed, &m_random_data);
ss_dassert(rv == 0);
}
enum action_t
{
ACTION_SELECT,
ACTION_UPDATE
};
action_t action() const
{
double d = random_decimal_fraction();
// 20% updates
// 80% selects
if (d <= 0.2)
{
return ACTION_UPDATE;
}
else
{
return ACTION_SELECT;
}
}
bool run(MYSQL* pConn)
{
bool rv = false;
switch (action())
{
case ACTION_SELECT:
rv = run_select(pConn);
break;
case ACTION_UPDATE:
rv = run_update(pConn);
break;
default:
ss_dassert(!true);
}
return rv;
}
bool run_select(MYSQL* pConn)
{
bool rv = true;
string stmt("SELECT * FROM test.t");
stmt += std::to_string(m_id);
stmt += " WHERE id=";
stmt += std::to_string(get_random_id());
if (mysql_query(pConn, stmt.c_str()) == 0)
{
flush_response(pConn);
}
else
{
if (m_verbose)
{
CMESSAGE("\"" << stmt << "\" failed: " << mysql_error(pConn));
}
rv = false;
}
return rv;
}
bool run_update(MYSQL* pConn)
{
bool rv = true;
string stmt("UPDATE test.t");
stmt += std::to_string(m_id);
stmt += " SET id=";
stmt += std::to_string(m_value);
stmt += " WHERE id=";
stmt += std::to_string(get_random_id());
m_value = (m_value + 1) % s_nRows;
if (mysql_query(pConn, stmt.c_str()) == 0)
{
flush_response(pConn);
}
else
{
if (m_verbose)
{
CMESSAGE("\"" << stmt << "\" failed: " << mysql_error(pConn));
}
rv = false;
}
return rv;
}
static void flush_response(MYSQL* pConn)
{
do
{
MYSQL_RES* pRes = mysql_store_result(pConn);
mysql_free_result(pRes);
}
while (mysql_next_result(pConn) == 0);
}
int get_random_id() const
{
int id = s_nRows * random_decimal_fraction();
ss_dassert(id >= 0);
ss_dassert(id <= s_nRows);
return id;
}
double random_decimal_fraction() const
{
int32_t r;
ss_debug(int rv=) random_r(&m_random_data, &r);
ss_dassert(rv == 0);
return double(r) / RAND_MAX;
}
void run(const char* zHost, int port, const char* zUser, const char* zPassword)
{
do
{
MYSQL* pMysql = mysql_init(NULL);
if (pMysql)
{
unsigned int timeout = 5;
mysql_options(pMysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
mysql_options(pMysql, MYSQL_OPT_READ_TIMEOUT, &timeout);
mysql_options(pMysql, MYSQL_OPT_WRITE_TIMEOUT, &timeout);
if (m_verbose)
{
CMESSAGE("Connecting");
}
if (mysql_real_connect(pMysql, zHost, zUser, zPassword, "test", port, NULL, 0))
{
if (m_verbose)
{
CMESSAGE("Connected.");
}
while (!s_shutdown && run(pMysql))
{
;
}
}
else
{
if (m_verbose)
{
CMESSAGE("mysql_real_connect() failed: " << mysql_error(pMysql));
}
}
if (m_verbose)
{
CMESSAGE("Closing");
}
mysql_close(pMysql);
}
else
{
CMESSAGE("mysql_init() failed.");
}
// To prevent some backend from becoming overwhelmed.
sleep(1);
}
while (!s_shutdown);
}
static void thread_main(int i, bool verbose,
const char* zHost, int port, const char* zUser, const char* zPassword)
{
if (mysql_thread_init() == 0)
{
Client client(i, verbose);
client.run(zHost, port, zUser, zPassword);
mysql_thread_end();
}
else
{
int m_id = i;
CMESSAGE("mysql_thread_init() failed.");
}
}
static bool create_tables(TestConnections& test)
{
cout << "\nCreating tables." << endl;
MYSQL* pConn = test.maxscales->conn_rwsplit[0];
string drop_head("DROP TABLE IF EXISTS test.t");
string create_head("CREATE TABLE test.t");
string create_tail(" (id INT)");
for (size_t i = 0; i < s_nClients; ++i)
{
string drop = drop_head + std::to_string(i);
test.try_query(pConn, drop.c_str());
string create = create_head + std::to_string(i) + create_tail;
test.try_query(pConn, create.c_str());
}
return test.ok();
}
static bool insert_data(TestConnections& test)
{
cout << "\nInserting data." << endl;
MYSQL* pConn = test.maxscales->conn_rwsplit[0];
for (size_t i = 0; i < s_nClients; ++i)
{
string insert("insert into test.t");
insert += std::to_string(i);
insert += " values ";
for (size_t j = 0; j < s_nRows; ++j)
{
insert += "(";
insert += std::to_string(j);
insert += ")";
if (j < s_nRows - 1)
{
insert += ", ";
}
}
test.try_query(pConn, insert.c_str());
}
return test.ok();
}
private:
enum
{
INITSTATE_SIZE = 32
};
size_t m_id;
bool m_verbose;
size_t m_value;
char m_initstate[INITSTATE_SIZE];
mutable struct random_data m_random_data;
static size_t s_nClients;
static size_t s_nRows;
static bool s_shutdown;
static std::vector<std::thread> s_threads;
};
size_t Client::s_nClients;
size_t Client::s_nRows;
bool Client::s_shutdown;
std::vector<std::thread> Client::s_threads;
}
namespace
{
void list_servers(TestConnections& test)
{
test.maxscales->execute_maxadmin_command_print(0, (char*)"list servers");
}
void sleep(int s)
{
cout << "Sleeping " << s << " times 1 second" << flush;
do
{
::sleep(1);
cout << "." << flush;
--s;
}
while (s > 0);
cout << endl;
}
bool check_server_status(TestConnections& test, int id)
{
bool is_master = false;
Mariadb_nodes* pRepl = test.repl;
string server = string("server") + std::to_string(id);
StringSet statuses = test.get_server_status(server.c_str());
std::ostream_iterator<string> oi(cout, " ");
cout << server << ": ";
std::copy(statuses.begin(), statuses.end(), oi);
cout << " => ";
if (statuses.count("Master"))
{
is_master = true;
cout << "OK";
}
else if (statuses.count("Slave"))
{
cout << "OK";
}
else if (statuses.count("Running"))
{
MYSQL* pConn = pRepl->nodes[id - 1];
char result[1024];
if (find_field(pConn, "SHOW SLAVE STATUS", "Last_IO_Error", result) == 0)
{
const char needle[] =
", which is not in the master's binlog. "
"Since the master's binlog contains GTIDs with higher sequence numbers, "
"it probably means that the slave has diverged due to executing extra "
"erroneous transactions";
if (strstr(result, needle))
{
// A rejoin was attempted, but it failed because the node (old master)
// had events that were not present in the new master. That is, a rejoin
// is not possible in principle without corrective action.
cout << "OK (could not be joined due to GTID issue)";
}
else
{
cout << result;
test.assert(false, "Merely 'Running' node did not error in expected way.");
}
}
else
{
test.assert(false, "Could not execute \"SHOW SLAVE STATUS\"");
}
}
else
{
test.assert(false, "Unexpected server state for %s.", server.c_str());
}
cout << endl;
return is_master;
}
void check_server_statuses(TestConnections& test)
{
int masters = 0;
masters += check_server_status(test, 1);
masters += check_server_status(test, 2);
masters += check_server_status(test, 3);
masters += check_server_status(test, 4);
test.assert(masters == 1, "Unpexpected number of masters: %d", masters);
}
void run(TestConnections& test)
{
int n_threads = Client::DEFAULT_N_CLIENTS;
cout << "\nConnecting to MaxScale." << endl;
test.maxscales->connect_maxscale();
Client::init(test, Client::DEFAULT_N_CLIENTS, Client::DEFAULT_N_ROWS);
if (test.ok())
{
const char* zHost = test.maxscales->IP[0];
int port = test.maxscales->rwsplit_port[0];
const char* zUser = test.maxscales->user_name;
const char* zPassword = test.maxscales->password;
cout << "Connecting to " << zHost << ":" << port << " as " << zUser << ":" << zPassword << endl;
cout << "Starting clients." << endl;
Client::start(test.verbose, zHost, port, zUser, zPassword);
time_t start = time(NULL);
list_servers(test);
while (time(NULL) - start < TEST_DURATION)
{
sleep(FAILOVER_DURATION);
int master_id = get_master_server_id(test);
if (master_id > 0 && master_id <= 4)
{
cout << "\nStopping node: " << master_id << endl;
test.repl->stop_node(master_id - 1);
sleep(2 * MONITOR_INTERVAL);
list_servers(test);
sleep(FAILOVER_DURATION);
list_servers(test);
sleep(FAILOVER_DURATION);
cout << "\nStarting node: " << master_id << endl;
test.repl->start_node(master_id - 1);
sleep(2 * MONITOR_INTERVAL);
list_servers(test);
sleep(FAILOVER_DURATION);
list_servers(test);
}
else
{
test.assert(false, "Unexpected master id: %d");
}
}
sleep(FAILOVER_DURATION);
cout << "\nStopping clients.\n" << flush;
Client::stop();
test.repl->close_connections();
test.repl->connect();
check_server_statuses(test);
}
}
}
int main(int argc, char* argv[])
{
std::ios::sync_with_stdio(true);
Mariadb_nodes::require_gtid(true);
TestConnections test(argc, argv);
run(test);
return test.global_result;
}

View File

@ -152,7 +152,7 @@ bool runtime_create_server(const char *name, const char *address, const char *po
} }
if (protocol == NULL) if (protocol == NULL)
{ {
protocol = "MySQLBackend"; protocol = "mariadbbackend";
} }
if (authenticator == NULL && (authenticator = get_default_authenticator(protocol)) == NULL) if (authenticator == NULL && (authenticator = get_default_authenticator(protocol)) == NULL)
{ {
@ -808,7 +808,7 @@ bool runtime_create_listener(SERVICE *service, const char *name, const char *add
} }
if (proto == NULL || strcasecmp(proto, CN_DEFAULT) == 0) if (proto == NULL || strcasecmp(proto, CN_DEFAULT) == 0)
{ {
proto = "MySQLClient"; proto = "mariadbclient";
} }
if (auth && strcasecmp(auth, CN_DEFAULT) == 0) if (auth && strcasecmp(auth, CN_DEFAULT) == 0)

View File

@ -21,6 +21,7 @@
#include <maxscale/semaphore.h> #include <maxscale/semaphore.h>
#include <maxscale/spinlock.h> #include <maxscale/spinlock.h>
#include <maxscale/thread.h> #include <maxscale/thread.h>
#include <maxscale/query_classifier.h>
#include <maxscale/json_api.h> #include <maxscale/json_api.h>
/** /**
@ -52,20 +53,30 @@ static THREAD hk_thr_handle;
static void hkthread(void *); static void hkthread(void *);
bool hkinit() struct hkinit_result
{ {
bool inited = false; sem_t sem;
bool ok;
};
if (thread_start(&hk_thr_handle, hkthread, NULL, 0) != NULL) bool
hkinit()
{ {
inited = true; struct hkinit_result res;
sem_init(&res.sem, 0, 0);
res.ok = false;
if (thread_start(&hk_thr_handle, hkthread, &res, 0) != NULL)
{
sem_wait(&res.sem);
} }
else else
{ {
MXS_ALERT("Failed to start housekeeper thread."); MXS_ALERT("Failed to start housekeeper thread.");
} }
return inited; sem_destroy(&res.sem);
return res.ok;
} }
int hktask_add(const char *name, void (*taskfn)(void *), void *data, int frequency) int hktask_add(const char *name, void (*taskfn)(void *), void *data, int frequency)
@ -214,6 +225,16 @@ void hkthread(void *data)
void *taskdata; void *taskdata;
int i; int i;
struct hkinit_result* res = (struct hkinit_result*)data;
res->ok = qc_thread_init(QC_INIT_BOTH);
if (!res->ok)
{
MXS_ERROR("Could not initialize housekeeper thread.");
}
sem_post(&res->sem);
while (!do_shutdown) while (!do_shutdown)
{ {
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++)
@ -253,6 +274,7 @@ void hkthread(void *data)
spinlock_release(&tasklock); spinlock_release(&tasklock);
} }
qc_thread_end(QC_INIT_BOTH);
MXS_NOTICE("Housekeeper shutting down."); MXS_NOTICE("Housekeeper shutting down.");
} }

View File

@ -259,7 +259,7 @@ size_t datetime_sizes[] =
*/ */
static void unpack_datetime(uint8_t *ptr, int length, struct tm *dest) static void unpack_datetime(uint8_t *ptr, int length, struct tm *dest)
{ {
int64_t val = 0; uint64_t val = 0;
uint32_t second, minute, hour, day, month, year; uint32_t second, minute, hour, day, month, year;
if (length == -1) if (length == -1)
@ -717,6 +717,7 @@ size_t unpack_decimal_field(uint8_t *ptr, uint8_t *metadata, double *val_float)
int fpart2 = decimals - fpart1 * dec_dig; int fpart2 = decimals - fpart1 * dec_dig;
int ibytes = ipart1 * 4 + dig_bytes[ipart2]; int ibytes = ipart1 * 4 + dig_bytes[ipart2];
int fbytes = fpart1 * 4 + dig_bytes[fpart2]; int fbytes = fpart1 * 4 + dig_bytes[fpart2];
int field_size = ibytes + fbytes;
/** Remove the sign bit and store it locally */ /** Remove the sign bit and store it locally */
bool negative = (ptr[0] & 0x80) == 0; bool negative = (ptr[0] & 0x80) == 0;
@ -735,7 +736,17 @@ size_t unpack_decimal_field(uint8_t *ptr, uint8_t *metadata, double *val_float)
} }
} }
int64_t val_i = unpack_bytes(ptr, ibytes); int64_t val_i = 0;
if (ibytes > 8)
{
int extra = ibytes - 8;
ptr += extra;
ibytes -= extra;
ss_dassert(ibytes == 8);
}
val_i = unpack_bytes(ptr, ibytes);
int64_t val_f = fbytes ? unpack_bytes(ptr + ibytes, fbytes) : 0; int64_t val_f = fbytes ? unpack_bytes(ptr + ibytes, fbytes) : 0;
if (negative) if (negative)
@ -746,5 +757,5 @@ size_t unpack_decimal_field(uint8_t *ptr, uint8_t *metadata, double *val_float)
*val_float = (double)val_i + ((double)val_f / (pow(10.0, decimals))); *val_float = (double)val_i + ((double)val_f / (pow(10.0, decimals)));
return ibytes + fbytes; return field_size;
} }

View File

@ -20,6 +20,11 @@
#include <maxscale/maxscale_test.h> #include <maxscale/maxscale_test.h>
#include <maxscale/log_manager.h> #include <maxscale/log_manager.h>
#include <maxscale/config.h> #include <maxscale/config.h>
#include <maxscale/query_classifier.h>
#include <maxscale/paths.h>
#include <maxscale/alloc.h>
#include <sys/stat.h>
#include "../internal/poll.h" #include "../internal/poll.h"
#include "../internal/statistics.h" #include "../internal/statistics.h"
@ -28,15 +33,17 @@
void init_test_env(char *path) void init_test_env(char *path)
{ {
int argc = 3;
const char* logdir = path ? path : TEST_LOG_DIR;
config_get_global_options()->n_threads = 1; config_get_global_options()->n_threads = 1;
ts_stats_init(); ts_stats_init();
mxs_log_init(NULL, logdir, MXS_LOG_TARGET_DEFAULT); if (!mxs_log_init(NULL, NULL, MXS_LOG_TARGET_STDOUT))
{
exit(1);
}
dcb_global_init(); dcb_global_init();
set_libdir(MXS_STRDUP(TEST_DIR "/query_classifier/qc_sqlite/"));
qc_setup(NULL, QC_SQL_MODE_DEFAULT, NULL);
qc_process_init(QC_INIT_BOTH);
poll_init(); poll_init();
maxscale::MessageQueue::init(); maxscale::MessageQueue::init();
maxscale::Worker::init(); maxscale::Worker::init();

View File

@ -1,39 +1,39 @@
# MaxScale documentation on GitHub: # MaxScale documentation:
# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Documentation-Contents.md # https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22/
# Global parameters # Global parameters
# #
# Complete list of configuration options: # Complete list of configuration options:
# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Getting-Started/Configuration-Guide.md # https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-mariadb-maxscale-configuration-usage-scenarios/
[maxscale] [maxscale]
threads=1 threads=auto
# Server definitions # Server definitions
# #
# Set the address of the server to the network # Set the address of the server to the network
# address of a MySQL server. # address of a MariaDB server.
# #
[server1] [server1]
type=server type=server
address=127.0.0.1 address=127.0.0.1
port=3306 port=3306
protocol=MySQLBackend protocol=MariaDBBackend
# Monitor for the servers # Monitor for the servers
# #
# This will keep MaxScale aware of the state of the servers. # This will keep MaxScale aware of the state of the servers.
# MySQL Monitor documentation: # MariaDB Monitor documentation:
# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Monitors/MySQL-Monitor.md # https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-mariadb-monitor/
[MySQL Monitor] [MariaDB-Monitor]
type=monitor type=monitor
module=mysqlmon module=mariadbmon
servers=server1 servers=server1
user=myuser user=myuser
passwd=mypwd passwd=mypwd
monitor_interval=10000 monitor_interval=2000
# Service definitions # Service definitions
# #
@ -42,9 +42,9 @@ monitor_interval=10000
# #
# ReadConnRoute documentation: # ReadConnRoute documentation:
# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Routers/ReadConnRoute.md # https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-readconnroute/
[Read-Only Service] [Read-Only-Service]
type=service type=service
router=readconnroute router=readconnroute
servers=server1 servers=server1
@ -53,21 +53,20 @@ passwd=mypwd
router_options=slave router_options=slave
# ReadWriteSplit documentation: # ReadWriteSplit documentation:
# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Routers/ReadWriteSplit.md # https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-readwritesplit/
[Read-Write Service] [Read-Write-Service]
type=service type=service
router=readwritesplit router=readwritesplit
servers=server1 servers=server1
user=myuser user=myuser
passwd=mypwd passwd=mypwd
max_slave_connections=100%
# This service enables the use of the MaxAdmin interface # This service enables the use of the MaxAdmin interface
# MaxScale administration guide: # MaxScale administration guide:
# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Reference/MaxAdmin.md # https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-maxadmin-admin-interface/
[MaxAdmin Service] [MaxAdmin-Service]
type=service type=service
router=cli router=cli
@ -77,20 +76,20 @@ router=cli
# services will listen on. # services will listen on.
# #
[Read-Only Listener] [Read-Only-Listener]
type=listener type=listener
service=Read-Only Service service=Read-Only-Service
protocol=MySQLClient protocol=MariaDBClient
port=4008 port=4008
[Read-Write Listener] [Read-Write-Listener]
type=listener type=listener
service=Read-Write Service service=Read-Write-Service
protocol=MySQLClient protocol=MariaDBClient
port=4006 port=4006
[MaxAdmin Listener] [MaxAdmin-Listener]
type=listener type=listener
service=MaxAdmin Service service=MaxAdmin-Service
protocol=maxscaled protocol=maxscaled
socket=default socket=default

View File

@ -180,7 +180,7 @@ int PamInstance::load_users(SERVICE* service)
"ON (u.user = t.user AND u.host = t.host) WHERE u.plugin = 'pam' " "ON (u.user = t.user AND u.host = t.host) WHERE u.plugin = 'pam' "
"ORDER BY user"; "ORDER BY user";
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
const int PAM_USERS_QUERY_NUM_FIELDS = 5; const unsigned int PAM_USERS_QUERY_NUM_FIELDS = 5;
#endif #endif
char *user, *pw; char *user, *pw;

View File

@ -280,6 +280,7 @@ RegexHintFilter::create(const char* name, char** options, MXS_CONFIG_PARAMETER*
} }
else if (legacy_mode && !mapping.size()) else if (legacy_mode && !mapping.size())
{ {
MXS_WARNING("Use of legacy parameters 'match' and 'server' is deprecated.");
/* Using legacy mode and no indexed parameters found. Add the legacy parameters /* Using legacy mode and no indexed parameters found. Add the legacy parameters
* to the mapping. */ * to the mapping. */
if (!regex_compile_and_add(pcre_ops, true, match_val_legacy, server_val_legacy, if (!regex_compile_and_add(pcre_ops, true, match_val_legacy, server_val_legacy,

View File

@ -23,6 +23,7 @@
*/ */
#include "avrorouter.h" #include "avrorouter.h"
#include <maxscale/query_classifier.h>
#include <binlog_common.h> #include <binlog_common.h>
#include <blr_constants.h> #include <blr_constants.h>
@ -1038,6 +1039,25 @@ void handle_query_event(AVRO_INSTANCE *router, REP_HEADER *hdr, int *pending_tra
len = tmpsz; len = tmpsz;
unify_whitespace(sql, len); unify_whitespace(sql, len);
static bool warn_not_row_format = true;
if (warn_not_row_format)
{
GWBUF* buffer = gwbuf_alloc(len + 5);
gw_mysql_set_byte3(GWBUF_DATA(buffer), len + 1);
GWBUF_DATA(buffer)[4] = 0x03;
memcpy(GWBUF_DATA(buffer) + 5, sql, len);
qc_query_op_t op = qc_get_operation(buffer);
gwbuf_free(buffer);
if (op == QUERY_OP_UPDATE || op == QUERY_OP_INSERT || op == QUERY_OP_DELETE)
{
MXS_WARNING("Possible STATEMENT or MIXED format binary log. Check that "
"'binlog_format' is set to ROW on the master.");
warn_not_row_format = false;
}
}
if (is_create_table_statement(router, sql, len)) if (is_create_table_statement(router, sql, len))
{ {
TABLE_CREATE *created = NULL; TABLE_CREATE *created = NULL;

View File

@ -17,6 +17,7 @@
#include <jansson.h> #include <jansson.h>
#include <maxscale/alloc.h> #include <maxscale/alloc.h>
#include <strings.h> #include <strings.h>
#include <signal.h>
#include <maxscale/utils.h> #include <maxscale/utils.h>
#define WRITE_EVENT 0 #define WRITE_EVENT 0
@ -107,6 +108,15 @@ bool handle_table_map_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr
TABLE_MAP *old = hashtable_fetch(router->table_maps, table_ident); TABLE_MAP *old = hashtable_fetch(router->table_maps, table_ident);
TABLE_MAP *map = table_map_alloc(ptr, ev_len, create); TABLE_MAP *map = table_map_alloc(ptr, ev_len, create);
MXS_ABORT_IF_NULL(map); // Fatal error at this point MXS_ABORT_IF_NULL(map); // Fatal error at this point
if (old && old->id == map->id && old->version == map->version &&
strcmp(old->table, map->table) == 0 &&
strcmp(old->database, map->database) == 0)
{
table_map_free(map);
return true;
}
char* json_schema = json_new_schema_from_table(map); char* json_schema = json_new_schema_from_table(map);
if (json_schema) if (json_schema)
@ -487,6 +497,19 @@ int get_metadata_len(uint8_t type)
} }
} }
// Make sure that both `i` and `trace` are defined before using this macro
#define check_overflow(t) do \
{ \
if (!(t)) \
{ \
for (long x = 0; x < i;x++) \
{ \
MXS_ALERT("%s", trace[x]); \
} \
raise(SIGABRT); \
} \
}while(false)
/** /**
* @brief Extract the values from a single row in a row event * @brief Extract the values from a single row in a row event
* *
@ -503,7 +526,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
{ {
int npresent = 0; int npresent = 0;
avro_value_t field; avro_value_t field;
long ncolumns = map->columns; long ncolumns = MXS_MIN(map->columns, create->columns);
uint8_t *metadata = map->column_metadata; uint8_t *metadata = map->column_metadata;
size_t metadata_offset = 0; size_t metadata_offset = 0;
@ -516,7 +539,10 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
ptr += (ncolumns + 7) / 8; ptr += (ncolumns + 7) / 8;
ss_dassert(ptr < end); ss_dassert(ptr < end);
for (long i = 0; i < map->columns && i < create->columns && npresent < ncolumns; i++) char trace[ncolumns][768];
memset(trace, 0, sizeof(trace));
for (long i = 0; i < ncolumns && npresent < ncolumns; i++)
{ {
ss_debug(int rc = )avro_value_get_by_name(record, create->column_names[i], &field, NULL); ss_debug(int rc = )avro_value_get_by_name(record, create->column_names[i], &field, NULL);
ss_dassert(rc == 0); ss_dassert(rc == 0);
@ -526,7 +552,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
npresent++; npresent++;
if (bit_is_set(null_bitmap, ncolumns, i)) if (bit_is_set(null_bitmap, ncolumns, i))
{ {
MXS_INFO("[%ld] NULL", i); sprintf(trace[i], "[%ld] NULL", i);
if (column_is_blob(map->column_types[i])) if (column_is_blob(map->column_types[i]))
{ {
uint8_t nullvalue = 0; uint8_t nullvalue = 0;
@ -548,9 +574,9 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
char strval[bytes * 2 + 1]; char strval[bytes * 2 + 1];
gw_bin2hex(strval, val, bytes); gw_bin2hex(strval, val, bytes);
avro_value_set_string(&field, strval); avro_value_set_string(&field, strval);
MXS_INFO("[%ld] ENUM: %lu bytes", i, bytes); sprintf(trace[i], "[%ld] ENUM: %lu bytes", i, bytes);
ptr += bytes; ptr += bytes;
ss_dassert(ptr < end); check_overflow(ptr < end);
} }
else else
{ {
@ -580,13 +606,13 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
bytes = *ptr++; bytes = *ptr++;
} }
MXS_INFO("[%ld] CHAR: field: %d bytes, data: %d bytes", i, field_length, bytes); sprintf(trace[i], "[%ld] CHAR: field: %d bytes, data: %d bytes", i, field_length, bytes);
char str[bytes + 1]; char str[bytes + 1];
memcpy(str, ptr, bytes); memcpy(str, ptr, bytes);
str[bytes] = '\0'; str[bytes] = '\0';
avro_value_set_string(&field, str); avro_value_set_string(&field, str);
ptr += bytes; ptr += bytes;
ss_dassert(ptr < end); check_overflow(ptr < end);
} }
} }
else if (column_is_bit(map->column_types[i])) else if (column_is_bit(map->column_types[i]))
@ -603,17 +629,17 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
MXS_WARNING("BIT is not currently supported, values are stored as 0."); MXS_WARNING("BIT is not currently supported, values are stored as 0.");
} }
avro_value_set_int(&field, value); avro_value_set_int(&field, value);
MXS_INFO("[%ld] BIT", i); sprintf(trace[i], "[%ld] BIT", i);
ptr += bytes; ptr += bytes;
ss_dassert(ptr < end); check_overflow(ptr < end);
} }
else if (column_is_decimal(map->column_types[i])) else if (column_is_decimal(map->column_types[i]))
{ {
double f_value = 0.0; double f_value = 0.0;
ptr += unpack_decimal_field(ptr, metadata + metadata_offset, &f_value); ptr += unpack_decimal_field(ptr, metadata + metadata_offset, &f_value);
avro_value_set_double(&field, f_value); avro_value_set_double(&field, f_value);
MXS_INFO("[%ld] DOUBLE", i); sprintf(trace[i], "[%ld] DECIMAL", i);
ss_dassert(ptr < end); check_overflow(ptr < end);
} }
else if (column_is_variable_string(map->column_types[i])) else if (column_is_variable_string(map->column_types[i]))
{ {
@ -630,13 +656,13 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
ptr++; ptr++;
} }
MXS_INFO("[%ld] VARCHAR: field: %d bytes, data: %lu bytes", i, bytes, sz); sprintf(trace[i], "[%ld] VARCHAR: field: %d bytes, data: %lu bytes", i, bytes, sz);
char buf[sz + 1]; char buf[sz + 1];
memcpy(buf, ptr, sz); memcpy(buf, ptr, sz);
buf[sz] = '\0'; buf[sz] = '\0';
ptr += sz; ptr += sz;
avro_value_set_string(&field, buf); avro_value_set_string(&field, buf);
ss_dassert(ptr < end); check_overflow(ptr < end);
} }
else if (column_is_blob(map->column_types[i])) else if (column_is_blob(map->column_types[i]))
{ {
@ -644,7 +670,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
uint64_t len = 0; uint64_t len = 0;
memcpy(&len, ptr, bytes); memcpy(&len, ptr, bytes);
ptr += bytes; ptr += bytes;
MXS_INFO("[%ld] BLOB: field: %d bytes, data: %lu bytes", i, bytes, len); sprintf(trace[i], "[%ld] BLOB: field: %d bytes, data: %lu bytes", i, bytes, len);
if (len) if (len)
{ {
avro_value_set_bytes(&field, ptr, len); avro_value_set_bytes(&field, ptr, len);
@ -655,7 +681,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
uint8_t nullvalue = 0; uint8_t nullvalue = 0;
avro_value_set_bytes(&field, &nullvalue, 1); avro_value_set_bytes(&field, &nullvalue, 1);
} }
ss_dassert(ptr < end); check_overflow(ptr < end);
} }
else if (column_is_temporal(map->column_types[i])) else if (column_is_temporal(map->column_types[i]))
{ {
@ -666,8 +692,8 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
create->column_lengths[i], &tm); create->column_lengths[i], &tm);
format_temporal_value(buf, sizeof(buf), map->column_types[i], &tm); format_temporal_value(buf, sizeof(buf), map->column_types[i], &tm);
avro_value_set_string(&field, buf); avro_value_set_string(&field, buf);
MXS_INFO("[%ld] %s: %s", i, column_type_to_string(map->column_types[i]), buf); sprintf(trace[i], "[%ld] %s: %s", i, column_type_to_string(map->column_types[i]), buf);
ss_dassert(ptr < end); check_overflow(ptr < end);
} }
/** All numeric types (INT, LONG, FLOAT etc.) */ /** All numeric types (INT, LONG, FLOAT etc.) */
else else
@ -677,11 +703,18 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
ptr += unpack_numeric_field(ptr, map->column_types[i], ptr += unpack_numeric_field(ptr, map->column_types[i],
&metadata[metadata_offset], lval); &metadata[metadata_offset], lval);
set_numeric_field_value(&field, 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); sprintf(trace[i], "[%ld] %s", i, column_type_to_string(map->column_types[i]));
check_overflow(ptr < end);
} }
ss_dassert(metadata_offset <= map->column_metadata_size); ss_dassert(metadata_offset <= map->column_metadata_size);
metadata_offset += get_metadata_len(map->column_types[i]); metadata_offset += get_metadata_len(map->column_types[i]);
} }
else
{
sprintf(trace[i], "[%ld] %s: Not present", i, column_type_to_string(map->column_types[i]));
}
MXS_INFO("%s", trace[i]);
} }
return ptr; return ptr;

View File

@ -311,10 +311,6 @@ void save_avro_schema(const char *path, const char* schema, TABLE_MAP *map)
} }
} }
} }
else
{
MXS_NOTICE("Schema version %d already exists: %s", map->version, filepath);
}
} }
/** /**
@ -421,28 +417,33 @@ static bool get_database_name(const char* sql, char* dest)
if (ptr) if (ptr)
{ {
ptr--; ptr--;
while (*ptr == '`' || isspace(*ptr)) while (ptr >= sql && (*ptr == '`' || isspace(*ptr)))
{ {
ptr--; ptr--;
} }
while (*ptr != '`' && *ptr != '.' && !isspace(*ptr)) while (ptr >= sql && *ptr != '`' && *ptr != '.' && !isspace(*ptr))
{ {
ptr--; ptr--;
} }
if (*ptr == '.') while (ptr >= sql && (*ptr == '`' || isspace(*ptr)))
{
ptr--;
}
if (ptr >= sql && *ptr == '.')
{ {
// The query defines an explicit database // The query defines an explicit database
while (*ptr == '`' || *ptr == '.' || isspace(*ptr)) while (ptr >= sql && (*ptr == '`' || *ptr == '.' || isspace(*ptr)))
{ {
ptr--; ptr--;
} }
const char* end = ptr + 1; const char* end = ptr + 1;
while (*ptr != '`' && *ptr != '.' && !isspace(*ptr)) while (ptr >= sql && *ptr != '`' && *ptr != '.' && !isspace(*ptr))
{ {
ptr--; ptr--;
} }
@ -702,6 +703,21 @@ TABLE_CREATE* table_create_from_schema(const char* file, const char* db,
return newtable; return newtable;
} }
int resolve_table_version(const char* db, const char* table)
{
int version = 0;
char buf[PATH_MAX + 1];
do
{
version++;
snprintf(buf, sizeof(buf), "%s.%s.%06d.avsc", db, table, version);
}
while (access(buf, F_OK) == 0);
return version;
}
/** /**
* @brief Handle a query event which contains a CREATE TABLE statement * @brief Handle a query event which contains a CREATE TABLE statement
* @param sql Query SQL * @param sql Query SQL
@ -757,7 +773,7 @@ TABLE_CREATE* table_create_alloc(const char* sql, int len, const char* db)
{ {
if ((rval = MXS_MALLOC(sizeof(TABLE_CREATE)))) if ((rval = MXS_MALLOC(sizeof(TABLE_CREATE))))
{ {
rval->version = 1; rval->version = resolve_table_version(db, table);
rval->was_used = false; rval->was_used = false;
rval->column_names = names; rval->column_names = names;
rval->column_lengths = lengths; rval->column_lengths = lengths;
@ -1445,6 +1461,8 @@ void table_map_free(TABLE_MAP *map)
if (map) if (map)
{ {
MXS_FREE(map->column_types); MXS_FREE(map->column_types);
MXS_FREE(map->column_metadata);
MXS_FREE(map->null_bitmap);
MXS_FREE(map->database); MXS_FREE(map->database);
MXS_FREE(map->table); MXS_FREE(map->table);
MXS_FREE(map); MXS_FREE(map);

View File

@ -63,7 +63,7 @@ static int maxinfo_statistics(INFO_INSTANCE *, INFO_SESSION *, GWBUF *);
static int maxinfo_ping(INFO_INSTANCE *, INFO_SESSION *, GWBUF *); static int maxinfo_ping(INFO_INSTANCE *, INFO_SESSION *, GWBUF *);
static int maxinfo_execute_query(INFO_INSTANCE *, INFO_SESSION *, char *); static int maxinfo_execute_query(INFO_INSTANCE *, INFO_SESSION *, char *);
static int handle_url(INFO_INSTANCE *instance, INFO_SESSION *router_session, GWBUF *queue); static int handle_url(INFO_INSTANCE *instance, INFO_SESSION *router_session, GWBUF *queue);
static int maxinfo_send_ok(DCB *dcb);
/* The router entry points */ /* The router entry points */
static MXS_ROUTER *createInstance(SERVICE *service, char **options); static MXS_ROUTER *createInstance(SERVICE *service, char **options);
@ -348,7 +348,7 @@ execute(MXS_ROUTER *rinstance, MXS_ROUTER_SESSION *router_session, GWBUF *queue)
switch (MYSQL_COMMAND(queue)) switch (MYSQL_COMMAND(queue))
{ {
case MXS_COM_PING: case MXS_COM_PING:
rc = maxinfo_ping(instance, session, queue); rc = maxinfo_send_ok(session->dcb);
break; break;
case MXS_COM_STATISTICS: case MXS_COM_STATISTICS:
rc = maxinfo_statistics(instance, session, queue); rc = maxinfo_statistics(instance, session, queue);
@ -622,7 +622,7 @@ maxinfo_execute_query(INFO_INSTANCE *instance, INFO_SESSION *session, char *sql)
respond_starttime(session->dcb); respond_starttime(session->dcb);
return 1; return 1;
} }
if (strcasecmp(sql, "set names 'utf8'") == 0) if (strncasecmp(sql, "set names", 9) == 0)
{ {
return maxinfo_send_ok(session->dcb); return maxinfo_send_ok(session->dcb);
} }
@ -630,6 +630,10 @@ maxinfo_execute_query(INFO_INSTANCE *instance, INFO_SESSION *session, char *sql)
{ {
return maxinfo_send_ok(session->dcb); return maxinfo_send_ok(session->dcb);
} }
if (strncasecmp(sql, "set @@session", 13) == 0)
{
return maxinfo_send_ok(session->dcb);
}
if (strncasecmp(sql, "set autocommit", 14) == 0) if (strncasecmp(sql, "set autocommit", 14) == 0)
{ {
return maxinfo_send_ok(session->dcb); return maxinfo_send_ok(session->dcb);

View File

@ -354,6 +354,13 @@ exec_flush(DCB *dcb, MAXINFO_TREE *tree)
int i; int i;
char errmsg[120]; char errmsg[120];
sprintf(errmsg, "Unsupported flush command '%s'", tree->value);
if(!tree)
{
maxinfo_send_error(dcb, 0, errmsg);
MXS_ERROR("%s", errmsg);
return;
}
for (i = 0; flush_commands[i].name; i++) for (i = 0; flush_commands[i].name; i++)
{ {
if (strcasecmp(flush_commands[i].name, tree->value) == 0) if (strcasecmp(flush_commands[i].name, tree->value) == 0)
@ -366,7 +373,6 @@ exec_flush(DCB *dcb, MAXINFO_TREE *tree)
{ {
tree->value[80] = 0; tree->value[80] = 0;
} }
sprintf(errmsg, "Unsupported flush command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg); maxinfo_send_error(dcb, 0, errmsg);
MXS_ERROR("%s", errmsg); MXS_ERROR("%s", errmsg);
} }