Merge branch '2.2' into develop
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -59,4 +59,5 @@ nbproject/
|
|||||||
|
|
||||||
# RBCommons
|
# RBCommons
|
||||||
.reviewboardrc
|
.reviewboardrc
|
||||||
|
# vscode
|
||||||
|
.vscode
|
||||||
|
26
CONTRIBUTING.md
Normal file
26
CONTRIBUTING.md
Normal 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
3
PULL_REQUEST_TEMPLATE.md
Normal 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.
|
@ -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")
|
||||||
|
@ -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()
|
||||||
|
1
maxscale-system-test/.gitignore
vendored
1
maxscale-system-test/.gitignore
vendored
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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 "'
|
||||||
|
32
maxscale-system-test/mxs1543.cpp
Normal file
32
maxscale-system-test/mxs1543.cpp
Normal 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;
|
||||||
|
}
|
576
maxscale-system-test/mysqlmon_failover_stress.cpp
Executable file
576
maxscale-system-test/mysqlmon_failover_stress.cpp
Executable 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;
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user