181 lines
4.5 KiB
C++
181 lines
4.5 KiB
C++
/*
|
|
* Copyright (c) 2019 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: 2023-11-12
|
|
*
|
|
* 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 <maxbase/assert.h>
|
|
#include <maxbase/semaphore.hh>
|
|
#include "mariadb_func.h"
|
|
#include "testconnections.h"
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
|
|
namespace
|
|
{
|
|
|
|
namespace Query
|
|
{
|
|
|
|
enum Status
|
|
{
|
|
SUCCESS, // Execution succeeded.
|
|
FAILURE, // Execution failed (e.g. broken SQL).
|
|
ERROR // Execution succeeded but ended with an error (e.g. deadlock).
|
|
};
|
|
|
|
struct Result : std::pair<Query::Status, std::string>
|
|
{
|
|
Result(Status status = Query::FAILURE)
|
|
{
|
|
first = status;
|
|
}
|
|
|
|
Result(const std::string& message)
|
|
{
|
|
mxb_assert(!message.empty());
|
|
first = Query::ERROR;
|
|
second = message;
|
|
}
|
|
};
|
|
|
|
Result execute(MYSQL* pConn, const char* zStmt)
|
|
{
|
|
Result rv;
|
|
|
|
if (mysql_query(pConn, zStmt) == 0)
|
|
{
|
|
MYSQL_RES* pRes = mysql_store_result(pConn);
|
|
|
|
rv.second = mysql_error(pConn);
|
|
|
|
if (rv.second.empty())
|
|
{
|
|
rv.first = Query::SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
rv.first = Query::ERROR;
|
|
}
|
|
|
|
mysql_free_result(pRes);
|
|
}
|
|
else
|
|
{
|
|
rv.second = mysql_error(pConn);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void execute(MYSQL* pConn, const char* zStmt, Query::Status expectation)
|
|
{
|
|
Result rv = execute(pConn, zStmt);
|
|
|
|
mxb_assert(rv.first == expectation);
|
|
}
|
|
|
|
}
|
|
|
|
enum class Expectation
|
|
{
|
|
SUCCESS,
|
|
FAILURE
|
|
};
|
|
|
|
void run_test(TestConnections& test, Expectation expectation)
|
|
{
|
|
maxbase::Semaphore sem1;
|
|
maxbase::Semaphore sem2;
|
|
|
|
std::thread t1([&test, &sem1, &sem2]() {
|
|
MYSQL* pConn = test.maxscales->open_rwsplit_connection();
|
|
mxb_assert(pConn);
|
|
mysql_autocommit(pConn, false);
|
|
|
|
Query::execute(pConn, "BEGIN", Query::SUCCESS);
|
|
Query::execute(pConn, "INSERT INTO mxs2512 VALUES(1)", Query::SUCCESS);
|
|
sem1.post();
|
|
sem2.wait();
|
|
|
|
// First we sleep to be sure that t2 has time to issue its SELECT (that will block).
|
|
sleep(5);
|
|
Query::execute(pConn, "INSERT INTO mxs2512 VALUES(0)", Query::SUCCESS);
|
|
|
|
mysql_close(pConn);
|
|
});
|
|
|
|
Query::Result rv;
|
|
std::thread t2([&test, &sem1, &sem2, &rv]() {
|
|
MYSQL* pConn = test.maxscales->open_rwsplit_connection();
|
|
mxb_assert(pConn);
|
|
mysql_autocommit(pConn, false);
|
|
|
|
Query::execute(pConn, "BEGIN", Query::SUCCESS);
|
|
sem1.wait();
|
|
sem2.post();
|
|
|
|
rv = Query::execute(pConn, "SELECT * from mxs2512 FOR UPDATE");
|
|
|
|
mysql_close(pConn);
|
|
});
|
|
|
|
t1.join();
|
|
t2.join();
|
|
|
|
if (expectation == Expectation::FAILURE)
|
|
{
|
|
test.expect(rv.first == Query::ERROR, "SELECT did NOT fail.");
|
|
}
|
|
else
|
|
{
|
|
test.expect(rv.first == Query::SUCCESS, "SELECT DID fail.");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
TestConnections test(argc, argv);
|
|
|
|
MYSQL* pConn = test.maxscales->open_rwsplit_connection();
|
|
test.expect(pConn, "Could not connect to rwsplit.");
|
|
|
|
// Preparations
|
|
test.try_query(pConn, "DROP TABLE IF EXISTS mxs2512");
|
|
test.try_query(pConn, "CREATE TABLE mxs2512 (x INT PRIMARY KEY)");
|
|
|
|
// Test with 'transaction_replay=false' => should fail.
|
|
cout << "Testing with 'transaction_replay=false', SELECT should fail." << endl;
|
|
run_test(test, Expectation::FAILURE);
|
|
|
|
// Intermediate cleanup; delete contents from table, turn on transaction replay, restart MaxScale.
|
|
test.try_query(pConn, "DELETE FROM mxs2512");
|
|
mysql_close(pConn);
|
|
test.stop_maxscale();
|
|
const char* zSed = "sed -i -e 's/transaction_replay=false/transaction_replay=true/' /etc/maxscale.cnf";
|
|
test.add_result(test.maxscales->ssh_node(0, zSed, true), "Could not tweak /etc/maxscale.cnf");
|
|
test.start_maxscale();
|
|
|
|
// Test with 'transaction_replay=true' => should succeed.
|
|
cout << "Testing with 'transaction_replay=true', SELECT should succeed." << endl;
|
|
run_test(test, Expectation::SUCCESS);
|
|
|
|
// Final cleanup
|
|
pConn = test.maxscales->open_rwsplit_connection();
|
|
test.expect(pConn, "Could not connect to rwsplit.");
|
|
test.try_query(pConn, "DROP TABLE mxs2512");
|
|
mysql_close(pConn);
|
|
|
|
return test.global_result;
|
|
}
|