diff --git a/test/tap/tests/mysql-set_transaction-t.cpp b/test/tap/tests/mysql-set_transaction-t.cpp new file mode 100644 index 000000000..b41198999 --- /dev/null +++ b/test/tap/tests/mysql-set_transaction-t.cpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +struct transaction_param { + struct next_transaction { + std::string set_transaction_val; + std::string exp_transaction_val; + }; + + std::string session_transaction_val; + std::vector next_transaction_val; +}; + +transaction_param next_trx_isolation_level_test[] = { + {"REPEATABLE READ", {{"", "REPEATABLE READ"}, {"SERIALIZABLE", "SERIALIZABLE"}, {"", "REPEATABLE READ"}}}, + {"REPEATABLE READ", {{"", "REPEATABLE READ"}, {"READ UNCOMMITTED", "READ UNCOMMITTED"}, {"", "REPEATABLE READ"}}}, + {"REPEATABLE READ", {{"", "REPEATABLE READ"}, {"READ COMMITTED", "READ COMMITTED"}, {"", "REPEATABLE READ"}}}, + {"SERIALIZABLE", {{"", "SERIALIZABLE"}, {"REPEATABLE READ", "REPEATABLE READ"}, {"", "SERIALIZABLE"}}}, +}; + +transaction_param next_trx_access_mode_test[] = { + {"READ WRITE", {{"", "READ WRITE"}, {"READ ONLY", "READ ONLY"}, {"", "READ WRITE"}}}, + {"READ WRITE", {{"", "READ WRITE"}, {"READ WRITE", "READ WRITE"}, {"", "READ WRITE"}}}, + {"READ ONLY", {{"", "READ ONLY"}, {"READ ONLY", "READ ONLY"}, {"", "READ ONLY"}}}, + {"READ ONLY", {{"", "READ ONLY"}, {"READ WRITE", "READ WRITE"}, {"", "READ ONLY"}}}, +}; + +int check_transaction_isolation_level(MYSQL* mysql) { + const std::string set_session_trx_isolation_level = "SET SESSION TRANSACTION ISOLATION LEVEL "; + const std::string set_next_trx_isolation_level = "SET TRANSACTION ISOLATION LEVEL "; + const std::string get_trx_isolation_level = "SELECT trx_isolation_level FROM information_schema.INNODB_TRX trx WHERE trx_mysql_thread_id=CONNECTION_ID();"; + const unsigned int test_size = sizeof(next_trx_isolation_level_test) / sizeof(transaction_param); + + for (unsigned int i = 0; i < test_size; i++) { + const transaction_param& param = next_trx_isolation_level_test[i]; + + if (param.session_transaction_val.empty() == false) { + MYSQL_QUERY(mysql, (set_session_trx_isolation_level + param.session_transaction_val).c_str()); + } + + for (const auto& isolation_level : param.next_transaction_val) { + if (isolation_level.set_transaction_val.empty() == false) { + MYSQL_QUERY(mysql, (set_next_trx_isolation_level + isolation_level.set_transaction_val).c_str()); + } + + MYSQL_QUERY(mysql, "BEGIN"); + MYSQL_QUERY(mysql, "INSERT INTO sbtest1 (id) VALUES (NULL)"); + MYSQL_QUERY(mysql, get_trx_isolation_level.c_str()); + + MYSQL_RES *res = mysql_store_result(mysql); + MYSQL_ROW row; + + const unsigned long long num_rows = mysql_num_rows(res); + ok(num_rows == 1, "check_transaction_isolation_level() -> mysql_num_rows(), expected: 1, actual: %llu", num_rows); + while ((row = mysql_fetch_row(res))) { + ok(strncmp(isolation_level.exp_transaction_val.c_str(), row[0], isolation_level.exp_transaction_val.size()) == 0, "check_transaction_isolation_level() -> row: expected: \"%s\", actual: \"%s\"", isolation_level.exp_transaction_val.c_str(), row[0]); + } + mysql_free_result(res); + MYSQL_QUERY(mysql, "ROLLBACK"); + sleep(1); + } + } + + return EXIT_SUCCESS; +} + +int check_transaction_access_mode(MYSQL* mysql) { + const char* access_mode_mapping[] = { "READ WRITE", "READ ONLY" }; + const std::string set_session_trx_access_mode = "SET SESSION TRANSACTION "; + const std::string set_next_trx_access_mode = "SET TRANSACTION "; + const std::string get_trx_access_mode = "SELECT trx_is_read_only FROM information_schema.INNODB_TRX trx WHERE trx_mysql_thread_id=CONNECTION_ID();"; + const unsigned int test_size = sizeof(next_trx_access_mode_test) / sizeof(transaction_param); + + for (unsigned int i = 0; i < test_size ; i++) { + const transaction_param& param = next_trx_access_mode_test[i]; + + if (param.session_transaction_val.empty() == false) { + MYSQL_QUERY(mysql, (set_session_trx_access_mode + param.session_transaction_val).c_str()); + } + + for (const auto& access_mode : param.next_transaction_val) { + if (access_mode.set_transaction_val.empty() == false) { + MYSQL_QUERY(mysql, (set_next_trx_access_mode + access_mode.set_transaction_val).c_str()); + } + + MYSQL_QUERY(mysql, "BEGIN"); + MYSQL_QUERY(mysql, "SELECT COUNT(*) FROM sbtest1"); + mysql_free_result(mysql_store_result(mysql)); + + MYSQL_QUERY(mysql, get_trx_access_mode.c_str()); + + MYSQL_RES *res = mysql_store_result(mysql); + MYSQL_ROW row; + + const unsigned long long num_rows = mysql_num_rows(res); + ok(num_rows == 1, "check_transaction_access_mode() -> mysql_num_rows(), expected: 1, actual: %llu", num_rows); + while ((row = mysql_fetch_row(res))) { + const char* access_mode_str = access_mode_mapping[atoi(row[0])]; + ok(strncmp(access_mode.exp_transaction_val.c_str(), access_mode_str, access_mode.exp_transaction_val.size()) == 0, "check_transaction_access_mode() -> row: expected: \"%s\", actual: \"%s\"", access_mode.exp_transaction_val.c_str(), access_mode_str); + } + mysql_free_result(res); + MYSQL_QUERY(mysql, "ROLLBACK"); + sleep(1); + } + } + return EXIT_SUCCESS; +} + +int main(int argc, char** argv) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + plan(48); + + MYSQL* mysql = mysql_init(NULL); + if (!mysql) + return exit_status(); + + if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "Failed to connect to database: Error: %s\n", + mysql_error(mysql)); + return exit_status(); + } + + if (create_table_test_sbtest1(0,mysql)) { + fprintf(stderr, "File %s, line %d, Error: create_table_test_sbtest1() failed\n", __FILE__, __LINE__); + return exit_status(); + } + + diag("Waiting few seconds for replication..."); + sleep(2); + MYSQL_QUERY(mysql, "USE test"); + + if (check_transaction_isolation_level(mysql)) { + fprintf(stderr, "check_transaction_isolation_level() failed\n"); + return exit_status(); + } + if (check_transaction_access_mode(mysql)) { + fprintf(stderr, "check_transaction_access_mode() failed\n"); + return exit_status(); + } + + mysql_close(mysql); + return exit_status(); +}