From 6b295477d6ba665d114d1e5f432b5b23d5e9b235 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Fri, 20 Oct 2023 11:51:08 +0500 Subject: [PATCH 01/18] Enhanced Warnings handling in ProxySQL: * Implemented 'SHOW WARNINGS' and 'SHOW COUNT(*) FROM WARNINGS' query handling. * When a query produces warnings, the warning count is now included in the query response. Additionally, a new variable 'mysql-query_cache_with_warnings_support' has been introduced for the query cache feature: * A value of '0' means that if a query execution produces warnings, the resultset will not be saved in the query cache. * A value of '1' indicates that if a query produces warnings, the resultset will be saved in the query cache, but the warning count in that resultset will be set to '0'. ** Note: * Warning handling is dependent on the query digest and 'mysql-query_digests' must be enabled for it to work. * Warnings saved in logfile ['mysql-log_mysql_warnings_enabled'] and the current feature can work in conjunction with each other. --- include/MySQL_Session.h | 2 + include/MySQL_Thread.h | 1 + include/mysql_connection.h | 1 + include/proxysql_structs.h | 2 + lib/MySQL_Protocol.cpp | 8 ++- lib/MySQL_Session.cpp | 124 +++++++++++++++++++++++++++++++++---- lib/MySQL_Thread.cpp | 5 ++ lib/Query_Cache.cpp | 26 ++++++++ lib/mysql_connection.cpp | 33 +++++++++- 9 files changed, 187 insertions(+), 15 deletions(-) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index c5a8023409..8b58f5f52a 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -274,6 +274,7 @@ class MySQL_Session int to_process; int pending_connect; enum proxysql_session_type session_type; + int warning_in_hg; // bool bool autocommit; @@ -379,6 +380,7 @@ class MySQL_Session bool has_any_backend(); void detected_broken_connection(const char *file, unsigned int line, const char *func, const char *action, MySQL_Connection *myconn, int myerr, const char *message, bool verbose=false); void generate_status_one_hostgroup(int hid, std::string& s); + void reset_warning_hostgroup_flag_and_release_connection(); friend void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt); }; diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index 2b60ebb033..999c744fb4 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -581,6 +581,7 @@ class MySQL_Threads_Handler char * ssl_p2s_crlpath; int query_cache_size_MB; int query_cache_soft_ttl_pct; + int query_cache_with_warnings_support; int min_num_servers_lantency_awareness; int aurora_max_lag_ms_only_read_from_replicas; bool stats_time_backend_query; diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 9fd9a66eba..c783a7d640 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -19,6 +19,7 @@ using json = nlohmann::json; #define STATUS_MYSQL_CONNECTION_FOUND_ROWS 0x00000200 #define STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG 0x00000400 #define STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT 0x00000800 +#define STATUS_MYSQL_CONNECTION_HAS_WARNINGS 0x00001000 class Variable { public: diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index d8ba71318b..63532d4e6e 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -869,6 +869,7 @@ __thread int mysql_thread___client_host_error_counts; /* variables used for Query Cache */ __thread int mysql_thread___query_cache_size_MB; __thread int mysql_thread___query_cache_soft_ttl_pct; +__thread int mysql_thread___query_cache_with_warnings_support; /* variables used for SSL , from proxy to server (p2s) */ __thread char * mysql_thread___ssl_p2s_ca; @@ -1036,6 +1037,7 @@ extern __thread int mysql_thread___client_host_error_counts; /* variables used for Query Cache */ extern __thread int mysql_thread___query_cache_size_MB; extern __thread int mysql_thread___query_cache_soft_ttl_pct; +extern __thread int mysql_thread___query_cache_with_warnings_support; /* variables used for SSL , from proxy to server (p2s) */ extern __thread char * mysql_thread___ssl_p2s_ca; diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 0b820d2ab4..9284e24967 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -3001,11 +3001,13 @@ void MySQL_ResultSet::add_eof() { //PSarrayOUT->add(pkt.ptr,pkt.size); //sid++; //resultset_size+=pkt.size; - + + // warnings count will only be sent to the client if mysql-query_digests is enabled + const bool warnings_enabled = myds->sess->CurrentQuery.QueryParserArgs.digest_text != nullptr; if (deprecate_eof_active) { PtrSize_t pkt; buffer_to_PSarrayOut(); - myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, 0, NULL, true); + myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, (warnings_enabled ? mysql->warning_count : 0), NULL, true); PSarrayOUT.add(pkt.ptr, pkt.size); resultset_size += pkt.size; } @@ -3015,7 +3017,7 @@ void MySQL_ResultSet::add_eof() { // note that EOF is added on a packet on its own, instead of using a buffer, // so that can be removed using remove_last_eof() buffer_to_PSarrayOut(); - myprot->generate_pkt_EOF(false, NULL, NULL, sid, 0, setStatus, this); + myprot->generate_pkt_EOF(false, NULL, NULL, sid, (warnings_enabled ? mysql->warning_count : 0), setStatus, this); resultset_size += 9; buffer_to_PSarrayOut(); } diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index b65cc171c6..66fed0ab7e 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -588,6 +588,7 @@ MySQL_Session::MySQL_Session() { mirrorPkt.ptr=NULL; mirrorPkt.size=0; set_status(session_status___NONE); + warning_in_hg = -1; idle_since = 0; transaction_started_at = 0; @@ -640,6 +641,7 @@ void MySQL_Session::reset() { autocommit_handled=false; sending_set_autocommit=false; autocommit_on_hostgroup=-1; + warning_in_hg = -1; current_hostgroup=-1; default_hostgroup=-1; locked_on_hostgroup=-1; @@ -923,7 +925,7 @@ bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { // specific connection, for that, we update 'current_hostgroup' with the first active transaction we are // able to find. If more transactions are simultaneously open for the session, more 'COMMIT|ROLLBACK' // commands are required to be issued by the client to continue ending transactions. - unsigned int hg = FindOneActiveTransaction(true); + int hg = FindOneActiveTransaction(true); if (hg != -1) { // there is an active transaction, we must forward the request current_hostgroup = hg; @@ -1124,6 +1126,7 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["last_HG_affected_rows"] = last_HG_affected_rows; j["active_transactions"] = active_transactions; j["transaction_time_ms"] = thread->curtime - transaction_started_at; + j["warning_in_hg"] = warning_in_hg; j["gtid"]["hid"] = gtid_hid; j["gtid"]["last"] = ( strlen(gtid_buf) ? gtid_buf : "" ); j["qpo"]["create_new_connection"] = qpo->create_new_conn; @@ -1246,6 +1249,7 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["backends"][i]["conn"]["status"]["no_multiplex_HG"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); j["backends"][i]["conn"]["status"]["compression"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); j["backends"][i]["conn"]["status"]["prepared_statement"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT); + j["backends"][i]["conn"]["status"]["has_warnings"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS); j["backends"][i]["conn"]["MultiplexDisabled"] = _myconn->MultiplexDisabled(); j["backends"][i]["conn"]["ps"]["backend_stmt_to_global_ids"] = _myconn->local_stmts->backend_stmt_to_global_ids; j["backends"][i]["conn"]["ps"]["global_stmt_to_backend_ids"] = _myconn->local_stmts->global_stmt_to_backend_ids; @@ -1538,19 +1542,41 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { return true; } } - if ( (pkt->size == 18) && (strncasecmp((char *)"SHOW WARNINGS",(char *)pkt->ptr+5,13)==0) ) { - SQLite3_result * resultset=new SQLite3_result(3); - resultset->add_column_definition(SQLITE_TEXT,"Level"); - resultset->add_column_definition(SQLITE_TEXT,"Code"); - resultset->add_column_definition(SQLITE_TEXT,"Message"); + // if query digest is disabled, warnings in ProxySQL are also deactivated, + // resulting in an empty response being sent to the client. + if ((pkt->size == 18) && (strncasecmp((char*)"SHOW WARNINGS", (char*)pkt->ptr + 5, 13) == 0) && + CurrentQuery.QueryParserArgs.digest_text == nullptr) { + SQLite3_result* resultset = new SQLite3_result(3); + resultset->add_column_definition(SQLITE_TEXT, "Level"); + resultset->add_column_definition(SQLITE_TEXT, "Code"); + resultset->add_column_definition(SQLITE_TEXT, "Message"); SQLite3_to_MySQL(resultset, NULL, 0, &client_myds->myprot, false, deprecate_eof_active); delete resultset; - client_myds->DSS=STATE_SLEEP; - status=WAITING_CLIENT_DATA; - if (mirror==false) { + client_myds->DSS = STATE_SLEEP; + status = WAITING_CLIENT_DATA; + if (mirror == false) { RequestEnd(NULL); } - l_free(pkt->size,pkt->ptr); + l_free(pkt->size, pkt->ptr); + return true; + } + // if query digest is disabled, warnings in ProxySQL are also deactivated, + // resulting in zero warning count sent to the client. + if ((pkt->size == 27) && (strncasecmp((char*)"SHOW COUNT(*) WARNINGS", (char*)pkt->ptr + 5, 22) == 0) && + CurrentQuery.QueryParserArgs.digest_text == nullptr) { + SQLite3_result* resultset = new SQLite3_result(1); + resultset->add_column_definition(SQLITE_TEXT, "@@session.warning_count"); + char* pta[1]; + pta[0] = (char*)"0"; + resultset->add_row(pta); + SQLite3_to_MySQL(resultset, NULL, 0, &client_myds->myprot, false, deprecate_eof_active); + delete resultset; + client_myds->DSS = STATE_SLEEP; + status = WAITING_CLIENT_DATA; + if (mirror == false) { + RequestEnd(NULL); + } + l_free(pkt->size, pkt->ptr); return true; } // 'LOAD DATA LOCAL INFILE' is unsupported. We report an specific error to inform clients about this fact. For more context see #833. @@ -5985,26 +6011,83 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C if (pkt->size > (unsigned int) mysql_thread___max_allowed_packet) { handler_WCD_SS_MCQ_qpo_LargePacket(pkt); + reset_warning_hostgroup_flag_and_release_connection(); return true; } if (qpo->OK_msg) { handler_WCD_SS_MCQ_qpo_OK_msg(pkt); + reset_warning_hostgroup_flag_and_release_connection(); return true; } if (qpo->error_msg) { handler_WCD_SS_MCQ_qpo_error_msg(pkt); + reset_warning_hostgroup_flag_and_release_connection(); return true; } if (prepared) { // for prepared statement we exit here + reset_warning_hostgroup_flag_and_release_connection(); goto __exit_set_destination_hostgroup; } // handle here #509, #815 and #816 if (CurrentQuery.QueryParserArgs.digest_text) { char *dig=CurrentQuery.QueryParserArgs.digest_text; + const size_t dig_len=strlen(dig); + if ((dig_len == 13) && (strncasecmp(dig, "SHOW WARNINGS", 13) == 0)) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Intercepted '%s'\n", dig); + if (warning_in_hg > -1) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); + current_hostgroup = warning_in_hg; + //warning_in_hg = -1; + return false; + } else { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); + std::unique_ptr resultset(new SQLite3_result(3)); + resultset->add_column_definition(SQLITE_TEXT, "Level"); + resultset->add_column_definition(SQLITE_TEXT, "Code"); + resultset->add_column_definition(SQLITE_TEXT, "Message"); + SQLite3_to_MySQL(resultset.get(), NULL, 0, &client_myds->myprot, false, (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF)); + client_myds->DSS = STATE_SLEEP; + status = WAITING_CLIENT_DATA; + if (mirror == false) { + RequestEnd(NULL); + } + l_free(pkt->size, pkt->ptr); + return true; + } + } + + if ((dig_len == 22) && (strncasecmp(dig, "SHOW COUNT(*) WARNINGS", 22) == 0)) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Intercepted '%s'\n", dig); + if (warning_in_hg > -1) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); + current_hostgroup = warning_in_hg; + //warning_in_hg = -1; + return false; + } + else { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); + std::unique_ptr resultset(new SQLite3_result(1)); + resultset->add_column_definition(SQLITE_TEXT, "@@session.warning_count"); + char* pta[1]; + pta[0] = (char*)"0"; + resultset->add_row(pta); + SQLite3_to_MySQL(resultset.get(), NULL, 0, &client_myds->myprot, false, (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF)); + client_myds->DSS = STATE_SLEEP; + status = WAITING_CLIENT_DATA; + if (mirror == false) { + RequestEnd(NULL); + } + l_free(pkt->size, pkt->ptr); + return true; + } + } + + reset_warning_hostgroup_flag_and_release_connection(); + unsigned int nTrx=NumActiveTransactions(); if ((locked_on_hostgroup == -1) && (strncasecmp(dig,(char *)"SET ",4)==0)) { // this code is executed only if locked_on_hostgroup is not set yet @@ -7288,7 +7371,9 @@ void MySQL_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *My assert(resultset_completed); // the resultset should always be completed if MySQL_Result_to_MySQL_wire is called if (transfer_started==false) { // we have all the resultset when MySQL_Result_to_MySQL_wire was called if (qpo && qpo->cache_ttl>0 && com_field_list==false) { // the resultset should be cached - if (mysql_errno(mysql)==0) { // no errors + if (mysql_errno(mysql)==0 && + (mysql_warning_count(mysql)==0 || + mysql_thread___query_cache_with_warnings_support==1)) { // no errors if ( (qpo->cache_empty_result==1) || ( @@ -8069,3 +8154,20 @@ void MySQL_Session::generate_status_one_hostgroup(int hid, std::string& s) { s = j_res.dump(); delete resultset; } + +void MySQL_Session::reset_warning_hostgroup_flag_and_release_connection() +{ + if (warning_in_hg > -1) { + // if we've reached this point, it means that warning was found in the previous query, but the + // current executed query is not 'SHOW WARNINGS' or 'SHOW COUNT(*) FROM WARNINGS', so we can safely reset warning_in_hg and + // return connection back to the connection pool. + MySQL_Backend* _mybe = NULL; + _mybe = find_backend(warning_in_hg); + MySQL_Data_Stream* myds = _mybe->server_myds; + myds->myconn->set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); + if ((myds->myconn->reusable == true) && myds->myconn->IsActiveTransaction() == false && myds->myconn->MultiplexDisabled() == false) { + myds->return_MySQL_Connection_To_Pool(); + } + warning_in_hg = -1; + } +} diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 5d6e470beb..5e019abac9 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -553,6 +553,7 @@ static char * mysql_thread_variables_names[]= { (char *)"long_query_time", (char *)"query_cache_size_MB", (char *)"query_cache_soft_ttl_pct", + (char *)"query_cache_with_warnings_support", (char *)"ping_interval_server_msec", (char *)"ping_timeout_server", (char *)"default_schema", @@ -1149,6 +1150,7 @@ MySQL_Threads_Handler::MySQL_Threads_Handler() { variables.long_query_time=1000; variables.query_cache_size_MB=256; variables.query_cache_soft_ttl_pct=0; + variables.query_cache_with_warnings_support=0; variables.init_connect=NULL; variables.ldap_user_variable=NULL; variables.add_ldap_user_comment=NULL; @@ -2285,6 +2287,8 @@ char ** MySQL_Threads_Handler::get_variables_list() { VariablesPointers_int["max_transaction_time"] = make_tuple(&variables.max_transaction_time, 1000, 20*24*3600*1000, false); VariablesPointers_int["query_cache_size_mb"] = make_tuple(&variables.query_cache_size_MB, 0, 1024*10240, false); VariablesPointers_int["query_cache_soft_ttl_pct"] = make_tuple(&variables.query_cache_soft_ttl_pct, 0, 100, false); + VariablesPointers_int["query_cache_with_warnings_support"] = make_tuple(&variables.query_cache_with_warnings_support, 0, 1, false); + #ifdef IDLE_THREADS VariablesPointers_int["session_idle_ms"] = make_tuple(&variables.session_idle_ms, 1, 3600*1000, false); #endif // IDLE_THREADS @@ -4033,6 +4037,7 @@ void MySQL_Thread::refresh_variables() { mysql_thread___long_query_time=GloMTH->get_variable_int((char *)"long_query_time"); mysql_thread___query_cache_size_MB=GloMTH->get_variable_int((char *)"query_cache_size_MB"); mysql_thread___query_cache_soft_ttl_pct=GloMTH->get_variable_int((char *)"query_cache_soft_ttl_pct"); + mysql_thread___query_cache_with_warnings_support =GloMTH->get_variable_int((char*)"query_cache_with_warnings_support"); mysql_thread___ping_interval_server_msec=GloMTH->get_variable_int((char *)"ping_interval_server_msec"); mysql_thread___ping_timeout_server=GloMTH->get_variable_int((char *)"ping_timeout_server"); mysql_thread___shun_on_failures=GloMTH->get_variable_int((char *)"shun_on_failures"); diff --git a/lib/Query_Cache.cpp b/lib/Query_Cache.cpp index 60c0302abe..327a0f46f3 100644 --- a/lib/Query_Cache.cpp +++ b/lib/Query_Cache.cpp @@ -728,8 +728,34 @@ bool Query_Cache::set(uint64_t user_hash, const unsigned char *kp, uint32_t kl, if (hdr.pkt_length < 9 && *payload == 0xfe) { if (deprecate_eof_active) { entry->ok_pkt_offset = it - vp; + + // Reset the warning flags to zero before storing resultset in the cache + // Reason: When a warning flag is set, it may prompt the client to invoke "SHOW WARNINGS" or "SHOW COUNT(*) FROM WARNINGS". + // However, when retrieving data from the cache, it's possible that there are no warnings present + // that might be associated with previous interactions. + unsigned char* payload_temp = payload+1; + + // skip affected_rows + payload_temp += mysql_decode_length(payload_temp, nullptr); + + // skip last_insert_id + payload_temp += mysql_decode_length(payload_temp, nullptr); + + // skip stats_flags + payload_temp += sizeof(uint16_t); + + uint16_t warnings = 0; + memcpy(payload_temp, &warnings, sizeof(uint16_t)); + } else { entry->row_eof_pkt_offset = it - vp; + + // Reset the warning flags to zero before storing resultset in the cache + // Reason: When a warning flag is set, it may prompt the client to invoke "SHOW WARNINGS" or "SHOW COUNT(*) FROM WARNINGS". + // However, when retrieving data from the cache, it's possible that there are no warnings present + // that might be associated with previous interactions. + uint16_t warnings = 0; + memcpy((payload + 1), &warnings, sizeof(uint16_t)); } break; } else { diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index d1aa96368c..44a4623da4 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -2448,7 +2448,10 @@ bool MySQL_Connection::MultiplexDisabled(bool check_delay_token) { // status_flags stores information about the status of the connection // can be used to determine if multiplexing can be enabled or not bool ret=false; - if (status_flags & (STATUS_MYSQL_CONNECTION_TRANSACTION|STATUS_MYSQL_CONNECTION_USER_VARIABLE|STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT|STATUS_MYSQL_CONNECTION_LOCK_TABLES|STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE|STATUS_MYSQL_CONNECTION_GET_LOCK|STATUS_MYSQL_CONNECTION_NO_MULTIPLEX|STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0|STATUS_MYSQL_CONNECTION_FOUND_ROWS|STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG|STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT) ) { + if (status_flags & (STATUS_MYSQL_CONNECTION_TRANSACTION | STATUS_MYSQL_CONNECTION_USER_VARIABLE | STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT | + STATUS_MYSQL_CONNECTION_LOCK_TABLES | STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE | STATUS_MYSQL_CONNECTION_GET_LOCK | STATUS_MYSQL_CONNECTION_NO_MULTIPLEX | + STATUS_MYSQL_CONNECTION_SQL_LOG_BIN0 | STATUS_MYSQL_CONNECTION_FOUND_ROWS | STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG | + STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT | STATUS_MYSQL_CONNECTION_HAS_WARNINGS) ) { ret=true; } if (check_delay_token && auto_increment_delay_token) return true; @@ -2569,6 +2572,34 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { } } } + // checking warnings and disabling multiplexing will be effective only when the mysql-query_digests is enabled + if (myds->sess->CurrentQuery.QueryParserArgs.digest_text) { + if (get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS) == false) { + if (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; + // enabling multiplexing + set_status(true, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); + } + } + } else { // reset warning_in_hg + const char* dig = myds->sess->CurrentQuery.QueryParserArgs.digest_text; + const size_t dig_len = strlen(dig); + // disable multiplexing and reset the 'warning_in_hg' flag only when the current executed query is not + // 'SHOW WARNINGS' or 'SHOW COUNT(*) WARNINGS', as these queries do not clear the warning message list + // 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; + } + // disabling multiplexing + set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); + } + } + } if (get_status(STATUS_MYSQL_CONNECTION_USER_VARIABLE)==false) { // we search for variables only if not already set // if ( // strncasecmp(query_digest_text,"SELECT @@tx_isolation", strlen("SELECT @@tx_isolation")) From b668021dade3a4bb768afc4012d72667b133c35e Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Sat, 21 Oct 2023 14:29:38 +0500 Subject: [PATCH 02/18] Added TAP test --- test/tap/tests/test_warnings-t.cpp | 345 +++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 test/tap/tests/test_warnings-t.cpp diff --git a/test/tap/tests/test_warnings-t.cpp b/test/tap/tests/test_warnings-t.cpp new file mode 100644 index 0000000000..992dee1dc5 --- /dev/null +++ b/test/tap/tests/test_warnings-t.cpp @@ -0,0 +1,345 @@ +/** + * @file test-warnings-t.cpp + * @brief This test will test warnings support in ProxySQL + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "json.hpp" +#include +#include +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using LEVEL = std::string; +using CODE = int; +using MESSAGE = std::string; + +#define MYSQL_QUERY__(mysql, query) \ + do { \ + if (mysql_query(mysql, query)) { \ + fprintf(stderr, "File %s, line %d, Error: %s\n", \ + __FILE__, __LINE__, mysql_error(mysql)); \ + goto cleanup; \ + } \ + } while(0) + +#define MYSQL_CLEAR_RESULT(mysql) do { MYSQL_RES* mysql_result = mysql_use_result(mysql); \ + while (MYSQL_ROW row = mysql_fetch_row(mysql_result)) {} \ + mysql_free_result(mysql_result); \ + } while(0) + + +enum MultiplexStatus { + kNotApplicable = 0, + kMultiplexingDisabled = (1 << 0), + kMultiplexingEnabled = (1 << 1), + kHasWarnings = (1 << 2), + kUserVariables = (1 << 3) +}; + +enum ConnectionType { + kAdmin, + kMySQL +}; + +enum class WarningCheckType { + kNotApplicable = 0, + kConnection = (1 << 0), + kCountQuery = (1 << 1), + kShowWarnings = (1 << 2), + kAll = (kConnection | kCountQuery | kShowWarnings) +}; + +struct QueryInfo { + const char* query; + bool is_select; +}; + +struct WarningCheckInfo { + WarningCheckType type; + int warning_count; + std::vector warning_codes; +}; + +struct TestInfo { + ConnectionType conn_type; + QueryInfo query_info; + WarningCheckInfo warning_check_info; + int multiplex_status; +}; + + +void parse_result_json_column(MYSQL_RES* result, nlohmann::json& j) { + if (!result) return; + while (MYSQL_ROW row = mysql_fetch_row(result)) { + j = nlohmann::json::parse(row[0]); + } +} + +int execute_query(MYSQL* proxysql, const QueryInfo& query_info) { + MYSQL_QUERY(proxysql, query_info.query); + if (query_info.is_select) { + MYSQL_CLEAR_RESULT(proxysql); + } + return EXIT_SUCCESS; +} + +// get warning count from MySQL connection (MYSQL::warning_count) +int get_warnings_count_from_connection(MYSQL* mysql) { + return mysql_warning_count(mysql); +} + +// retrieve warning count through a query. This action does not clear the warning message list. +int get_warnings_count(MYSQL* mysql) { + MYSQL_QUERY(mysql, "SHOW COUNT(*) WARNINGS"); + MYSQL_RES* mysql_result = mysql_use_result(mysql); + if (!mysql_result) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); + return -1; + } + MYSQL_ROW row = mysql_fetch_row(mysql_result); + const int warning_count = atoi(row[0]); + if (mysql_result) { + mysql_free_result(mysql_result); + mysql_result = nullptr; + } + return warning_count; +} + +// retrieve warning message list. This action does not clear the warning message list. +int get_warnings(MYSQL* mysql, std::list>& warning_list) { + MYSQL_QUERY(mysql, "SHOW WARNINGS"); + MYSQL_RES* mysql_result = mysql_use_result(mysql); + unsigned long fetched_row_count = 0; + while (MYSQL_ROW row = mysql_fetch_row(mysql_result)) { + fetched_row_count++; + warning_list.emplace_back(std::make_tuple(std::string(row[0]),atoi(row[1]),std::string(row[2]))); + } + if (mysql_result) { + mysql_free_result(mysql_result); + mysql_result = nullptr; + } + return fetched_row_count; +} + +// check multiplexing status +int check_proxysql_internal_session(MYSQL* proxysql, int exp_status) { + nlohmann::json j_status{}; + MYSQL_RES* res = nullptr; + int status{}; + + bool found_backend = false; + + MYSQL_QUERY(proxysql, "PROXYSQL INTERNAL SESSION"); + res = mysql_store_result(proxysql); + parse_result_json_column(res, j_status); + mysql_free_result(res); + + + if (j_status.contains("backends")) { + for (auto& backend : j_status["backends"]) { + if (backend != nullptr && backend.contains("conn")) { + found_backend = true; + + if (backend["conn"]["MultiplexDisabled"]) { + status |= MultiplexStatus::kMultiplexingEnabled; + } + + if (backend["conn"]["status"]["has_warnings"] && j_status["warning_in_hg"] != -1) { + status |= MultiplexStatus::kHasWarnings; + } + + if (backend["conn"]["status"]["user_variable"]) { + status |= MultiplexStatus::kUserVariables; + } + } + } + } + + if (found_backend == false) { + status |= MultiplexStatus::kMultiplexingDisabled; + } + + ok(status == exp_status, "Multiplex status matches. Expected status:'%d' Actual status:'%d'", exp_status, status); + + return EXIT_SUCCESS; +} + +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) } +}; + +const std::vector insert_test = { + { ConnectionType::kMySQL, {"SET sql_mode='ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kMySQL, {"DROP DATABASE IF EXISTS testdb", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kMySQL, {"CREATE DATABASE testdb", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kMySQL, {"CREATE TABLE testdb.t1 (a TINYINT NOT NULL, b CHAR(4))", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kMySQL, {"INSERT INTO testdb.t1 VALUES(10, 'mysql'), (NULL, 'test'), (300, 'xyz')", false}, {WarningCheckType::kAll, 3, {1265,1048,1264}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { ConnectionType::kMySQL, {"SELECT 1", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kMySQL, {"DROP DATABASE IF EXISTS testdb", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingDisabled) } +}; + +const std::vector query_cache_test = { + { ConnectionType::kAdmin, {"SET mysql-query_cache_with_warnings_support=0", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"INSERT INTO mysql_query_rules (rule_id,active,digest,cache_ttl,apply) VALUES (500,1,'0x1559bca5d536e403',60000,1)", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL QUERY RULES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + // 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, {"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 + { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ConnectionType::kAdmin, {"DELETE FROM mysql_query_rules WHERE rule_id = 500 AND digest = '0x1559bca5d536e403'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL QUERY RULES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } +}; + +const std::vector query_digest_test = { + { ConnectionType::kAdmin, {"SET mysql-query_digests='false'", 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::kAdmin, {"SET mysql-query_digests='true'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } +}; + +const std::vector warning_log_test = { + { ConnectionType::kAdmin, {"SET mysql-log_mysql_warnings_enabled='true'", 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::kAdmin, {"SET mysql-log_mysql_warnings_enabled='false'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } +}; + +const std::vector multiplexing_test = { + { ConnectionType::kMySQL, {"SELECT @@sql_mode", true}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) }, + { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables | MultiplexStatus::kHasWarnings) }, + { ConnectionType::kMySQL, {"SELECT 1", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) } +}; + + + +#define IS_BIT_MASK_SET(variable,flag) ((variable & static_cast(flag)) == static_cast(flag)) + +int main(int argc, char** argv) { + + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(9+13+4+23+9+10); + + // Initialize Admin connection + MYSQL* proxysql_admin = mysql_init(NULL); + if (!proxysql_admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + // Connnect to ProxySQL Admin + if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return exit_status(); + } + + // Initialize ProxySQL connection + MYSQL* proxysql = mysql_init(NULL); + if (!proxysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + // Connect to ProxySQL + if (!mysql_real_connect(proxysql, 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)); + return exit_status(); + } + + std::vector>> all_tests(6); + + all_tests[0].first = "SELECT"; + all_tests[0].second.insert(all_tests[0].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[2].first = "QUERY_DIGEST"; + all_tests[2].second.insert(all_tests[2].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[4].first = "WARNING_LOGS"; + all_tests[4].second.insert(all_tests[4].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()); + + for (const auto& test : all_tests) { + diag("Executing [%s] test...", test.first); + for (const auto& test_info : test.second) { + MYSQL* mysql = (test_info.conn_type == ConnectionType::kMySQL ? proxysql : proxysql_admin); + + if (execute_query(mysql, test_info.query_info) == EXIT_FAILURE) + goto __exit; + + const int check_type = static_cast(test_info.warning_check_info.type); + + if (IS_BIT_MASK_SET(check_type, WarningCheckType::kConnection)) { + const int count = get_warnings_count_from_connection(mysql); + ok((count == test_info.warning_check_info.warning_count), "Connection warning count should match. Expected count:'%d' Actual count:'%d'", test_info.warning_check_info.warning_count, count); + } + if (IS_BIT_MASK_SET(check_type, WarningCheckType::kCountQuery)) { + const int count = get_warnings_count(mysql); + ok((count == test_info.warning_check_info.warning_count), "Warnings count via query should match. Expected count:'%d' Actual count:'%d'", test_info.warning_check_info.warning_count, count); + } + if (IS_BIT_MASK_SET(check_type, WarningCheckType::kShowWarnings)) { + std::list> warnings_list; + + const int count = get_warnings(mysql, warnings_list); + ok((count == test_info.warning_check_info.warning_count), "Fetched warning messages count should match. Expected count:'%d' Actual count:'%d'", test_info.warning_check_info.warning_count, count); + + if (test_info.warning_check_info.warning_codes.empty() == false) { + for (const auto& warnings : warnings_list) { + const int exp_code = std::get<1>(warnings); + bool match_found = false; + for (const int code : test_info.warning_check_info.warning_codes) { + if (exp_code == code) { + match_found = true; + break; + } + } + ok(match_found, "Warning code '%d' should match", exp_code); + } + } + } + + if (test_info.multiplex_status != MultiplexStatus::kNotApplicable) { + if (check_proxysql_internal_session(mysql, test_info.multiplex_status) != EXIT_SUCCESS) + goto __exit; + } + } + } + +__exit: + mysql_close(proxysql); + mysql_close(proxysql_admin); + + return exit_status(); +} From d2b8f7d54e6e79a88778dcb4791d3cd5fa633182 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Sat, 21 Oct 2023 14:43:58 +0500 Subject: [PATCH 03/18] Fixed code style --- lib/MySQL_Session.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 66fed0ab7e..f4ef709b35 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -6067,8 +6067,7 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C current_hostgroup = warning_in_hg; //warning_in_hg = -1; return false; - } - else { + } else { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); std::unique_ptr resultset(new SQLite3_result(1)); resultset->add_column_definition(SQLITE_TEXT, "@@session.warning_count"); From c28f88eb15bc9b5cfe32b716a463ae6a5c5a7872 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 23 Oct 2023 10:11:46 +0500 Subject: [PATCH 04/18] Added delay between query execution --- test/tap/tests/test_warnings-t.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tap/tests/test_warnings-t.cpp b/test/tap/tests/test_warnings-t.cpp index 992dee1dc5..dd58552bd9 100644 --- a/test/tap/tests/test_warnings-t.cpp +++ b/test/tap/tests/test_warnings-t.cpp @@ -89,6 +89,7 @@ int execute_query(MYSQL* proxysql, const QueryInfo& query_info) { if (query_info.is_select) { MYSQL_CLEAR_RESULT(proxysql); } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); return EXIT_SUCCESS; } From 30f989722b83a18b65c25e261825ce7a96bb85e5 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 23 Oct 2023 12:41:32 +0500 Subject: [PATCH 05/18] Changed digest to match_digest --- test/tap/tests/test_warnings-t.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/test_warnings-t.cpp b/test/tap/tests/test_warnings-t.cpp index dd58552bd9..96d01dea47 100644 --- a/test/tap/tests/test_warnings-t.cpp +++ b/test/tap/tests/test_warnings-t.cpp @@ -192,7 +192,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, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kAdmin, {"INSERT INTO mysql_query_rules (rule_id,active,digest,cache_ttl,apply) VALUES (500,1,'0x1559bca5d536e403',60000,1)", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"INSERT INTO mysql_query_rules (rule_id,active,match_digest,cache_ttl,apply) VALUES (500,1,'SELECT ?/?',60000,1)", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"LOAD MYSQL QUERY RULES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, @@ -204,7 +204,7 @@ const std::vector query_cache_test = { { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, // resultset will be retrived from cache, with warning count zero { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, - { ConnectionType::kAdmin, {"DELETE FROM mysql_query_rules WHERE rule_id = 500 AND digest = '0x1559bca5d536e403'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ConnectionType::kAdmin, {"DELETE FROM mysql_query_rules WHERE rule_id = 500 AND match_digest = 'SELECT ?/?'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"LOAD MYSQL QUERY RULES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } }; From 0c85c4f458257f722ea729412ad34818cf93294a Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 23 Oct 2023 13:15:47 +0500 Subject: [PATCH 06/18] Remove *.so files from tap folder on make clean --- test/tap/tap/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tap/tap/Makefile b/test/tap/tap/Makefile index 1858a60f66..9e58496e42 100644 --- a/test/tap/tap/Makefile +++ b/test/tap/tap/Makefile @@ -30,6 +30,7 @@ all: libtap.a libtap.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so .PHONY: clean clean: rm -f *.o libtap.a libtap.so || true + find . -name '*.so' -type f -delete || true find cpp-dotenv/dynamic -name '*.o' -or -name '*.a' -delete || true find cpp-dotenv/static -name '*.o' -or -name '*.a' -delete || true From a47d0ef48492a46a1ed38ede81d02701173161c5 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 23 Oct 2023 14:53:21 +0500 Subject: [PATCH 07/18] Remove old mysql query rule entries --- test/tap/tests/test_warnings-t.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/test_warnings-t.cpp b/test/tap/tests/test_warnings-t.cpp index 96d01dea47..bb6f2e40ce 100644 --- a/test/tap/tests/test_warnings-t.cpp +++ b/test/tap/tests/test_warnings-t.cpp @@ -192,7 +192,8 @@ 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, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kAdmin, {"INSERT INTO mysql_query_rules (rule_id,active,match_digest,cache_ttl,apply) VALUES (500,1,'SELECT ?/?',60000,1)", 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) }, { ConnectionType::kAdmin, {"LOAD MYSQL QUERY RULES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kAdmin, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, @@ -204,7 +205,7 @@ const std::vector query_cache_test = { { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, // resultset will be retrived from cache, with warning count zero { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, - { ConnectionType::kAdmin, {"DELETE FROM mysql_query_rules WHERE rule_id = 500 AND match_digest = 'SELECT ?/?'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { 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, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } }; From b588798a08d55a81a697cf625be26d5085c86cde Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 31 Oct 2023 10:39:39 +0500 Subject: [PATCH 08/18] Few fixes --- include/mysql_connection.h | 1 + lib/MySQL_Session.cpp | 35 ++++++++++++++++-------------- lib/mysql_connection.cpp | 7 ++++-- test/tap/tests/test_warnings-t.cpp | 23 ++++++++++---------- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/include/mysql_connection.h b/include/mysql_connection.h index c783a7d640..7b796b18cc 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -129,6 +129,7 @@ class MySQL_Connection { } statuses; unsigned long largest_query_length; + unsigned int warning_count; /** * @brief This represents the internal knowledge of ProxySQL about the connection. It keeps track of those * states which *are not reflected* into 'server_status', but are relevant for connection handling. diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index f4ef709b35..c168069b8b 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1250,6 +1250,7 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["backends"][i]["conn"]["status"]["compression"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); j["backends"][i]["conn"]["status"]["prepared_statement"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT); j["backends"][i]["conn"]["status"]["has_warnings"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS); + j["backends"][i]["conn"]["warning_count"] = _myconn->warning_count; j["backends"][i]["conn"]["MultiplexDisabled"] = _myconn->MultiplexDisabled(); j["backends"][i]["conn"]["ps"]["backend_stmt_to_global_ids"] = _myconn->local_stmts->backend_stmt_to_global_ids; j["backends"][i]["conn"]["ps"]["global_stmt_to_backend_ids"] = _myconn->local_stmts->global_stmt_to_backend_ids; @@ -6062,27 +6063,28 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C if ((dig_len == 22) && (strncasecmp(dig, "SHOW COUNT(*) WARNINGS", 22) == 0)) { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Intercepted '%s'\n", dig); + std::string warning_count = "0"; if (warning_in_hg > -1) { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); - current_hostgroup = warning_in_hg; - //warning_in_hg = -1; - return false; + current_hostgroup = warning_in_hg; + assert(mybe && mybe->server_myds && mybe->server_myds->myconn && mybe->server_myds->myconn->mysql); + warning_count = std::to_string(mybe->server_myds->myconn->warning_count); } else { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); - std::unique_ptr resultset(new SQLite3_result(1)); - resultset->add_column_definition(SQLITE_TEXT, "@@session.warning_count"); - char* pta[1]; - pta[0] = (char*)"0"; - resultset->add_row(pta); - SQLite3_to_MySQL(resultset.get(), NULL, 0, &client_myds->myprot, false, (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF)); - client_myds->DSS = STATE_SLEEP; - status = WAITING_CLIENT_DATA; - if (mirror == false) { - RequestEnd(NULL); - } - l_free(pkt->size, pkt->ptr); - return true; } + std::unique_ptr resultset(new SQLite3_result(1)); + resultset->add_column_definition(SQLITE_TEXT, "@@session.warning_count"); + char* pta[1]; + pta[0] = (char*)warning_count.c_str(); + resultset->add_row(pta); + SQLite3_to_MySQL(resultset.get(), NULL, 0, &client_myds->myprot, false, (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF)); + client_myds->DSS = STATE_SLEEP; + status = WAITING_CLIENT_DATA; + if (mirror == false) { + RequestEnd(NULL); + } + l_free(pkt->size, pkt->ptr); + return true; } reset_warning_hostgroup_flag_and_release_connection(); @@ -8163,6 +8165,7 @@ void MySQL_Session::reset_warning_hostgroup_flag_and_release_connection() MySQL_Backend* _mybe = NULL; _mybe = find_backend(warning_in_hg); MySQL_Data_Stream* myds = _mybe->server_myds; + myds->myconn->warning_count = 0; myds->myconn->set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); if ((myds->myconn->reusable == true) && myds->myconn->IsActiveTransaction() == false && myds->myconn->MultiplexDisabled() == false) { myds->return_MySQL_Connection_To_Pool(); diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 44a4623da4..4f22354563 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -434,6 +434,7 @@ MySQL_Connection::MySQL_Connection() { query.stmt_meta=NULL; query.stmt_result=NULL; largest_query_length=0; + warning_count=0; multiplex_delayed=false; MyRS=NULL; MyRS_reuse=NULL; @@ -2575,11 +2576,12 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { // checking warnings and disabling multiplexing will be effective only when the mysql-query_digests is enabled if (myds->sess->CurrentQuery.QueryParserArgs.digest_text) { if (get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS) == false) { - if (mysql_warning_count(this->mysql) > 0) { + if (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); } @@ -2595,6 +2597,7 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { if (myds && myds->sess) { myds->sess->warning_in_hg = -1; } + warning_count = 0; // disabling multiplexing set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); } @@ -2828,7 +2831,7 @@ void MySQL_Connection::reset() { set_status(old_compress,STATUS_MYSQL_CONNECTION_COMPRESSION); reusable=true; options.last_set_autocommit=-1; // never sent - + warning_count=0; delete local_stmts; local_stmts=new MySQL_STMTs_local_v14(false); creation_time = monotonic_time(); diff --git a/test/tap/tests/test_warnings-t.cpp b/test/tap/tests/test_warnings-t.cpp index bb6f2e40ce..9071ccccb5 100644 --- a/test/tap/tests/test_warnings-t.cpp +++ b/test/tap/tests/test_warnings-t.cpp @@ -31,11 +31,7 @@ using MESSAGE = std::string; } \ } while(0) -#define MYSQL_CLEAR_RESULT(mysql) do { MYSQL_RES* mysql_result = mysql_use_result(mysql); \ - while (MYSQL_ROW row = mysql_fetch_row(mysql_result)) {} \ - mysql_free_result(mysql_result); \ - } while(0) - +#define MYSQL_CLEAR_RESULT(mysql) mysql_free_result(mysql_store_result(mysql)); enum MultiplexStatus { kNotApplicable = 0, @@ -150,15 +146,17 @@ int check_proxysql_internal_session(MYSQL* proxysql, int exp_status) { if (backend != nullptr && backend.contains("conn")) { found_backend = true; - if (backend["conn"]["MultiplexDisabled"]) { + if (backend["conn"]["MultiplexDisabled"] == true) { status |= MultiplexStatus::kMultiplexingEnabled; } - if (backend["conn"]["status"]["has_warnings"] && j_status["warning_in_hg"] != -1) { + if (backend["conn"]["status"]["has_warnings"] == true && + backend["conn"]["warning_count"] > 0 && + j_status["warning_in_hg"] != -1) { status |= MultiplexStatus::kHasWarnings; } - if (backend["conn"]["status"]["user_variable"]) { + if (backend["conn"]["status"]["user_variable"] == true) { status |= MultiplexStatus::kUserVariables; } } @@ -233,8 +231,6 @@ const std::vector multiplexing_test = { { ConnectionType::kMySQL, {"SELECT 1", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) } }; - - #define IS_BIT_MASK_SET(variable,flag) ((variable & static_cast(flag)) == static_cast(flag)) int main(int argc, char** argv) { @@ -246,7 +242,12 @@ int main(int argc, char** argv) { return -1; } - plan(9+13+4+23+9+10); + 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 // Initialize Admin connection MYSQL* proxysql_admin = mysql_init(NULL); From 743cd502a5f45af0cb680bbe670e41a75498f707 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 6 Nov 2023 12:24:01 +0500 Subject: [PATCH 09/18] * Updated variable name from 'mysql-query_cache_with_warnings_support' to 'mysql-query_cache_handle_warnings' * Introduced global variable 'mysql-handle_warnings' to control warning support in ProxySQL * Included 'handling_warnings' flag in the 'mysql_hostgroup_attributes' table (hostgroup_settings). This flag holds top priority and can override the value of the global variable 'mysql_handle_warnings'. --- include/MySQL_HostGroups_Manager.h | 5 +++++ include/MySQL_Protocol.h | 2 +- include/MySQL_Thread.h | 3 ++- include/proxysql_structs.h | 6 ++++-- lib/MySQL_HostGroups_Manager.cpp | 9 +++++++-- lib/MySQL_Protocol.cpp | 8 ++++---- lib/MySQL_Session.cpp | 2 +- lib/MySQL_Thread.cpp | 12 ++++++++---- lib/mysql_connection.cpp | 6 ++++-- 9 files changed, 36 insertions(+), 17 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index c2df41d6da..1f5c1d8dcc 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -237,6 +237,7 @@ class MyHGC { // MySQL Host Group Container uint32_t throttle_connections_per_sec; int8_t autocommit; int8_t free_connections_pct; + int8_t handle_warnings; bool multiplex; bool connection_warming; bool configured; // this variable controls if attributes are configured or not. If not configured, they do not apply @@ -249,6 +250,10 @@ class MyHGC { // MySQL Host Group Container int32_t use_ssl; } servers_defaults; void reset_attributes(); + inline + bool handle_warnings_enabled() const { + return attributes.configured == true && attributes.handle_warnings != -1 ? attributes.handle_warnings : mysql_thread___handle_warnings; + } MyHGC(int); ~MyHGC(); MySrvC *get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_lag_ms, MySQL_Session *sess); diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index a7b68994c3..93962f6209 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -51,7 +51,7 @@ class MySQL_ResultSet { unsigned int add_row(MYSQL_ROWS *rows); unsigned int add_row(MYSQL_ROW row); unsigned int add_row2(MYSQL_ROWS *row, unsigned char *offset); - void add_eof(); + void add_eof(bool handle_warnings_enabled=false); void remove_last_eof(); void add_err(MySQL_Data_Stream *_myds); bool get_resultset(PtrSizeArray *PSarrayFinal); diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index 999c744fb4..9ca92cf13a 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -581,7 +581,7 @@ class MySQL_Threads_Handler char * ssl_p2s_crlpath; int query_cache_size_MB; int query_cache_soft_ttl_pct; - int query_cache_with_warnings_support; + int query_cache_handle_warnings; int min_num_servers_lantency_awareness; int aurora_max_lag_ms_only_read_from_replicas; bool stats_time_backend_query; @@ -594,6 +594,7 @@ class MySQL_Threads_Handler bool enable_load_data_local_infile; bool log_mysql_warnings_enabled; int data_packets_history_size; + int handle_warnings; } variables; struct { unsigned int mirror_sessions_current; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 63532d4e6e..d2abf393ea 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -865,11 +865,12 @@ __thread bool mysql_thread___log_mysql_warnings_enabled; __thread bool mysql_thread___enable_load_data_local_infile; __thread int mysql_thread___client_host_cache_size; __thread int mysql_thread___client_host_error_counts; +__thread int mysql_thread___handle_warnings; /* variables used for Query Cache */ __thread int mysql_thread___query_cache_size_MB; __thread int mysql_thread___query_cache_soft_ttl_pct; -__thread int mysql_thread___query_cache_with_warnings_support; +__thread int mysql_thread___query_cache_handle_warnings; /* variables used for SSL , from proxy to server (p2s) */ __thread char * mysql_thread___ssl_p2s_ca; @@ -1033,11 +1034,12 @@ extern __thread bool mysql_thread___log_mysql_warnings_enabled; extern __thread bool mysql_thread___enable_load_data_local_infile; extern __thread int mysql_thread___client_host_cache_size; extern __thread int mysql_thread___client_host_error_counts; +extern __thread int mysql_thread___handle_warnings; /* variables used for Query Cache */ extern __thread int mysql_thread___query_cache_size_MB; extern __thread int mysql_thread___query_cache_soft_ttl_pct; -extern __thread int mysql_thread___query_cache_with_warnings_support; +extern __thread int mysql_thread___query_cache_handle_warnings; /* variables used for SSL , from proxy to server (p2s) */ extern __thread char * mysql_thread___ssl_p2s_ca; diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 7fb83388a1..80f3608271 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -903,6 +903,7 @@ void MyHGC::reset_attributes() { attributes.throttle_connections_per_sec = 1000000; attributes.autocommit = -1; attributes.free_connections_pct = 10; + attributes.handle_warnings = -1; attributes.multiplex = true; attributes.connection_warming = false; free(attributes.init_connect); @@ -6931,7 +6932,8 @@ T j_get_srv_default_int_val( /** * @brief Initializes the supplied 'MyHGC' with the specified 'hostgroup_settings'. * @details Input verification is performed in the supplied 'hostgroup_settings'. It's expected to be a valid - * JSON. + * JSON that may contain the following fields: + * - handle_warnings: Value must be >= 0. * * In case input verification fails for a field, supplied 'MyHGC' is NOT updated for that field. An error * message is logged specifying the source of the error. @@ -6945,7 +6947,10 @@ void init_myhgc_hostgroup_settings(const char* hostgroup_settings, MyHGC* myhgc) if (hostgroup_settings[0] != '\0') { try { nlohmann::json j = nlohmann::json::parse(hostgroup_settings); - // fields to be populated + + const auto handle_warnings_check = [](int8_t handle_warnings) -> bool { return handle_warnings == 0 || handle_warnings == 1; }; + int8_t handle_warnings = j_get_srv_default_int_val(j, hid, "handle_warnings", handle_warnings_check); + myhgc->attributes.handle_warnings = handle_warnings; } catch (const json::exception& e) { proxy_error( diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 9284e24967..d036155072 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -2990,7 +2990,7 @@ unsigned int MySQL_ResultSet::add_row2(MYSQL_ROWS *row, unsigned char *offset) { return length; } -void MySQL_ResultSet::add_eof() { +void MySQL_ResultSet::add_eof(bool handle_warnings_enabled) { if (myprot) { unsigned int nTrx=myds->sess->NumActiveTransactions(); uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); @@ -3003,11 +3003,11 @@ void MySQL_ResultSet::add_eof() { //resultset_size+=pkt.size; // warnings count will only be sent to the client if mysql-query_digests is enabled - const bool warnings_enabled = myds->sess->CurrentQuery.QueryParserArgs.digest_text != nullptr; + if (deprecate_eof_active) { PtrSize_t pkt; buffer_to_PSarrayOut(); - myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, (warnings_enabled ? mysql->warning_count : 0), NULL, true); + myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, (handle_warnings_enabled ? mysql->warning_count : 0), NULL, true); PSarrayOUT.add(pkt.ptr, pkt.size); resultset_size += pkt.size; } @@ -3017,7 +3017,7 @@ void MySQL_ResultSet::add_eof() { // note that EOF is added on a packet on its own, instead of using a buffer, // so that can be removed using remove_last_eof() buffer_to_PSarrayOut(); - myprot->generate_pkt_EOF(false, NULL, NULL, sid, (warnings_enabled ? mysql->warning_count : 0), setStatus, this); + myprot->generate_pkt_EOF(false, NULL, NULL, sid, (handle_warnings_enabled ? mysql->warning_count : 0), setStatus, this); resultset_size += 9; buffer_to_PSarrayOut(); } diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index a01b0c66d8..1c9befadb6 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -7372,7 +7372,7 @@ void MySQL_Session::MySQL_Result_to_MySQL_wire(MYSQL *mysql, MySQL_ResultSet *My if (qpo && qpo->cache_ttl>0 && com_field_list==false) { // the resultset should be cached if (mysql_errno(mysql)==0 && (mysql_warning_count(mysql)==0 || - mysql_thread___query_cache_with_warnings_support==1)) { // no errors + mysql_thread___query_cache_handle_warnings==1)) { // no errors if ( (qpo->cache_empty_result==1) || ( diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 5e019abac9..75253fa03c 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -553,7 +553,7 @@ static char * mysql_thread_variables_names[]= { (char *)"long_query_time", (char *)"query_cache_size_MB", (char *)"query_cache_soft_ttl_pct", - (char *)"query_cache_with_warnings_support", + (char *)"query_cache_handle_warnings", (char *)"ping_interval_server_msec", (char *)"ping_timeout_server", (char *)"default_schema", @@ -604,6 +604,7 @@ static char * mysql_thread_variables_names[]= { (char *)"stats_time_query_processor", (char *)"query_cache_stores_empty_result", (char *)"data_packets_history_size", + (char *)"handle_warnings", NULL }; @@ -1067,6 +1068,7 @@ MySQL_Threads_Handler::MySQL_Threads_Handler() { variables.query_retries_on_failure=1; variables.client_host_cache_size=0; variables.client_host_error_counts=0; + variables.handle_warnings=1; variables.connect_retries_on_failure=10; variables.connection_delay_multiplex_ms=0; variables.connection_max_age_ms=0; @@ -1150,7 +1152,7 @@ MySQL_Threads_Handler::MySQL_Threads_Handler() { variables.long_query_time=1000; variables.query_cache_size_MB=256; variables.query_cache_soft_ttl_pct=0; - variables.query_cache_with_warnings_support=0; + variables.query_cache_handle_warnings=0; variables.init_connect=NULL; variables.ldap_user_variable=NULL; variables.add_ldap_user_comment=NULL; @@ -2272,6 +2274,7 @@ char ** MySQL_Threads_Handler::get_variables_list() { VariablesPointers_int["ping_timeout_server"] = make_tuple(&variables.ping_timeout_server, 10, 600*1000, false); VariablesPointers_int["client_host_cache_size"] = make_tuple(&variables.client_host_cache_size, 0, 1024*1024, false); VariablesPointers_int["client_host_error_counts"] = make_tuple(&variables.client_host_error_counts, 0, 1024*1024, false); + VariablesPointers_int["handle_warnings"] = make_tuple(&variables.handle_warnings, 0, 1, false); // logs VariablesPointers_int["auditlog_filesize"] = make_tuple(&variables.auditlog_filesize, 1024*1024, 1*1024*1024*1024, false); @@ -2287,7 +2290,7 @@ char ** MySQL_Threads_Handler::get_variables_list() { VariablesPointers_int["max_transaction_time"] = make_tuple(&variables.max_transaction_time, 1000, 20*24*3600*1000, false); VariablesPointers_int["query_cache_size_mb"] = make_tuple(&variables.query_cache_size_MB, 0, 1024*10240, false); VariablesPointers_int["query_cache_soft_ttl_pct"] = make_tuple(&variables.query_cache_soft_ttl_pct, 0, 100, false); - VariablesPointers_int["query_cache_with_warnings_support"] = make_tuple(&variables.query_cache_with_warnings_support, 0, 1, false); + VariablesPointers_int["query_cache_handle_warnings"] = make_tuple(&variables.query_cache_handle_warnings, 0, 1, false); #ifdef IDLE_THREADS VariablesPointers_int["session_idle_ms"] = make_tuple(&variables.session_idle_ms, 1, 3600*1000, false); @@ -4037,7 +4040,7 @@ void MySQL_Thread::refresh_variables() { mysql_thread___long_query_time=GloMTH->get_variable_int((char *)"long_query_time"); mysql_thread___query_cache_size_MB=GloMTH->get_variable_int((char *)"query_cache_size_MB"); mysql_thread___query_cache_soft_ttl_pct=GloMTH->get_variable_int((char *)"query_cache_soft_ttl_pct"); - mysql_thread___query_cache_with_warnings_support =GloMTH->get_variable_int((char*)"query_cache_with_warnings_support"); + mysql_thread___query_cache_handle_warnings =GloMTH->get_variable_int((char*)"query_cache_handle_warnings"); mysql_thread___ping_interval_server_msec=GloMTH->get_variable_int((char *)"ping_interval_server_msec"); mysql_thread___ping_timeout_server=GloMTH->get_variable_int((char *)"ping_timeout_server"); mysql_thread___shun_on_failures=GloMTH->get_variable_int((char *)"shun_on_failures"); @@ -4199,6 +4202,7 @@ void MySQL_Thread::refresh_variables() { mysql_thread___log_mysql_warnings_enabled=(bool)GloMTH->get_variable_int((char *)"log_mysql_warnings_enabled"); mysql_thread___client_host_cache_size=GloMTH->get_variable_int((char *)"client_host_cache_size"); mysql_thread___client_host_error_counts=GloMTH->get_variable_int((char *)"client_host_error_counts"); + mysql_thread___handle_warnings=GloMTH->get_variable_int((char*)"handle_warnings"); #ifdef DEBUG mysql_thread___session_debug=(bool)GloMTH->get_variable_int((char *)"session_debug"); #endif /* DEBUG */ diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 4f22354563..18f4b35dd5 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -1635,7 +1635,8 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { } } // we reach here if there was no error - MyRS->add_eof(); + MyRS->add_eof(myds->sess->CurrentQuery.QueryParserArgs.digest_text != nullptr && + myds->myconn->parent->myhgc->handle_warnings_enabled()); NEXT_IMMEDIATE(ASYNC_QUERY_END); } } @@ -2576,7 +2577,8 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { // checking warnings and disabling multiplexing will be effective only when the mysql-query_digests is enabled if (myds->sess->CurrentQuery.QueryParserArgs.digest_text) { if (get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS) == false) { - if (this->mysql && mysql_warning_count(this->mysql) > 0) { + 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' From 2706b4e05c8ffbd4fdb221f811eca7157014402a Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 6 Nov 2023 12:24:37 +0500 Subject: [PATCH 10/18] Updated cluster TAP test --- test/tap/tests/test_cluster_sync-t.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/tap/tests/test_cluster_sync-t.cpp b/test/tap/tests/test_cluster_sync-t.cpp index e5897061ec..5b02bd5b5b 100644 --- a/test/tap/tests/test_cluster_sync-t.cpp +++ b/test/tap/tests/test_cluster_sync-t.cpp @@ -2008,6 +2008,8 @@ int main(int, char**) { std::make_tuple("mysql-auto_increment_delay_multiplex" , "6" ), std::make_tuple("mysql-long_query_time" , "1001" ), // here std::make_tuple("mysql-query_cache_size_MB" , "256" ), + std::make_tuple("mysql-query_cache_handle_warnings" , "1" ), + std::make_tuple("mysql-handle_warnings" , "1" ), std::make_tuple("mysql-poll_timeout_on_failure" , "100" ), std::make_tuple("mysql-keep_multiplexing_variables" , "tx_isolation,version" ), std::make_tuple("mysql-kill_backend_connection_when_disconnect" , "true" ), From 7223ba19705ae2f3877f1735a62bec88e2ea82ce Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 6 Nov 2023 12:49:56 +0500 Subject: [PATCH 11/18] Updated test_mysql_hostgroup_attributes-1-t TAP test --- test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp b/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp index 8f90fdf712..31adbb1f4d 100644 --- a/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp +++ b/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp @@ -79,12 +79,12 @@ int main(int argc, char** argv) { "(20,3,-1,40,'SET sql_mode=\"\"',1,0,124,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','','{\"weight\": 100, \"max_connections\": 1000}','servers defaults')" }, { - "0x1A6E38CBB2E44672", + "0xF33858C81FDA7372", "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'','','','')," "(18,2,-1,20,'SET sql_mode=\"\"',0,0,100,'','','','hello world')," "(17,0,0,30,'SET long_query_time=0',1,0,123,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','','','filtering variables')," "(20,3,-1,40,'SET sql_mode=\"\"',1,0,124,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','','{\"weight\": 100, \"max_connections\": 1000}','servers defaults')," - "(21,2,-1,50,'SET sql_mode=\"\"',1,0,125,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','{}','{\"weight\": 100, \"max_connections\": 1000}','hostgroup settings')" + "(21,2,-1,50,'SET sql_mode=\"\"',1,0,125,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','{\"handle_warnings\": 1}','{\"weight\": 100, \"max_connections\": 1000}','hostgroup settings')" } }; From 05208e2839640fed7e413742e01b9dfd4b0692fa Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 7 Nov 2023 17:26:34 +0500 Subject: [PATCH 12/18] * Update warning_count as soon as query is executed. --- include/MySQL_Session.h | 2 +- lib/MySQL_Session.cpp | 12 ++-- lib/mysql_connection.cpp | 24 ++++---- test/tap/tests/test_warnings-t.cpp | 89 ++++++++++++++++++++++-------- 4 files changed, 87 insertions(+), 40 deletions(-) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index c168fd075d..049659306a 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 1c9befadb6..f23845e474 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 @@ int MySQL_Session::handler() { 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 @@ int MySQL_Session::handler() { 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 18f4b35dd5..b8cbd147ec 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 9071ccccb5..6ddc148b9c 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); From dae3d669b9c6c27e949dd96b26d9fadba39dd7e4 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 14 Nov 2023 15:06:26 +0500 Subject: [PATCH 13/18] * Setting 'warning_count' as soon as query is executed. * 'MySQL_Connection::warning_count' will be globally employed for managing multiplexing control and transmitting the warning count to the client * Fixed prepare statement bug: When preparing, if the digest matches, the result set is retrieved from the cache and sent to the client. Note: Currently using 'mysql_warning_count' to retrieve the warning count for prepared statement. --- include/MySQL_Protocol.h | 2 +- include/mysql_connection.h | 2 + lib/MySQL_Protocol.cpp | 14 ++++-- lib/MySQL_Session.cpp | 99 +++++++++++++++++++++----------------- lib/mysql_connection.cpp | 91 +++++++++++++++++++++++------------ 5 files changed, 125 insertions(+), 83 deletions(-) diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index 93962f6209..a7b68994c3 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -51,7 +51,7 @@ class MySQL_ResultSet { unsigned int add_row(MYSQL_ROWS *rows); unsigned int add_row(MYSQL_ROW row); unsigned int add_row2(MYSQL_ROWS *row, unsigned char *offset); - void add_eof(bool handle_warnings_enabled=false); + void add_eof(); void remove_last_eof(); void add_err(MySQL_Data_Stream *_myds); bool get_resultset(PtrSizeArray *PSarrayFinal); diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 7b796b18cc..501ccd7610 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -54,6 +54,8 @@ class MySQL_Connection_userinfo { class MySQL_Connection { private: + void update_warning_count_from_connection(); + void update_warning_count_from_statement(); bool is_expired(unsigned long long timeout); unsigned long long inserted_into_pool; public: diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index d036155072..f19d5957bf 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -2990,7 +2990,7 @@ unsigned int MySQL_ResultSet::add_row2(MYSQL_ROWS *row, unsigned char *offset) { return length; } -void MySQL_ResultSet::add_eof(bool handle_warnings_enabled) { +void MySQL_ResultSet::add_eof() { if (myprot) { unsigned int nTrx=myds->sess->NumActiveTransactions(); uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); @@ -3002,12 +3002,16 @@ void MySQL_ResultSet::add_eof(bool handle_warnings_enabled) { //sid++; //resultset_size+=pkt.size; - // warnings count will only be sent to the client if mysql-query_digests is enabled - + // Note: warnings count will only be sent to the client if mysql-query_digests is enabled + const MySQL_Backend* _mybe = myds->sess->mybe; + const MySQL_Data_Stream* _server_myds = (_mybe && _mybe->server_myds) ? _mybe->server_myds : nullptr; + const MySQL_Connection* _myconn = (_server_myds && _server_myds->myds_type == MYDS_BACKEND && _server_myds->myconn) ? + _server_myds->myconn : nullptr; + const unsigned int warning_count = (_myconn) ? _myconn->warning_count : 0; if (deprecate_eof_active) { PtrSize_t pkt; buffer_to_PSarrayOut(); - myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, (handle_warnings_enabled ? mysql->warning_count : 0), NULL, true); + myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, warning_count, NULL, true); PSarrayOUT.add(pkt.ptr, pkt.size); resultset_size += pkt.size; } @@ -3017,7 +3021,7 @@ void MySQL_ResultSet::add_eof(bool handle_warnings_enabled) { // note that EOF is added on a packet on its own, instead of using a buffer, // so that can be removed using remove_last_eof() buffer_to_PSarrayOut(); - myprot->generate_pkt_EOF(false, NULL, NULL, sid, (handle_warnings_enabled ? mysql->warning_count : 0), setStatus, this); + myprot->generate_pkt_EOF(false, NULL, NULL, sid, warning_count, setStatus, this); resultset_size += 9; buffer_to_PSarrayOut(); } diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index f23845e474..24f6939d24 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -3288,7 +3288,8 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C (begint.tv_sec*1000000000+begint.tv_nsec); } assert(qpo); // GloQPro->process_mysql_query() should always return a qpo - rc_break=handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup); + // setting 'prepared' to prevent fetching results from the cache if the digest matches + rc_break=handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup, true); if (rc_break==true) { return; } @@ -6031,23 +6032,53 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C goto __exit_set_destination_hostgroup; } - // handle here #509, #815 and #816 + // handle warnings if (CurrentQuery.QueryParserArgs.digest_text) { - char *dig=CurrentQuery.QueryParserArgs.digest_text; - const size_t dig_len=strlen(dig); - if ((dig_len == 13) && (strncasecmp(dig, "SHOW WARNINGS", 13) == 0)) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Intercepted '%s'\n", dig); - if (warning_in_hg > -1) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); - current_hostgroup = warning_in_hg; - //warning_in_hg = -1; - return false; - } else { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); - std::unique_ptr resultset(new SQLite3_result(3)); - resultset->add_column_definition(SQLITE_TEXT, "Level"); - resultset->add_column_definition(SQLITE_TEXT, "Code"); - resultset->add_column_definition(SQLITE_TEXT, "Message"); + const char* dig_text = CurrentQuery.QueryParserArgs.digest_text; + const size_t dig_len = strlen(dig_text); + + if (dig_len > 0) { + if ((dig_len == 13) && (strncasecmp(dig_text, "SHOW WARNINGS", 13) == 0)) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Intercepted '%s'\n", dig_text); + if (warning_in_hg > -1) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); + current_hostgroup = warning_in_hg; + //warning_in_hg = -1; + return false; + } else { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); + std::unique_ptr resultset(new SQLite3_result(3)); + resultset->add_column_definition(SQLITE_TEXT, "Level"); + resultset->add_column_definition(SQLITE_TEXT, "Code"); + resultset->add_column_definition(SQLITE_TEXT, "Message"); + SQLite3_to_MySQL(resultset.get(), NULL, 0, &client_myds->myprot, false, (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF)); + client_myds->DSS = STATE_SLEEP; + status = WAITING_CLIENT_DATA; + if (mirror == false) { + RequestEnd(NULL); + } + l_free(pkt->size, pkt->ptr); + return true; + } + } + + if ((dig_len == 22) && (strncasecmp(dig_text, "SHOW COUNT(*) WARNINGS", 22) == 0)) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Intercepted '%s'\n", dig_text); + std::string warning_count = "0"; + if (warning_in_hg > -1) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); + current_hostgroup = warning_in_hg; + assert(mybe && mybe->server_myds && mybe->server_myds->myconn && mybe->server_myds->myconn->mysql); + warning_count = std::to_string(mybe->server_myds->myconn->warning_count); + } + else { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); + } + std::unique_ptr resultset(new SQLite3_result(1)); + resultset->add_column_definition(SQLITE_TEXT, "@@session.warning_count"); + char* pta[1]; + pta[0] = (char*)warning_count.c_str(); + resultset->add_row(pta); SQLite3_to_MySQL(resultset.get(), NULL, 0, &client_myds->myprot, false, (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF)); client_myds->DSS = STATE_SLEEP; status = WAITING_CLIENT_DATA; @@ -6057,36 +6088,14 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C l_free(pkt->size, pkt->ptr); return true; } - } - - if ((dig_len == 22) && (strncasecmp(dig, "SHOW COUNT(*) WARNINGS", 22) == 0)) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Intercepted '%s'\n", dig); - std::string warning_count = "0"; - if (warning_in_hg > -1) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); - current_hostgroup = warning_in_hg; - assert(mybe && mybe->server_myds && mybe->server_myds->myconn && mybe->server_myds->myconn->mysql); - warning_count = std::to_string(mybe->server_myds->myconn->warning_count); - } else { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); - } - std::unique_ptr resultset(new SQLite3_result(1)); - resultset->add_column_definition(SQLITE_TEXT, "@@session.warning_count"); - char* pta[1]; - pta[0] = (char*)warning_count.c_str(); - resultset->add_row(pta); - SQLite3_to_MySQL(resultset.get(), NULL, 0, &client_myds->myprot, false, (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF)); - client_myds->DSS = STATE_SLEEP; - status = WAITING_CLIENT_DATA; - if (mirror == false) { - RequestEnd(NULL); - } - l_free(pkt->size, pkt->ptr); - return true; } + } - reset_warning_hostgroup_flag_and_release_connection(); + reset_warning_hostgroup_flag_and_release_connection(); + // handle here #509, #815 and #816 + if (CurrentQuery.QueryParserArgs.digest_text) { + char *dig=CurrentQuery.QueryParserArgs.digest_text; unsigned int nTrx=NumActiveTransactions(); if ((locked_on_hostgroup == -1) && (strncasecmp(dig,(char *)"SET ",4)==0)) { // this code is executed only if locked_on_hostgroup is not set yet @@ -7343,7 +7352,7 @@ void MySQL_Session::MySQL_Stmt_Result_to_MySQL_wire(MYSQL_STMT *stmt, MySQL_Conn 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 , myconn ? myconn->warning_count : 0,mysql->info); client_myds->pkt_sid++; } else { // error diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index b8cbd147ec..fb1dd4cabe 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -549,6 +549,35 @@ unsigned int MySQL_Connection::set_charset(unsigned int _c, enum charset_action return _c; } +void MySQL_Connection::update_warning_count_from_connection() { + // if a prepared statement was cached while 'mysql_thread_query_digest' was true, and subsequently, + // 'mysql_thread_query_digest' is set to false, fetching that statement from the cache may still contain the digest text. + // To prevent this, we will check the digest text in conjunction with 'mysql_thread_query_digest' to verify whether it + // is enabled or disabled. + if (myds && myds->sess && (myds->sess->CurrentQuery.QueryParserArgs.digest_text || + (myds->sess->CurrentQuery.stmt_info && myds->sess->CurrentQuery.stmt_info->digest_text && + mysql_thread___query_digests == true))) { + const bool handle_warnings_enabled = parent->myhgc->handle_warnings_enabled(); + if (handle_warnings_enabled && mysql_errno(mysql) == 0 && mysql_warning_count(mysql) > 0) { + warning_count = mysql_warning_count(mysql); + } + } +} + +void MySQL_Connection::update_warning_count_from_statement() { + // if a prepared statement was cached while 'mysql_thread_query_digest' was true, and subsequently, + // 'mysql_thread_query_digest' is set to false, fetching that statement from the cache may still contain the digest text. + // To prevent this, we will check the digest text in conjunction with 'mysql_thread_query_digest' to verify whether it + // is enabled or disabled. + if (myds && myds->sess && myds->sess->CurrentQuery.stmt_info && myds->sess->CurrentQuery.stmt_info->digest_text && + mysql_thread___query_digests == true) { + const bool handle_warnings_enabled = parent->myhgc->handle_warnings_enabled(); + if (handle_warnings_enabled && mysql_stmt_warning_count(query.stmt) > 0) { + warning_count = mysql_stmt_warning_count(query.stmt); + } + } +} + bool MySQL_Connection::is_expired(unsigned long long timeout) { // FIXME: here the check should be a sanity check // FIXME: for now this is just a temporary (and stupid) check @@ -1353,6 +1382,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { if (query.stmt_result==NULL) { NEXT_IMMEDIATE(ASYNC_STMT_EXECUTE_END); } else { + update_warning_count_from_connection(); //update_warning_count_from_statement(); if (myds->sess->mirror==false) { if (MyRS_reuse == NULL) { MyRS = new MySQL_ResultSet(); @@ -1467,6 +1497,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { NEXT_IMMEDIATE(ASYNC_STMT_EXECUTE_SUCCESSFUL); } */ + update_warning_count_from_connection(); //update_warning_count_from_statement(); break; // case ASYNC_STMT_EXECUTE_SUCCESSFUL: // break; @@ -1532,6 +1563,8 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { if (mysql_result==NULL) { NEXT_IMMEDIATE(ASYNC_QUERY_END); } else { + // since 'add_eof' utilizes 'warning_count,' we are setting the 'warning_count' here + update_warning_count_from_connection(); if (myds->sess->mirror==false) { if (MyRS_reuse == NULL) { MyRS = new MySQL_ResultSet(); @@ -1634,9 +1667,10 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { } } } + // since 'add_eof' utilizes 'warning_count,' we are setting the 'warning_count' here + update_warning_count_from_connection(); // we reach here if there was no error - MyRS->add_eof(myds->sess->CurrentQuery.QueryParserArgs.digest_text != nullptr && - myds->myconn->parent->myhgc->handle_warnings_enabled()); + MyRS->add_eof(); NEXT_IMMEDIATE(ASYNC_QUERY_END); } } @@ -1647,6 +1681,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { int _myerrno=mysql_errno(mysql); if (_myerrno == 0) { unknown_transaction_status = false; + update_warning_count_from_connection(); } else { compute_unknown_transaction_status(); } @@ -2018,12 +2053,6 @@ 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; } } @@ -2581,32 +2610,30 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { } } // checking warnings and disabling multiplexing will be effective only when the mysql-query_digests is enabled - if (myds->sess->CurrentQuery.QueryParserArgs.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) { - // '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; - const size_t dig_len = strlen(dig); - // disable multiplexing and reset the 'warning_in_hg' flag only when the current executed query is not - // 'SHOW WARNINGS' or 'SHOW COUNT(*) WARNINGS', as these queries do not clear the warning message list - // on backend. - if (!((dig_len == 22 && strncasecmp(dig, "SHOW COUNT(*) WARNINGS", 22) == 0) || - (dig_len == 13 && strncasecmp(dig, "SHOW WARNINGS", 13) == 0))) { - myds->sess->warning_in_hg = -1; - warning_count = 0; - // disabling multiplexing - set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); - } + if (get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS) == false) { + if (warning_count > 0) { + // '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 = query_digest_text; + const size_t dig_len = strlen(dig); + // disable multiplexing and reset the 'warning_in_hg' flag only when the current executed query is not + // 'SHOW WARNINGS' or 'SHOW COUNT(*) WARNINGS', as these queries do not clear the warning message list + // on backend. + if (!((dig_len == 22 && strncasecmp(dig, "SHOW COUNT(*) WARNINGS", 22) == 0) || + (dig_len == 13 && strncasecmp(dig, "SHOW WARNINGS", 13) == 0))) { + myds->sess->warning_in_hg = -1; + warning_count = 0; + // disabling multiplexing + set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); } } + if (get_status(STATUS_MYSQL_CONNECTION_USER_VARIABLE)==false) { // we search for variables only if not already set // if ( // strncasecmp(query_digest_text,"SELECT @@tx_isolation", strlen("SELECT @@tx_isolation")) From 045d6bcaeb3abf2b32fde2ce4784932963175594 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Thu, 16 Nov 2023 13:01:05 +0500 Subject: [PATCH 14/18] * Execute all test cases with 'CLIENT_DEPRECATE_EOF' both enabled and disabled. * Introduced 'ps_type' enum to differentiate between preparing a statement and executing a statement * Fixed issue where the warning count fails to reset when the SET statement generates a warning. * Resolved the issue where the 'server_status' and 'warning count' were not accurately represented in the statement. * Replaced 'mysql_warning_count' with 'mysql_stmt_warning_count' for retrieving the warning count in statements. * Added 'handle_warning' field in hostgroup_attributes TAP test * Updated warnings TAP test --- .../client_deprecate_eof.patch | 4 +- include/MySQL_Session.h | 8 +- lib/MySQL_Session.cpp | 15 +- lib/mysql_connection.cpp | 20 +- test/tap/tests/test_cluster_sync-t.cpp | 2 +- test/tap/tests/test_warnings-t.cpp | 459 ++++++++++++------ 6 files changed, 342 insertions(+), 166 deletions(-) diff --git a/deps/mariadb-client-library/client_deprecate_eof.patch b/deps/mariadb-client-library/client_deprecate_eof.patch index f7f11b3b61..2c7a611f50 100644 --- a/deps/mariadb-client-library/client_deprecate_eof.patch +++ b/deps/mariadb-client-library/client_deprecate_eof.patch @@ -653,7 +653,7 @@ index 0aaaf1a..229023b 100644 { /* allocate space for rows */ if (!(current= (MYSQL_ROWS *)ma_alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + packet_len))) -@@ -276,10 +284,14 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) +@@ -276,10 +284,16 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) { *pprevious= 0; /* sace status info */ @@ -664,6 +664,8 @@ index 0aaaf1a..229023b 100644 + + if (stmt->mysql->server_capabilities & CLIENT_DEPRECATE_EOF && !is_data_packet) { + ma_read_ok_packet(stmt->mysql, p + 1, packet_len); ++ stmt->upsert_status.warning_count= stmt->mysql->warning_count; ++ stmt->upsert_status.server_status= stmt->mysql->server_status; + } else { + stmt->upsert_status.warning_count= stmt->mysql->warning_count= uint2korr(p + 1); + stmt->upsert_status.server_status= stmt->mysql->server_status= uint2korr(p + 3); diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index 049659306a..d2de482c79 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -24,6 +24,12 @@ enum proxysql_session_type { PROXYSQL_SESSION_NONE }; +enum ps_type : uint8_t { + ps_type_not_set = 0x0, + ps_type_prepare_stmt = 0x1, + ps_type_execute_stmt = 0x2 +}; + std::string proxysql_session_type_str(enum proxysql_session_type session_type); // these structs will be used for various regex hardcoded @@ -121,7 +127,7 @@ class MySQL_Session void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_SET_OPTION(PtrSize_t *); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STATISTICS(PtrSize_t *); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_PROCESS_KILL(PtrSize_t *); - bool handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(PtrSize_t *, bool *lock_hostgroup, bool ps=false); + bool handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(PtrSize_t *, bool *lock_hostgroup, ps_type prepare_stmt_type=ps_type_not_set); void handler___client_DSS_QUERY_SENT___server_DSS_NOT_INITIALIZED__get_connection(); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 24f6939d24..d904d84712 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -3289,7 +3289,7 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C } assert(qpo); // GloQPro->process_mysql_query() should always return a qpo // setting 'prepared' to prevent fetching results from the cache if the digest matches - rc_break=handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup, true); + rc_break=handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup, ps_type_prepare_stmt); if (rc_break==true) { return; } @@ -3457,7 +3457,7 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C CurrentQuery.stmt_meta=stmt_meta; //current_hostgroup=qpo->destination_hostgroup; - rc_break=handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup, true); + rc_break=handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(&pkt, &lock_hostgroup, ps_type_execute_stmt); if (rc_break==true) { return; } @@ -5992,7 +5992,7 @@ int MySQL_Session::handler_WCD_SS_MCQ_qpo_Parse_SQL_LOG_BIN(PtrSize_t *pkt, bool } */ -bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(PtrSize_t *pkt, bool *lock_hostgroup, bool prepared) { +bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo(PtrSize_t *pkt, bool *lock_hostgroup, ps_type prepare_stmt_type) { /* lock_hostgroup: If this variable is set to true, this session will get lock to a @@ -6027,7 +6027,7 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C return true; } - if (prepared) { // for prepared statement we exit here + if (prepare_stmt_type & ps_type_execute_stmt) { // for prepared statement execute we exit here reset_warning_hostgroup_flag_and_release_connection(); goto __exit_set_destination_hostgroup; } @@ -6043,7 +6043,6 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C if (warning_in_hg > -1) { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Changing current_hostgroup to '%d'\n", warning_in_hg); current_hostgroup = warning_in_hg; - //warning_in_hg = -1; return false; } else { proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "No warnings were detected in the previous query. Sending an empty response.\n"); @@ -6951,12 +6950,12 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C } // handle command KILL #860 - if (prepared == false) { + //if (prepared == false) { if (handle_command_query_kill(pkt)) { return true; } - } - if (qpo->cache_ttl>0) { + //} + if (qpo->cache_ttl>0 && ((prepare_stmt_type & ps_type_prepare_stmt) == 0)) { bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; uint32_t resbuf=0; unsigned char *aa=GloQC->get( diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index fb1dd4cabe..3788287b13 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -554,11 +554,13 @@ void MySQL_Connection::update_warning_count_from_connection() { // 'mysql_thread_query_digest' is set to false, fetching that statement from the cache may still contain the digest text. // To prevent this, we will check the digest text in conjunction with 'mysql_thread_query_digest' to verify whether it // is enabled or disabled. - if (myds && myds->sess && (myds->sess->CurrentQuery.QueryParserArgs.digest_text || - (myds->sess->CurrentQuery.stmt_info && myds->sess->CurrentQuery.stmt_info->digest_text && - mysql_thread___query_digests == true))) { - const bool handle_warnings_enabled = parent->myhgc->handle_warnings_enabled(); - if (handle_warnings_enabled && mysql_errno(mysql) == 0 && mysql_warning_count(mysql) > 0) { + if (myds && myds->sess && myds->sess->CurrentQuery.QueryParserArgs.digest_text) { + const char* dig_text = myds->sess->CurrentQuery.QueryParserArgs.digest_text; + const size_t dig_len = strlen(dig_text); + // SHOW WARNINGS doesn't have any impact warning count, + // so we are replication same behaviour here + if (parent->myhgc->handle_warnings_enabled() && + (dig_len != 13 || strncasecmp(dig_text, "SHOW WARNINGS", 13) != 0)) { warning_count = mysql_warning_count(mysql); } } @@ -571,8 +573,7 @@ void MySQL_Connection::update_warning_count_from_statement() { // is enabled or disabled. if (myds && myds->sess && myds->sess->CurrentQuery.stmt_info && myds->sess->CurrentQuery.stmt_info->digest_text && mysql_thread___query_digests == true) { - const bool handle_warnings_enabled = parent->myhgc->handle_warnings_enabled(); - if (handle_warnings_enabled && mysql_stmt_warning_count(query.stmt) > 0) { + if (parent->myhgc->handle_warnings_enabled()) { warning_count = mysql_stmt_warning_count(query.stmt); } } @@ -1382,7 +1383,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { if (query.stmt_result==NULL) { NEXT_IMMEDIATE(ASYNC_STMT_EXECUTE_END); } else { - update_warning_count_from_connection(); //update_warning_count_from_statement(); + update_warning_count_from_statement(); if (myds->sess->mirror==false) { if (MyRS_reuse == NULL) { MyRS = new MySQL_ResultSet(); @@ -1497,7 +1498,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { NEXT_IMMEDIATE(ASYNC_STMT_EXECUTE_SUCCESSFUL); } */ - update_warning_count_from_connection(); //update_warning_count_from_statement(); + update_warning_count_from_statement(); break; // case ASYNC_STMT_EXECUTE_SUCCESSFUL: // break; @@ -2615,7 +2616,6 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { // '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); } diff --git a/test/tap/tests/test_cluster_sync-t.cpp b/test/tap/tests/test_cluster_sync-t.cpp index 9e948ada8f..ace26132d0 100644 --- a/test/tap/tests/test_cluster_sync-t.cpp +++ b/test/tap/tests/test_cluster_sync-t.cpp @@ -1070,7 +1070,7 @@ int main(int, char**) { std::make_tuple(18, 2, -1, 20, "SET sql_mode = \"\"", 0, 0, 100, "", "", ""), std::make_tuple(19, 2, -1, 20, "SET sql_mode = \"\"", 0, 0, 100, "{}", "{}", "{}"), std::make_tuple(20, 0, 0, 30, "SET long_query_time = 0", 1, 0, 123, "{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}", "", ""), - std::make_tuple(21, 2, -1, 50, "SET sql_mode = \"\"", 1, 0, 125, "{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}", "{}", ""), + std::make_tuple(21, 2, -1, 50, "SET sql_mode = \"\"", 1, 0, 125, "{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}", "{\"handle_warnings\":1}", ""), std::make_tuple(22, 3, -1, 40, "SET sql_mode = \"\"", 1, 0, 124, "{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}", "", "{\"weight\": 100, \"max_connections\": 1000}") }; std::vector insert_mysql_hostgroup_attributes_queries{}; diff --git a/test/tap/tests/test_warnings-t.cpp b/test/tap/tests/test_warnings-t.cpp index 6ddc148b9c..65ed4c76d7 100644 --- a/test/tap/tests/test_warnings-t.cpp +++ b/test/tap/tests/test_warnings-t.cpp @@ -31,7 +31,12 @@ using MESSAGE = std::string; } \ } while(0) -#define MYSQL_CLEAR_RESULT(mysql) mysql_free_result(mysql_store_result(mysql)); +#define MYSQL_CLEAR_RESULT(mysql) mysql_free_result(mysql_store_result(mysql)); +#define MYSQL_CLEAR_STMT_RESULT(stmt) mysql_stmt_store_result(stmt); \ + mysql_stmt_free_result(stmt); + +#define INIT_QUERY_TEXT(QUERY, IS_SELECT) {QUERY, IS_SELECT, false} +#define INIT_QUERY_PREPARE_STMT(QUERY, IS_SELECT) {QUERY, IS_SELECT, true} enum MultiplexStatus { kNotApplicable = 0, @@ -42,8 +47,8 @@ enum MultiplexStatus { }; enum ConnectionType { - kAdmin, - kMySQL + kAdmin = 0, + kMySQL = 1 }; enum class WarningCheckType { @@ -57,6 +62,7 @@ enum class WarningCheckType { struct QueryInfo { const char* query; bool is_select; + bool prepare_stmt; }; struct WarningCheckInfo { @@ -65,13 +71,60 @@ struct WarningCheckInfo { std::vector warning_codes; }; -struct TestInfo { +struct Connection { ConnectionType conn_type; + size_t id; +}; + +struct TestInfo { + Connection conn; QueryInfo query_info; WarningCheckInfo warning_check_info; int multiplex_status; }; +#define MYSQL_CONN_DEFAULT {ConnectionType::kMySQL, 0} +#define ADMIN_CONN_DEFAULT {ConnectionType::kAdmin, 0} +#define MYSQL_CONN(ID) {ConnectionType::kMySQL, ID} +#define ADMIN_CONN(ID) {ConnectionType::kAdmin, ID} + +CommandLine cl; +std::array,2> conn_pool; + +MYSQL* get_connection(const Connection& conn, bool enable_client_deprecate_eof) { + auto& my_conn = conn_pool[conn.conn_type]; + const auto& itr = my_conn.find(conn.id); + if (itr != my_conn.end()) { + return itr->second; + } + // Initialize connection + MYSQL* proxysql = mysql_init(NULL); + if (!proxysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return NULL; + } + + if (enable_client_deprecate_eof) { + // enable 'CLIENT_DEPRECATE_EOF' support + proxysql->options.client_flag |= CLIENT_DEPRECATE_EOF; + } + + if (conn.conn_type == kAdmin) { + // Connnect to ProxySQL + if (!mysql_real_connect(proxysql, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return NULL; + } + } else if (conn.conn_type == kMySQL) { + // Connect to ProxySQL + if (!mysql_real_connect(proxysql, 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)); + return NULL; + } + } + my_conn[conn.id] = proxysql; + return proxysql; +} void parse_result_json_column(MYSQL_RES* result, nlohmann::json& j) { if (!result) return; @@ -89,11 +142,41 @@ int execute_query(MYSQL* proxysql, const QueryInfo& query_info) { return EXIT_SUCCESS; } +int prepare_and_execute_stmt(MYSQL* mysql, const QueryInfo& query_info, MYSQL_STMT** stmt_out) { + assert(stmt_out); + MYSQL_STMT* stmt = mysql_stmt_init(mysql); + if (!stmt) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); + return EXIT_FAILURE; + } + if (mysql_stmt_prepare(stmt, query_info.query, strlen(query_info.query))) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_stmt_error(stmt)); + mysql_stmt_close(stmt); + return EXIT_FAILURE; + } + if (mysql_stmt_execute(stmt) != 0) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_stmt_error(stmt)); + mysql_stmt_close(stmt); + return EXIT_FAILURE; + } + if (query_info.is_select) { + MYSQL_CLEAR_STMT_RESULT(stmt); + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + *stmt_out = stmt; + return EXIT_SUCCESS; +} + // get warning count from MySQL connection (MYSQL::warning_count) int get_warnings_count_from_connection(MYSQL* mysql) { return mysql_warning_count(mysql); } +// get warning count from statement (MYSQL_STMT::mysql_upsert_status::warning_count) +int get_warnings_count_from_statement(MYSQL_STMT* stmt) { + return mysql_stmt_warning_count(stmt); +} + // retrieve warning count through a query. This action does not clear the warning message list. int get_warnings_count(MYSQL* mysql) { MYSQL_QUERY(mysql, "SHOW COUNT(*) WARNINGS"); @@ -173,184 +256,209 @@ int check_proxysql_internal_session(MYSQL* proxysql, int exp_status) { } 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) } + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("DELETE FROM mysql_query_rules", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL QUERY RULES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("DELETE FROM mysql_hostgroup_attributes", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL SERVERS TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-handle_warnings=0", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1/0", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-handle_warnings=1", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1" , true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1" , true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("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) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-handle_warnings=1", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("DELETE FROM mysql_hostgroup_attributes", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("INSERT INTO mysql_hostgroup_attributes (hostgroup_id, hostgroup_settings) VALUES (0, '{\"handle_warnings\":0}')", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("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 + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled)}, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled)}, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1/0", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled)}, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled)}, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-handle_warnings=0", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("DELETE FROM mysql_hostgroup_attributes", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("INSERT INTO mysql_hostgroup_attributes (hostgroup_id, hostgroup_settings) VALUES (0, '{\"handle_warnings\":1}')", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL SERVERS TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) } }; -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, {"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 random_test = { + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1" , true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1" , false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SET character_set_database='latin1'", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1" , true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1" , true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1" , false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SET character_set_database='latin2'", false), {WarningCheckType::kAll, 1, {1681}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1" , true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) } }; const std::vector insert_test = { - { ConnectionType::kMySQL, {"SET sql_mode='ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kMySQL, {"DROP DATABASE IF EXISTS testdb", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kMySQL, {"CREATE DATABASE testdb", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kMySQL, {"CREATE TABLE testdb.t1 (a TINYINT NOT NULL, b CHAR(4))", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingDisabled) }, - { ConnectionType::kMySQL, {"INSERT INTO testdb.t1 VALUES(10, 'mysql'), (NULL, 'test'), (300, 'xyz')", false}, {WarningCheckType::kAll, 3, {1265,1048,1264}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, - { ConnectionType::kMySQL, {"SELECT 1", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, - { ConnectionType::kMySQL, {"DROP DATABASE IF EXISTS testdb", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingDisabled) } + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SET sql_mode='ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DROP DATABASE IF EXISTS testdb", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("CREATE DATABASE testdb", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("CREATE TABLE testdb.t1 (a TINYINT NOT NULL, b CHAR(4))", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("INSERT INTO testdb.t1 VALUES(10, 'mysql'), (NULL, 'test'), (300, 'xyz')", false), {WarningCheckType::kAll, 3, {1265,1048,1264}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("INSERT INTO testdb.t1 VALUES(10, 'mysql'), (NULL, 'test'), (300, 'xyz')", false), {WarningCheckType::kAll, 3, {1265,1048,1264}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DROP DATABASE IF EXISTS testdb", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingDisabled) } }; const std::vector query_cache_test = { - { 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) }, - { ConnectionType::kAdmin, {"LOAD MYSQL QUERY RULES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kAdmin, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-query_cache_handle_warnings=0", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("DELETE FROM mysql_query_rules", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("INSERT INTO mysql_query_rules (rule_id,active,match_digest,cache_ttl,apply) VALUES (1,1,'SELECT ?/?',60000,1)", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL QUERY RULES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("PROXYSQL FLUSH QUERY CACHE", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, // 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_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) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + // to check if prepare statement conflicts with cache + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + // { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-query_cache_handle_warnings=1", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, // resultset will be retrived from cache, with warning count zero - { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, - { 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, {"PROXYSQL FLUSH QUERY CACHE", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + // to check if prepare statement conflicts with cache + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("DELETE FROM mysql_query_rules", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL QUERY RULES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("PROXYSQL FLUSH QUERY CACHE", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } }; const std::vector query_digest_test = { - { ConnectionType::kAdmin, {"SET mysql-query_digests='false'", 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::kAdmin, {"SET mysql-query_digests='true'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-query_digests='false'", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("DO 1/0", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-query_digests='true'", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) } }; const std::vector warning_log_test = { - { ConnectionType::kAdmin, {"SET mysql-log_mysql_warnings_enabled='true'", 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::kAdmin, {"SET mysql-log_mysql_warnings_enabled='false'", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, - { ConnectionType::kAdmin, {"LOAD MYSQL VARIABLES TO RUNTIME", false}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-log_mysql_warnings_enabled='true'", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("DO 1", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingDisabled) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("SET mysql-log_mysql_warnings_enabled='false'", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) }, + { ADMIN_CONN_DEFAULT, INIT_QUERY_TEXT("LOAD MYSQL VARIABLES TO RUNTIME", false), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kNotApplicable) } }; const std::vector multiplexing_test = { - { ConnectionType::kMySQL, {"SELECT @@sql_mode", true}, {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) }, - { ConnectionType::kMySQL, {"SELECT 1/0", true}, {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables | MultiplexStatus::kHasWarnings) }, - { ConnectionType::kMySQL, {"SELECT 1", true}, {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) } + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT @@sql_mode", true), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables | MultiplexStatus::kHasWarnings) }, + { MYSQL_CONN_DEFAULT, INIT_QUERY_TEXT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) }, + { MYSQL_CONN(1), INIT_QUERY_TEXT("SELECT @@sql_mode", true), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables)}, + { MYSQL_CONN(1), INIT_QUERY_TEXT("DO 1/0", false), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables | MultiplexStatus::kHasWarnings)}, + { MYSQL_CONN(1), INIT_QUERY_TEXT("DO 1", false), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables)}, + { MYSQL_CONN(2), INIT_QUERY_PREPARE_STMT("SELECT @@sql_mode", true), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables)}, + { MYSQL_CONN(2), INIT_QUERY_PREPARE_STMT("SELECT 1/0", true), {WarningCheckType::kAll, 1, {1365}}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables | MultiplexStatus::kHasWarnings)}, + { MYSQL_CONN(2), INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) }, + { MYSQL_CONN(3), INIT_QUERY_PREPARE_STMT("SET @test_variable = 44", true), {WarningCheckType::kNotApplicable}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables)}, + { MYSQL_CONN(3), INIT_QUERY_PREPARE_STMT("SELECT 1", true), {WarningCheckType::kAll, 0}, (MultiplexStatus::kMultiplexingEnabled | MultiplexStatus::kUserVariables) } }; #define IS_BIT_MASK_SET(variable,flag) ((variable & static_cast(flag)) == static_cast(flag)) -int main(int argc, char** argv) { +// base case +size_t check_count() { return 0; } - CommandLine cl; +template +size_t check_count(First&& first, Rest&&... rest) { - if (cl.getEnv()) { - diag("Failed to get the required environmental variables."); - return -1; - } - - 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); - if (!proxysql_admin) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); - return -1; - } - // Connnect to ProxySQL Admin - if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); - return exit_status(); - } - - // Initialize ProxySQL connection - MYSQL* proxysql = mysql_init(NULL); - if (!proxysql) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); - return exit_status(); - } - - // Connect to ProxySQL - if (!mysql_real_connect(proxysql, 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)); - return exit_status(); - } - - std::vector>> all_tests(8); - - 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()); + size_t count = 0; - all_tests[2].first = "SELECT"; - all_tests[2].second.insert(all_tests[2].second.end(), select_test.begin(), select_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[4].first = "QUERY_DIGEST"; - all_tests[4].second.insert(all_tests[4].second.end(), query_digest_test.begin(), query_digest_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()); + for (const auto& val : first) { + if (val.warning_check_info.type != WarningCheckType::kNotApplicable) { + if (val.warning_check_info.type == WarningCheckType::kAll) + count += 3; + else + count += 1; + count += val.warning_check_info.warning_codes.size(); + } + if (val.multiplex_status != 0) + count += 1; + } + return (count + check_count(rest...)); +} - all_tests[6].first = "WARNING_LOGS"; - all_tests[6].second.insert(all_tests[6].second.end(), warning_log_test.begin(), warning_log_test.end()); +template +constexpr size_t test_size(Args&&... args) { + return sizeof...(args); +} - all_tests[7].first = "MULTIPLEXING"; - all_tests[7].second.insert(all_tests[7].second.end(), multiplexing_test.begin(), multiplexing_test.end()); +#define TESTS_COMBINED mysql_variable_test, hostgroup_attributes_test, random_test, insert_test, query_digest_test, \ + query_cache_test, warning_log_test, multiplexing_test +void execute_tests(const std::vector>>& all_tests, bool enable_client_deprecate_eof) { for (const auto& test : all_tests) { - diag("Executing [%s] test...", test.first); + diag("Executing [%s] test... [CLIENT_DEPRECATE_EOF=%s]", test.first, (enable_client_deprecate_eof ? "TRUE" : "FALSE")); for (const auto& test_info : test.second) { - MYSQL* mysql = (test_info.conn_type == ConnectionType::kMySQL ? proxysql : proxysql_admin); - - if (execute_query(mysql, test_info.query_info) == EXIT_FAILURE) + MYSQL_STMT* stmt = nullptr; + MYSQL* mysql = get_connection(test_info.conn, enable_client_deprecate_eof); + if (!mysql) { goto __exit; + } + if (test_info.query_info.prepare_stmt) { + if (prepare_and_execute_stmt(mysql, test_info.query_info, &stmt) == EXIT_FAILURE) + goto __exit; + } else { + if (execute_query(mysql, test_info.query_info) == EXIT_FAILURE) + goto __exit; + } const int check_type = static_cast(test_info.warning_check_info.type); if (IS_BIT_MASK_SET(check_type, WarningCheckType::kConnection)) { - const int count = get_warnings_count_from_connection(mysql); + int count = get_warnings_count_from_connection(mysql); + if (test_info.query_info.prepare_stmt) { + count &= get_warnings_count_from_statement(stmt); + } ok((count == test_info.warning_check_info.warning_count), "Connection warning count should match. Expected count:'%d' Actual count:'%d'", test_info.warning_check_info.warning_count, count); } if (IS_BIT_MASK_SET(check_type, WarningCheckType::kCountQuery)) { @@ -379,15 +487,76 @@ int main(int argc, char** argv) { } if (test_info.multiplex_status != MultiplexStatus::kNotApplicable) { - if (check_proxysql_internal_session(mysql, test_info.multiplex_status) != EXIT_SUCCESS) + if (check_proxysql_internal_session(mysql, test_info.multiplex_status) != EXIT_SUCCESS) { + if (stmt) + mysql_stmt_close(stmt); goto __exit; + } } + + if (stmt) + mysql_stmt_close(stmt); } } __exit: - mysql_close(proxysql); - mysql_close(proxysql_admin); + for (const auto& mysql_conn : conn_pool[kAdmin]) { + mysql_close(mysql_conn.second); + } + conn_pool[kAdmin].clear(); + for (const auto& mysql_conn : conn_pool[kMySQL]) { + mysql_close(mysql_conn.second); + } + conn_pool[kMySQL].clear(); +} + +int main(int argc, char** argv) { + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(check_count(TESTS_COMBINED)*2); // also check with client_deprecate_eof flag + + /*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) + // random 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 + */ + + std::vector>> all_tests(test_size(TESTS_COMBINED)); + + 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 = "RANDOM"; + all_tests[2].second.insert(all_tests[2].second.end(), random_test.begin(), random_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[4].first = "QUERY_DIGEST"; + all_tests[4].second.insert(all_tests[4].second.end(), query_digest_test.begin(), query_digest_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[6].first = "WARNING_LOGS"; + all_tests[6].second.insert(all_tests[6].second.end(), warning_log_test.begin(), warning_log_test.end()); + + all_tests[7].first = "MULTIPLEXING"; + all_tests[7].second.insert(all_tests[7].second.end(), multiplexing_test.begin(), multiplexing_test.end()); + + execute_tests(all_tests, false); + execute_tests(all_tests, true); return exit_status(); } From e1c489acb1b7ae6c984f9db12815b8591382fe0e Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 21 Nov 2023 00:57:50 +0500 Subject: [PATCH 15/18] * If the backend server has CLIENT_DEPRECATE_EOF enabled, and the client does not support CLIENT_DEPRECATE_EOF, warning_count will be '0' in intermediate EOF packet. * Suppress the display of warning counts in the "SHOW WARNINGS" statement. --- include/MySQL_Protocol.h | 2 +- lib/MySQL_Protocol.cpp | 8 +++++--- lib/MySQL_Session.cpp | 17 ++++++++++------- lib/mysql_connection.cpp | 15 ++++++++++++--- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index a7b68994c3..ad849a124f 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -51,7 +51,7 @@ class MySQL_ResultSet { unsigned int add_row(MYSQL_ROWS *rows); unsigned int add_row(MYSQL_ROW row); unsigned int add_row2(MYSQL_ROWS *row, unsigned char *offset); - void add_eof(); + void add_eof(bool suppress_warning_count=false); void remove_last_eof(); void add_err(MySQL_Data_Stream *_myds); bool get_resultset(PtrSizeArray *PSarrayFinal); diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index f19d5957bf..ddcbc84228 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -2726,7 +2726,9 @@ void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, // up to 2.2.0 we used to add an EOF here. // due to bug #3547 we move the logic into add_eof() that can now handle also prepared statements PROXY_TRACE2(); - add_eof(); + // if the backend server has CLIENT_DEPRECATE_EOF enabled, and the client does not support + // CLIENT_DEPRECATE_EOF, warning_count will be excluded from the intermediate EOF packet + add_eof((mysql->server_capabilities & CLIENT_DEPRECATE_EOF)); } } @@ -2990,7 +2992,7 @@ unsigned int MySQL_ResultSet::add_row2(MYSQL_ROWS *row, unsigned char *offset) { return length; } -void MySQL_ResultSet::add_eof() { +void MySQL_ResultSet::add_eof(bool suppress_warning_count) { if (myprot) { unsigned int nTrx=myds->sess->NumActiveTransactions(); uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); @@ -3007,7 +3009,7 @@ void MySQL_ResultSet::add_eof() { const MySQL_Data_Stream* _server_myds = (_mybe && _mybe->server_myds) ? _mybe->server_myds : nullptr; const MySQL_Connection* _myconn = (_server_myds && _server_myds->myds_type == MYDS_BACKEND && _server_myds->myconn) ? _server_myds->myconn : nullptr; - const unsigned int warning_count = (_myconn) ? _myconn->warning_count : 0; + const unsigned int warning_count = (_myconn && suppress_warning_count == false) ? _myconn->warning_count : 0; if (deprecate_eof_active) { PtrSize_t pkt; buffer_to_PSarrayOut(); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index d904d84712..fe9736fb48 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -8168,13 +8168,16 @@ void MySQL_Session::reset_warning_hostgroup_flag_and_release_connection() // if we've reached this point, it means that warning was found in the previous query, but the // current executed query is not 'SHOW WARNINGS' or 'SHOW COUNT(*) FROM WARNINGS', so we can safely reset warning_in_hg and // return connection back to the connection pool. - MySQL_Backend* _mybe = NULL; - _mybe = find_backend(warning_in_hg); - MySQL_Data_Stream* myds = _mybe->server_myds; - myds->myconn->warning_count = 0; - myds->myconn->set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); - if ((myds->myconn->reusable == true) && myds->myconn->IsActiveTransaction() == false && myds->myconn->MultiplexDisabled() == false) { - myds->return_MySQL_Connection_To_Pool(); + MySQL_Backend* _mybe = find_backend(warning_in_hg); + if (_mybe) { + MySQL_Data_Stream* myds = _mybe->server_myds; + if (myds && myds->myconn) { + myds->myconn->warning_count = 0; + myds->myconn->set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); + if ((myds->myconn->reusable == true) && myds->myconn->IsActiveTransaction() == false && myds->myconn->MultiplexDisabled() == false) { + myds->return_MySQL_Connection_To_Pool(); + } + } } warning_in_hg = -1; } diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 3788287b13..c5cb83a8c1 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -1565,6 +1565,12 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { NEXT_IMMEDIATE(ASYNC_QUERY_END); } else { // since 'add_eof' utilizes 'warning_count,' we are setting the 'warning_count' here + + // Note: There is a possibility of obtaining inaccurate warning_count and server_status at this point + // if the backend server has CLIENT_DEPRECATE_EOF enabled, and the client does not support CLIENT_DEPRECATE_EOF, + // especially when the query generates a warning. This information will be included in the intermediate EOF packet. + // Correct information becomes available only after fetching all rows, + // and the warning_count and status flag details are extracted from the final OK packet. update_warning_count_from_connection(); if (myds->sess->mirror==false) { if (MyRS_reuse == NULL) { @@ -1671,7 +1677,8 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { // since 'add_eof' utilizes 'warning_count,' we are setting the 'warning_count' here update_warning_count_from_connection(); // we reach here if there was no error - MyRS->add_eof(); + // exclude warning_count from the OK/EOF packet for the ‘SHOW WARNINGS’ statement + MyRS->add_eof(query.length == 13 && strncasecmp(query.ptr, "SHOW WARNINGS", 13) == 0); NEXT_IMMEDIATE(ASYNC_QUERY_END); } } @@ -2615,7 +2622,8 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { if (warning_count > 0) { // '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; + if (myds && myds->sess) + myds->sess->warning_in_hg = myds->sess->current_hostgroup; // enabling multiplexing set_status(true, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); } @@ -2627,7 +2635,8 @@ 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))) { - myds->sess->warning_in_hg = -1; + if (myds && myds->sess) + myds->sess->warning_in_hg = -1; warning_count = 0; // disabling multiplexing set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); From 581a0957b0c6b170a8e874442f9e952b05709095 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Fri, 24 Nov 2023 13:08:21 +0500 Subject: [PATCH 16/18] Fixed test_wexecvp_syscall_failures-t compiling --- test/tap/tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index d401704d2d..0c641eb1c8 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -231,4 +231,4 @@ prepare_statement_err3024_async-t: prepare_statement_err3024-t.cpp $(TAP_LIBDIR) $(CXX) -DASYNC_API prepare_statement_err3024-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o prepare_statement_err3024_async-t -DGITVERSION=\"$(GIT_VERSION)\" test_wexecvp_syscall_failures-t: test_wexecvp_syscall_failures-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) $^ $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 -Wl,--wrap=pipe,--wrap=fcntl,--wrap=read,--wrap=poll -lpthread -ldl -ltap $(STATIC_LIBS) -o $@ + $(CXX) test_wexecvp_syscall_failures-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 -Wl,--wrap=pipe,--wrap=fcntl,--wrap=read,--wrap=poll -lpthread -ldl -ltap $(STATIC_LIBS) -o $@ From f26ea6b44ec471b9f22a1bb5e42cbeda63d78c14 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 27 Nov 2023 01:01:39 +0500 Subject: [PATCH 17/18] * Overriding some of the mariadb APIs to extract the warning count and print it in the log. This override will apply to all TAP tests, except when the TAP test is linked with the MySQL client library (LIBMYSQL_HELPER defined). * Avoid closing of same mysql connection twice on error. * Disabled warning count logging on few TAP tests * Enable CLIENT_DEPRECATE_EOF on 20% of total mysql connections --- test/tap/tap/utils.cpp | 145 ++++++++++++++++++ test/tap/tap/utils.h | 35 +++++ test/tap/tests/Makefile | 12 +- .../reg_test_3427-stmt_first_comment1-t.cpp | 1 - test/tap/tests/set_testing-240-t.cpp | 5 + 5 files changed, 191 insertions(+), 7 deletions(-) diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index f5fc3354c8..f33a90c198 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -31,6 +31,151 @@ using std::vector; using std::to_string; using nlohmann::json; +#define LAST_QUERY_EXECUTED_STR(mysql) (*static_cast(mysql->unused_0)) +#define STMT_VECTOR(stmt) (*static_cast*>(stmt->mysql->unused_3)) +#define STMT_EXECUTED_VECTOR(stmt) (*static_cast>*>(stmt->mysql->unused_4)) +#define LAST_QUERY_EXECUTED_PTR(mysql) (static_cast(mysql->unused_0)) +#define STMT_VECTOR_PTR(mysql) (static_cast*>(mysql->unused_3)) +#define STMT_EXECUTED_VECTOR_PTR(mysql) (static_cast>*>(mysql->unused_4)) + +#define STMT_FIND_INDEX(stmt,idx) const std::vector& vec_stmt = STMT_VECTOR(stmt); \ + for (size_t i = 0; i < vec_stmt.size(); i++) {\ + if (vec_stmt[i] == stmt) {\ + idx = i; \ + break; \ + }\ + } + +#define STMT_PUSH_QUERY(stmt,query) size_t idx = -1; \ + STMT_FIND_INDEX(stmt,idx);\ + if (idx == -1) {\ + STMT_VECTOR(stmt).emplace_back(stmt); \ + STMT_EXECUTED_VECTOR(stmt).emplace_back(strdup(query), &free);\ + } else {\ + STMT_EXECUTED_VECTOR(stmt)[idx] = std::unique_ptr(strdup(query), &free);\ + } + +#define STMT_LOAD_QUERY(stmt,query) size_t idx = -1; \ + STMT_FIND_INDEX(stmt,idx);\ + if (idx != -1) query = STMT_EXECUTED_VECTOR(stmt)[idx].get(); + +#define STMT_REMOVE(stmt) size_t idx = -1; \ + STMT_FIND_INDEX(stmt,idx);\ + if (idx != -1) {\ + std::vector& vec_stmt = STMT_VECTOR(stmt);\ + std::vector>& vec_query = STMT_EXECUTED_VECTOR(stmt);\ + if (idx != vec_stmt.size() - 1) {\ + vec_stmt[idx] = vec_stmt.back();\ + vec_query[idx] = std::move(vec_query.back());\ + }\ + vec_stmt.pop_back();\ + vec_query.pop_back();\ + } + +MYSQL* mysql_init_override(MYSQL* mysql, const char* file, int line) { + static bool init = false; + MYSQL* result = (*real_mysql_init)(mysql); + if (init == false) { + init = true; + fprintf(stdout, ">> [mysql_init] Override functions attached <<\n"); + } + result->unused_0 = new std::string; + result->unused_3 = nullptr; + result->unused_4 = nullptr; + return result; +} + +int mysql_query_override(MYSQL* mysql, const char* query, const char* file, int line) { + const int result = (*real_mysql_query)(mysql, query); + if (result == 0) { + LAST_QUERY_EXECUTED_STR(mysql) = query; + if (mysql_errno(mysql) == 0 && mysql_field_count(mysql) == 0 && mysql_warning_count(mysql) > 0) { + fprintf(stdout, "File %s, Line %d, [mysql_query] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, query, mysql_warning_count(mysql)); + } + } + return result; +} + +MYSQL_RES* mysql_store_result_override(MYSQL* mysql, const char* file, int line) { + MYSQL_RES* result = (*real_mysql_store_result)(mysql); + if (mysql_errno(mysql) == 0 && mysql_warning_count(mysql) > 0) { + fprintf(stdout, "File %s, Line %d, [mysql_store_result] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, LAST_QUERY_EXECUTED_STR(mysql).c_str(), mysql_warning_count(mysql)); + } + return result; +} + +void mysql_close_override(MYSQL* mysql, const char* file, int line) { + delete LAST_QUERY_EXECUTED_PTR(mysql); + if (STMT_VECTOR_PTR(mysql)) { + delete STMT_VECTOR_PTR(mysql); + delete STMT_EXECUTED_VECTOR_PTR(mysql); + } + (*real_mysql_close)(mysql); +} + +MYSQL_STMT* mysql_stmt_init_override(MYSQL* mysql, const char* file, int line) { + MYSQL_STMT* result = (*real_mysql_stmt_init)(mysql); + if (result->mysql->unused_3 == nullptr) { + std::vector* vec_stmt = new std::vector; + std::vector>* vec_query = + new std::vector>; + vec_stmt->reserve(64); + vec_query->reserve(64); + result->mysql->unused_3 = vec_stmt; + result->mysql->unused_4 = vec_query; + } + return result; +} + +int mysql_stmt_prepare_override(MYSQL_STMT* stmt, const char* stmt_str, unsigned long length, const char* file, int line) { + const int result = (*real_mysql_stmt_prepare)(stmt, stmt_str, length); + if (result == 0) { + STMT_PUSH_QUERY(stmt,stmt_str); + // mysql_stmt_warning_count is not available in MySQL connector + if (mysql_stmt_errno(stmt) == 0 && /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql) > 0) { + fprintf(stdout, "File %s, Line %d, [mysql_stmt_prepare] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, stmt_str, /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql)); + } + } + return result; +} + +int mysql_stmt_execute_override(MYSQL_STMT* stmt, const char* file, int line) { + const int result = (*real_mysql_stmt_execute)(stmt); + if (result == 0) { + // mysql_stmt_warning_count is not available in MySQL connector + if (mysql_stmt_errno(stmt) == 0 && mysql_stmt_field_count(stmt) == 0 && + /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql) > 0) { + char* query = nullptr; + STMT_LOAD_QUERY(stmt, query); + fprintf(stdout, "File %s, Line %d, [mysql_stmt_execute] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, (query ? query : ""), /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql)); + } + } + return result; +} + +int mysql_stmt_store_result_override(MYSQL_STMT* stmt, const char* file, int line) { + const int result = (*real_mysql_stmt_store_result)(stmt); + if (result == 0) { + // mysql_stmt_warning_count is not available in MySQL connector + if (mysql_stmt_errno(stmt) == 0 && /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql) > 0) { + char* query = nullptr; + STMT_LOAD_QUERY(stmt, query); + fprintf(stdout, "File %s, Line %d, [mysql_stmt_store_result] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, (query ? query : ""), /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql)); + } + } + return result; +} + +my_bool mysql_stmt_close_override(MYSQL_STMT* stmt, const char* file, int line) { + STMT_REMOVE(stmt) + return (*real_mysql_stmt_close)(stmt); +} + std::size_t count_matches(const string& str, const string& substr) { std::size_t result = 0; std::size_t pos = 0; diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index da13c69e8f..d028c1f277 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -16,6 +16,41 @@ #include "command_line.h" #include "json.hpp" +#ifndef DISABLE_WARNING_COUNT_LOGGING +/* We are overriding some of the mariadb APIs to extract the warning count and print it in the log. + This override will apply to all TAP tests, except when the TAP test is linked with the MySQL client library (LIBMYSQL_HELPER defined). +*/ +static MYSQL* (*real_mysql_init)(MYSQL* mysql) = &mysql_init; +static int (*real_mysql_query)(MYSQL* mysql, const char* query) = &mysql_query; +static MYSQL_RES* (*real_mysql_store_result)(MYSQL* mysql) = &mysql_store_result; +static void (*real_mysql_close)(MYSQL* mysql) = &mysql_close; +static MYSQL_STMT* (*real_mysql_stmt_init)(MYSQL* mysql) = &mysql_stmt_init; +static int (*real_mysql_stmt_prepare)(MYSQL_STMT* stmt, const char* stmt_str, unsigned long length) = &mysql_stmt_prepare; +static int (*real_mysql_stmt_execute)(MYSQL_STMT* stmt) = &mysql_stmt_execute; +static int (*real_mysql_stmt_store_result)(MYSQL_STMT* stmt) = &mysql_stmt_store_result; +static my_bool (*real_mysql_stmt_close)(MYSQL_STMT* stmt) = &mysql_stmt_close; + +MYSQL* mysql_init_override(MYSQL* mysql, const char* file, int line); +int mysql_query_override(MYSQL* mysql, const char* query, const char* file, int line); +MYSQL_RES* mysql_store_result_override(MYSQL* mysql, const char* file, int line); +void mysql_close_override(MYSQL* mysql, const char* file, int line); +MYSQL_STMT* mysql_stmt_init_override(MYSQL* mysql, const char* file, int line); +int mysql_stmt_prepare_override(MYSQL_STMT* stmt, const char* stmt_str, unsigned long length, const char* file, int line); +int mysql_stmt_execute_override(MYSQL_STMT* stmt, const char* file, int line); +int mysql_stmt_store_result_override(MYSQL_STMT* stmt, const char* file, int line); +my_bool mysql_stmt_close_override(MYSQL_STMT* stmt, const char* file, int line); + +#define mysql_init(mysql) mysql_init_override(mysql,__FILE__,__LINE__) +#define mysql_query(mysql,query) mysql_query_override(mysql,query,__FILE__,__LINE__) +#define mysql_store_result(mysql) mysql_store_result_override(mysql,__FILE__,__LINE__) +#define mysql_close(mysql) mysql_close_override(mysql,__FILE__,__LINE__) +#define mysql_stmt_init(mysql) mysql_stmt_init_override(mysql,__FILE__,__LINE__) +#define mysql_stmt_prepare(stmt,stmt_str,length) mysql_stmt_prepare_override(stmt,stmt_str,length,__FILE__,__LINE__) +#define mysql_stmt_execute(stmt) mysql_stmt_execute_override(stmt,__FILE__,__LINE__) +#define mysql_stmt_store_result(stmt) mysql_stmt_store_result_override(stmt,__FILE__,__LINE__) +#define mysql_stmt_close(stmt) mysql_stmt_close_override(stmt,__FILE__,__LINE__) +#endif + inline std::string get_formatted_time() { time_t __timer; char __buffer[30]; diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 0c641eb1c8..5a4d57bc0c 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -207,25 +207,25 @@ setparser_test3: setparser_test3.cpp $(TAP_LIBDIR)/libtap.a $(LDIR)/set_parser.c CUSTOMARGS=-DGITVERSION=\"$(GIT_VERSION)\" -I$(SQLITE3_DIR) -I$(IDIR) -I$(CURL_IDIR) -I$(JSON_IDIR) -I../tap -L$(TAP_LIBDIR) -L$(CURL_LDIR) -Wl,-Bstatic -lcurl -Wl,-Bdynamic -ltap -lcpp_dotenv -lpthread -std=c++11 -lz -ldl reg_test_3504-change_user_libmariadb_helper: reg_test_3504-change_user_helper.cpp $(TAP_LIBDIR)/libtap.so - $(CXX) -DDEBUG reg_test_3504-change_user_helper.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 $(STATIC_LIBS) -o reg_test_3504-change_user_libmariadb_helper -DGITVERSION=\"$(GIT_VERSION)\" + $(CXX) -DDISABLE_WARNING_COUNT_LOGGING -DDEBUG reg_test_3504-change_user_helper.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 $(STATIC_LIBS) -o reg_test_3504-change_user_libmariadb_helper -DGITVERSION=\"$(GIT_VERSION)\" reg_test_3504-change_user_libmysql_helper: reg_test_3504-change_user_helper.cpp - $(CXX) -DLIBMYSQL_HELPER reg_test_3504-change_user_helper.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_3504-change_user_libmysql_helper + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING reg_test_3504-change_user_helper.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_3504-change_user_libmysql_helper test_clickhouse_server_libmysql-t: test_clickhouse_server-t.cpp - $(CXX) -DLIBMYSQL_HELPER -DDEBUG test_clickhouse_server-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o test_clickhouse_server_libmysql-t + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING -DDEBUG test_clickhouse_server-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o test_clickhouse_server_libmysql-t reg_test_stmt_resultset_err_no_rows_libmysql-t: reg_test_stmt_resultset_err_no_rows-t.cpp - $(CXX) -DLIBMYSQL_HELPER reg_test_stmt_resultset_err_no_rows-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_stmt_resultset_err_no_rows_libmysql-t + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING reg_test_stmt_resultset_err_no_rows-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_stmt_resultset_err_no_rows_libmysql-t reg_test_mariadb_stmt_store_result_libmysql-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) -DLIBMYSQL_HELPER reg_test_mariadb_stmt_store_result-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_mariadb_stmt_store_result_libmysql-t + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING reg_test_mariadb_stmt_store_result-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o reg_test_mariadb_stmt_store_result_libmysql-t reg_test_mariadb_stmt_store_result_async-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LIBDIR)/libtap.a $(CXX) -DASYNC_API reg_test_mariadb_stmt_store_result-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o reg_test_mariadb_stmt_store_result_async-t prepare_statement_err3024_libmysql-t: prepare_statement_err3024-t.cpp $(TAP_LIBDIR)/libtap.a - $(CXX) -DLIBMYSQL_HELPER prepare_statement_err3024-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o prepare_statement_err3024_libmysql-t + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING prepare_statement_err3024-t.cpp -I/usr/include/mysql $(CUSTOMARGS) -lmysqlclient -o prepare_statement_err3024_libmysql-t prepare_statement_err3024_async-t: prepare_statement_err3024-t.cpp $(TAP_LIBDIR)/libtap.a $(CXX) -DASYNC_API prepare_statement_err3024-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o prepare_statement_err3024_async-t -DGITVERSION=\"$(GIT_VERSION)\" diff --git a/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp b/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp index 4b03f23fdc..0c0350ee8e 100644 --- a/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp +++ b/test/tap/tests/reg_test_3427-stmt_first_comment1-t.cpp @@ -168,7 +168,6 @@ int main(int argc, char** argv) { if (mysql_stmt_prepare(stmt, query.c_str(), strlen(query.c_str()))) { diag("mysql_stmt_prepare at line %d failed: %s", __LINE__ , mysql_error(proxysql_mysql)); - mysql_close(proxysql_mysql); res = EXIT_FAILURE; goto exit; } else { diff --git a/test/tap/tests/set_testing-240-t.cpp b/test/tap/tests/set_testing-240-t.cpp index 0576bc4b81..3a0fc53f24 100644 --- a/test/tap/tests/set_testing-240-t.cpp +++ b/test/tap/tests/set_testing-240-t.cpp @@ -39,6 +39,7 @@ int queries_per_connections=10; //unsigned int num_threads=5; unsigned int num_threads=20; int count=20; +int total_conn_having_client_deprecate_eof_support = (count * 0.2); // 20% of connections will have CLIENT_DEPRECATE_EOF flag enabled char *username=NULL; char *password=NULL; char *host=(char *)"localhost"; @@ -114,6 +115,10 @@ void * my_conn_thread(void *arg) { if (mysql==NULL) { exit(EXIT_FAILURE); } + if (i < total_conn_having_client_deprecate_eof_support) { + // enable 'CLIENT_DEPRECATE_EOF' support + mysql->options.client_flag |= CLIENT_DEPRECATE_EOF; + } MYSQL *rc=mysql_real_connect(mysql, cl.root_host, cl.root_username, cl.root_password, schema, (local ? 0 : ( port + rand()%multiport ) ), NULL, 0); // MYSQL *rc=mysql_real_connect(mysql, cl.mysql_host, cl.mysql_username, cl.mysql_password, schema, (local ? 0 : ( cl.mysql_port + rand()%multiport ) ), NULL, 0); if (rc==NULL) { From 676c6dfe80ff9ef63081938581f41783897c8490 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 28 Nov 2023 13:18:18 +0500 Subject: [PATCH 18/18] Added README --- test/tap/tests/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/tap/tests/README.md diff --git a/test/tap/tests/README.md b/test/tap/tests/README.md new file mode 100644 index 0000000000..a34fe9b621 --- /dev/null +++ b/test/tap/tests/README.md @@ -0,0 +1,13 @@ +## Warning Count Logging in ProxySQL TAP Tests + +With the exception of a few, all TAP tests are now geared up to log warning count and the query that triggered the warning during its execution. + +## Working + +The method for extracting both the warning count and the associated query in all TAP tests involves overriding of specific APIs of MariaDB client library. This method facilitates the seamless extraction of both the warning count and the query. + +## Default Settings + +By default, the logging of both the warning count and the associated query is activated for all TAP tests. + +However, there are specific tests where logging is intentionally disabled. If needed, you have the flexibility to disable the logging by defining the preprocessor directive 'DISABLE_WARNING_COUNT_LOGGING'.