Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closes #3549: Autocommit tracking being lost when 'locked_on_hostgroup' is set #3560

Merged
merged 4 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/MySQL_Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4436,6 +4436,14 @@ int MySQL_Session::handler() {

handler_rc0_Process_GTID(myconn);

// if we are locked on hostgroup, the value of autocommit is copied from the backend connection
// see bug #3549
if (locked_on_hostgroup >= 0) {
assert(myconn != NULL);
assert(myconn->mysql != NULL);
autocommit = myconn->mysql->server_status & SERVER_STATUS_AUTOCOMMIT;
}

if (mirror == false) {
// Support for LAST_INSERT_ID()
if (myconn->mysql->insert_id) {
Expand Down
8 changes: 8 additions & 0 deletions test/tap/tap/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
} \
} while(0)

#define MYSQL_QUERY_err(mysql, query) \
do { \
if (mysql_query(mysql, query)) { \
fprintf(stderr, "File %s, line %d, Error: %s\n", \
__FILE__, __LINE__, mysql_error(mysql)); \
} \
} while(0)

#ifdef __cplusplus
extern "C" {
#endif
Expand Down
301 changes: 301 additions & 0 deletions test/tap/tests/reg_test_3549-autocommit_tracking-t.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
/**
* @file reg_test_3549-autocommit_tracking-t.cpp
* @brief This test verifies that ProxySQL is properly tracking autocommit being
* set, being properly forwarded to the client after changing its status.
*
* TODO: This test should serve as the template from which construct a more
* complete test for 'autocommit' tracking, for client and backend sides.
*/

#include <cstring>
#include <vector>
#include <string>
#include <stdio.h>

#include <mysql.h>

#include "proxysql_utils.h"
#include "tap.h"
#include "command_line.h"
#include "utils.h"
#include "json.hpp"

using nlohmann::json;

using query_spec = std::tuple<std::string, int>;

void fetch_and_discard_results(MYSQL_RES* result, bool verbose=false) {
MYSQL_ROW row = nullptr;
unsigned int num_fields = 0;
unsigned int i = 0;
unsigned int j = 0;

num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result))) {
unsigned long *lengths = mysql_fetch_lengths(result);

if (verbose) {
printf("# RowNum_%d: ", j);
}

for(i = 0; i < num_fields; i++) {
if (verbose) {
printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
}
}

if (verbose) {
printf("\n");
}

j++;
}
}

/**
* @brief Execute the supplied queries and fetch and ignore the data in case of
* being necessary.
*
* @param proxysql An already opened MYSQL connection to ProxySQL.
*
* @param queries The query to be executed.
*/
int execute_and_fetch_query(MYSQL* proxysql, const std::string& query) {
int query_err = mysql_query(proxysql, query.c_str());
if (query_err == EXIT_SUCCESS) {
MYSQL_RES* result = mysql_store_result(proxysql);
if (result) {
fetch_and_discard_results(result, false);
mysql_free_result(result);
}
}

return query_err;
}

void parse_result_json_column(MYSQL_RES *result, json& j) {
if(!result) return;
MYSQL_ROW row;

while ((row = mysql_fetch_row(result))) {
j = json::parse(row[0]);
}
}

bool check_client_autocommit(MYSQL* proxysql) {
if (proxysql == NULL) return false;
return proxysql->server_status & SERVER_STATUS_AUTOCOMMIT;
}

using test_spec = std::vector<std::pair<std::string, int>>;

/**
* @brief Executes the provided queries specs, and logs if an error is found with
* the query execution, or the value expected after the query doesn't match.
*
* @param proxysql An already opened MYSQL connection to ProxySQL.
* @param queries_specs The queries specs to execute and check.
*
* @return 'EXIT_SUCCESS' if all the queries were properly executed,
* 'EXIT_FAILURE' otherwise.
*/
int execute_queries_specs(MYSQL* proxysql, const test_spec& queries_specs) {
for (const auto& query_spec : queries_specs) {
std::string query = std::get<0>(query_spec);
int exp_autocommit = std::get<1>(query_spec);

int myerr = execute_and_fetch_query(proxysql, query);
if (myerr != EXIT_SUCCESS) {
diag(
"Query failed to be executed:"
" (query: '%s', exp_err: '%d', act_err: '%d', err_msg: '%s', line: '%d')",
query.c_str(), EXIT_SUCCESS, mysql_errno(proxysql), mysql_error(proxysql), __LINE__
);
return EXIT_FAILURE;
} else {
bool autocommit = check_client_autocommit(proxysql);
if (autocommit != exp_autocommit) {
diag(
"Unexpected autocommit value for:"
" (query: '%s', exp_autocommit: '%d', autocommit: '%d', line: '%d')",
query.c_str(), exp_autocommit, autocommit, __LINE__
);
return EXIT_FAILURE;
}
}
}

return EXIT_SUCCESS;
}

/**
* @brief The tests definition.
*/
std::vector<std::pair<std::string, test_spec>> test_definitions {
{
// Check if autocommit is properly set by using simple queries,
// target to be handled by 'handler_special_queries' in ProxySQL side.
"simple_set_autocommit_no_lock",
{
{ "SET autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 },
{ "SET autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 }
}
},
{
"simple_set_autocommit_no_lock_2",
{
{ "SET autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
{ "SET autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 }
}
},
{
"simple_set_autocommit_lock_1",
{
{ "SET @session_var=1", 1 },
{ "SET autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 },
{ "SET autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 }
}
},
{
"simple_set_autocommit_lock_2",
{
{ "SET @session_var=1", 1 },
{ "SET autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
{ "SET autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 }
}
},
{
// Check if autocommit is properly set by using complex queries,
// target to be handled by 'handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo'
// in ProxySQL side.
"complex_set_autocommit_no_lock_1",
{
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 }
}
},
{
"complex_set_autocommit_no_lock_2",
{
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 }
}
},
{
"complex_set_autocommit_lock_1",
{
{ "SET @session_var=1", 1 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 }
}
},
{
"complex_set_autocommit_lock_2",
{
{ "SET @session_var=1", 1 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 }
}
},
{
"mix_set_autocommit_1",
{
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 },
{ "SET autocommit=0", 0 },
{ "COMMIT", 0 },
{ "SET autocommit=1", 1 },
{ "BEGIN", 1 },
{ "SET @session_var=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 },
{ "COMMIT", 1 },
}
},
{
"mix_set_autocommit_2",
{
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
{ "SET time_zone='+04:00', character_set_client='latin1', max_join_size=10000, autocommit=1", 1 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 1 },
{ "SET autocommit=0", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
{ "BEGIN", 0 },
{ "SET @session_var=1", 0 },
{ "SELECT /*+ ;hostgroup=0 */ 1", 0 },
{ "COMMIT", 0 },
}
},
};

/**
* @brief Execute the supplied test definition on the provided, already oppened
* MySQL connection to ProxySQL.
*
* @param proxysql An already oppened connection to ProxySQL.
* @param test_def The test definition to be verified.
*/
void execute_test_definition(MYSQL* proxysql, std::pair<std::string, test_spec> test_def) {
int queries_res = execute_queries_specs(proxysql, test_def.second);
std::string t_ok_msg {
"Autocommit should match expected values for '%s' queries"
};
std::string ok_msg {};
string_format(t_ok_msg, ok_msg, test_def.first.c_str());

ok(
queries_res == EXIT_SUCCESS,
"%s", ok_msg.c_str()
);
}

int main(int argc, char** argv) {
CommandLine cl;

if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return -1;
}

for (const auto& test_def : test_definitions) {
MYSQL* proxysql_mysql = mysql_init(NULL);
if (!mysql_real_connect(proxysql_mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(
stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)
);
return EXIT_FAILURE;
}

// perform the next test
execute_test_definition(proxysql_mysql, test_def);

mysql_close(proxysql_mysql);
}

return exit_status();
}