diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index c168fd075..049659306 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -335,7 +335,7 @@ class MySQL_Session MySQL_Backend * find_or_create_backend(int, MySQL_Data_Stream *_myds=NULL); void SQLite3_to_MySQL(SQLite3_result *, char *, int , MySQL_Protocol *, bool in_transaction=false, bool deprecate_eof_active=false); - void MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, MySQL_Data_Stream *_myds=NULL); + void MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, unsigned int warning_count, MySQL_Data_Stream *_myds=NULL); void MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Connection *myconn); unsigned int NumActiveTransactions(bool check_savpoint=false); bool HasOfflineBackends(); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 1c9befadb..f23845e47 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -4598,9 +4598,9 @@ void MySQL_Session::handler_minus1_GenerateErrorMessage(MySQL_Data_Stream *myds, switch (status) { case PROCESSING_QUERY: if (myconn) { - MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myds); + MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->warning_count, myds); } else { - MySQL_Result_to_MySQL_wire(NULL, NULL, myds); + MySQL_Result_to_MySQL_wire(NULL, NULL, 0, myds); } break; case PROCESSING_STMT_PREPARE: @@ -5076,7 +5076,7 @@ handler_again: switch (status) { case PROCESSING_QUERY: - MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->myds); + MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->warning_count, myconn->myds); break; case PROCESSING_STMT_PREPARE: { @@ -5165,7 +5165,7 @@ handler_again: break; // rc==2 : a multi-resultset (or multi statement) was detected, and the current statement is completed case 2: - MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->myds); + MySQL_Result_to_MySQL_wire(myconn->mysql, myconn->MyRS, myconn->warning_count, myconn->myds); if (myconn->MyRS) { // we also need to clear MyRS, so that the next staement will recreate it if needed if (myconn->MyRS_reuse) { delete myconn->MyRS_reuse; @@ -7355,7 +7355,7 @@ void MySQL_Session::MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Conn } } -void MySQL_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, MySQL_Data_Stream *_myds) { +void MySQL_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *MyRS, unsigned int warning_count, MySQL_Data_Stream *_myds) { if (mysql == NULL) { // error client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,client_myds->pkt_sid+1, 2013, (char *)"HY000" ,(char *)"Lost connection to MySQL server during query"); @@ -7413,7 +7413,7 @@ void MySQL_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *My setStatus |= SERVER_MORE_RESULTS_EXIST; setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,client_myds->pkt_sid+1,num_rows,mysql->insert_id, setStatus, mysql->warning_count,mysql->info); + client_myds->myprot.generate_pkt_OK(true,NULL,NULL,client_myds->pkt_sid+1,num_rows,mysql->insert_id, setStatus, warning_count, mysql->info); //client_myds->pkt_sid++; } else { // error diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 18f4b35dd..b8cbd147e 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -2018,6 +2018,12 @@ int MySQL_Connection::async_query(short event, char *stmt, unsigned long length, if (mysql_errno(mysql)) { return -1; } else { + if (myds && myds->sess && myds->sess->CurrentQuery.QueryParserArgs.digest_text) { + const bool handle_warnings_enabled = parent->myhgc->handle_warnings_enabled(); + if (handle_warnings_enabled && this->mysql && mysql_warning_count(this->mysql) > 0) { + warning_count = mysql_warning_count(this->mysql); + } + } return 0; } } @@ -2579,14 +2585,12 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { if (get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS) == false) { const bool handle_warnings_enabled = parent->myhgc->handle_warnings_enabled(); if (handle_warnings_enabled && this->mysql && mysql_warning_count(this->mysql) > 0) { - if (myds && myds->sess) { - // 'warning_in_hg' will be used if the next query is 'SHOW WARNINGS' or - // 'SHOW COUNT(*) WARNINGS' - myds->sess->warning_in_hg = myds->sess->current_hostgroup; - warning_count = mysql_warning_count(this->mysql); - // enabling multiplexing - set_status(true, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); - } + // 'warning_in_hg' will be used if the next query is 'SHOW WARNINGS' or + // 'SHOW COUNT(*) WARNINGS' + myds->sess->warning_in_hg = myds->sess->current_hostgroup; + //warning_count = mysql_warning_count(this->mysql); + // enabling multiplexing + set_status(true, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); } } else { // reset warning_in_hg const char* dig = myds->sess->CurrentQuery.QueryParserArgs.digest_text; @@ -2596,9 +2600,7 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { // on backend. if (!((dig_len == 22 && strncasecmp(dig, "SHOW COUNT(*) WARNINGS", 22) == 0) || (dig_len == 13 && strncasecmp(dig, "SHOW WARNINGS", 13) == 0))) { - if (myds && myds->sess) { - myds->sess->warning_in_hg = -1; - } + myds->sess->warning_in_hg = -1; warning_count = 0; // disabling multiplexing set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); diff --git a/test/tap/tests/test_warnings-t.cpp b/test/tap/tests/test_warnings-t.cpp index 9071ccccb..6ddc148b9 100644 --- a/test/tap/tests/test_warnings-t.cpp +++ b/test/tap/tests/test_warnings-t.cpp @@ -172,9 +172,46 @@ int check_proxysql_internal_session(MYSQL* proxysql, int exp_status) { return EXIT_SUCCESS; } +const std::vector mysql_variable_test = { + { ConnectionType::kAdmin, {"DELETE FROM mysql_query_rules", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL QUERY RULES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"SET mysql-handle_warnings=0", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kMySQL, {"DO 1/0", false}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kAdmin, {"SET mysql-handle_warnings=1", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { ConnectionType::kMySQL, {"SELECT 1" , true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kMySQL, {"DO 1/0", false}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { ConnectionType::kMySQL, {"DO 1", false}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) } +}; + +const std::vector hostgroup_attributes_test = { + { ConnectionType::kAdmin, {"SET mysql-handle_warnings=1", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"DELETE FROM mysql_hostgroup_attributes", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"INSERT INTO mysql_hostgroup_attributes (hostgroup_id, hostgroup_settings) VALUES (0, '{\"handle_warnings\":0}')", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL SERVERS TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + // Hostgroup attributes take precedence and should override the global variable value for the specified hostgroup. + { ConnectionType::kMySQL, {"DO 1/0", false}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled)}, + { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled)}, + { ConnectionType::kMySQL, {"DO 1", false}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kMySQL, {"SELECT 1", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kAdmin, {"SET mysql-handle_warnings=0", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"DELETE FROM mysql_hostgroup_attributes", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"INSERT INTO mysql_hostgroup_attributes (hostgroup_id, hostgroup_settings) VALUES (0, '{\"handle_warnings\":1}')", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL SERVERS TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kMySQL, {"DO 1/0", false}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) } // intentional +}; + const std::vector select_test = { { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, - { ConnectionType::kMySQL, {"SELECT 1" , true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) } + { ConnectionType::kMySQL, {"SELECT 1" , true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kMySQL, {"DO 1/0", false}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { ConnectionType::kMySQL, {"DO 1" , false}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) } }; const std::vector insert_test = { @@ -188,7 +225,7 @@ const std::vector insert_test = { }; const std::vector query_cache_test = { - { ConnectionType::kAdmin, {"SET mysql-query_cache_with_warnings_support=0", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"SET mysql-query_cache_handle_warnings=0", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"DELETE FROM mysql_query_rules", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"INSERT INTO mysql_query_rules (rule_id,active,match_digest,cache_ttl,apply) VALUES (1,1,'SELECT ?/?',60000,1)", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, @@ -198,7 +235,7 @@ const std::vector query_cache_test = { // this entry should not be saved in cache { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, { ConnectionType::kMySQL, {"SELECT 1", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, - { ConnectionType::kAdmin, {"SET mysql-query_cache_with_warnings_support=1", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"SET mysql-query_cache_handle_warnings=1", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, // resultset will be retrived from cache, with warning count zero @@ -242,12 +279,14 @@ int main(int argc, char** argv) { return -1; } - plan((7+2) + // select test: 7 warning checks, 2 multiplex status checks - (9+4) + // insert test: 9 warning checks, 4 multiplex status checks - (3+1) + // query digest test: 3 warning checks, 1 multiplex status checks - (18+5) + // query cache test: 18 warning checks, 5 multiplex status checks - (7+2) + // warning log test: 7 warning checks, 2 multiplex status checks - (7+3)); // multiplexing test: 7 warning checks, 3 multiplex status checks + plan((20 + 6) + // mysql variable test: 20 warning checks, 6 multiplex status checks + (20 + 6) + // hostgroup attributes test: 20 warning checks, 6 multiplex status checks + (14 + 4) + // select test: 14 warning checks, 4 multiplex status checks + (9 + 4) + // insert test: 9 warning checks, 4 multiplex status checks + (3 + 1) + // query digest test: 3 warning checks, 1 multiplex status checks + (18 + 5) + // query cache test: 18 warning checks, 5 multiplex status checks + (7 + 2) + // warning log test: 7 warning checks, 2 multiplex status checks + (7 + 3)); // multiplexing test: 7 warning checks, 3 multiplex status checks // Initialize Admin connection MYSQL* proxysql_admin = mysql_init(NULL); @@ -274,25 +313,31 @@ int main(int argc, char** argv) { return exit_status(); } - std::vector>> all_tests(6); + std::vector>> all_tests(8); - all_tests[0].first = "SELECT"; - all_tests[0].second.insert(all_tests[0].second.end(), select_test.begin(), select_test.end()); + all_tests[0].first = "MYSQL VARIABLE (mysql-handle_warnings)"; + all_tests[0].second.insert(all_tests[0].second.end(), mysql_variable_test.begin(), mysql_variable_test.end()); + + all_tests[1].first = "HOSTGROUP ATTRIBUTES (handle_warnings)"; + all_tests[1].second.insert(all_tests[1].second.end(), hostgroup_attributes_test.begin(), hostgroup_attributes_test.end()); + + all_tests[2].first = "SELECT"; + all_tests[2].second.insert(all_tests[2].second.end(), select_test.begin(), select_test.end()); - all_tests[1].first = "INSERT"; - all_tests[1].second.insert(all_tests[1].second.end(), insert_test.begin(), insert_test.end()); + all_tests[3].first = "INSERT"; + all_tests[3].second.insert(all_tests[3].second.end(), insert_test.begin(), insert_test.end()); - all_tests[2].first = "QUERY_DIGEST"; - all_tests[2].second.insert(all_tests[2].second.end(), query_digest_test.begin(), query_digest_test.end()); + all_tests[4].first = "QUERY_DIGEST"; + all_tests[4].second.insert(all_tests[4].second.end(), query_digest_test.begin(), query_digest_test.end()); - all_tests[3].first = "QUERY_CACHE"; - all_tests[3].second.insert(all_tests[3].second.end(), query_cache_test.begin(), query_cache_test.end()); + all_tests[5].first = "QUERY_CACHE"; + all_tests[5].second.insert(all_tests[5].second.end(), query_cache_test.begin(), query_cache_test.end()); - all_tests[4].first = "WARNING_LOGS"; - all_tests[4].second.insert(all_tests[4].second.end(), warning_log_test.begin(), warning_log_test.end()); + all_tests[6].first = "WARNING_LOGS"; + all_tests[6].second.insert(all_tests[6].second.end(), warning_log_test.begin(), warning_log_test.end()); - all_tests[5].first = "MULTIPLEXING"; - all_tests[5].second.insert(all_tests[5].second.end(), multiplexing_test.begin(), multiplexing_test.end()); + all_tests[7].first = "MULTIPLEXING"; + all_tests[7].second.insert(all_tests[7].second.end(), multiplexing_test.begin(), multiplexing_test.end()); for (const auto& test : all_tests) { diag("Executing [%s] test...", test.first);