#include #include #include #include #include #include #include "mysql.h" #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)) { if (!mysql_real_connect(mysql, cl.root_host, cl.root_username, cl.root_password, NULL, cl.root_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(); }