From 0c904eae5eb25c30286d46d4f6b99764631621c7 Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Wed, 9 Dec 2020 17:43:50 +0200 Subject: [PATCH] MXS-3158 Preserve character set and collation when altering events During switchover/failover, server events are altered. The ALTER EVENT command automatically modifies the event charset and collation to the values of the connetion running the query. This may cause the event to become invalid. Fixed this by changing connection charset and collation to the ones in the event description just before altering it. --- server/core/monitor.cc | 1 + .../monitor/mariadbmon/mariadbserver.cc | 54 ++++++++++++++----- .../monitor/mariadbmon/mariadbserver.hh | 2 + 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/server/core/monitor.cc b/server/core/monitor.cc index 06baec1a1..081a4594f 100644 --- a/server/core/monitor.cc +++ b/server/core/monitor.cc @@ -1162,6 +1162,7 @@ Monitor::ping_or_connect_to_db(const MonitorServer::ConnectionSettings& sett, SE mysql_optionsv(pConn, MYSQL_OPT_READ_TIMEOUT, &sett.read_timeout); mysql_optionsv(pConn, MYSQL_OPT_WRITE_TIMEOUT, &sett.write_timeout); mysql_optionsv(pConn, MYSQL_PLUGIN_DIR, get_connector_plugindir()); + mysql_optionsv(pConn, MARIADB_OPT_MULTI_STATEMENTS, nullptr); time_t start = time(NULL); if (mxs_mysql_real_connect(pConn, &server, uname.c_str(), dpwd)) diff --git a/server/modules/monitor/mariadbmon/mariadbserver.cc b/server/modules/monitor/mariadbmon/mariadbserver.cc index e98ef93e5..4abb10b58 100644 --- a/server/modules/monitor/mariadbmon/mariadbserver.cc +++ b/server/modules/monitor/mariadbmon/mariadbserver.cc @@ -104,18 +104,30 @@ bool MariaDBServer::execute_cmd_ex(const string& cmd, QueryRetryMode mode, bool rval = false; if (query_success) { - MYSQL_RES* result = mysql_store_result(conn); - if (result == NULL) + // In case query was a multiquery, loop for more resultsets. Error message is produced from first + // non-empty resultset and does not specify the subquery. + string results_errmsg; + do + { + MYSQL_RES* result = mysql_store_result(conn); + if (result) + { + int cols = mysql_num_fields(result); + int rows = mysql_num_rows(result); + if (results_errmsg.empty()) + { + results_errmsg = string_printf("Query '%s' on '%s' returned %d columns and %d rows " + "of data when none was expected.", + cmd.c_str(), name(), cols, rows); + } + } + } + while (mysql_next_result(conn) == 0); + + if (results_errmsg.empty()) { rval = true; } - else if (errmsg_out) - { - int cols = mysql_num_fields(result); - int rows = mysql_num_rows(result); - *errmsg_out = string_printf("Query '%s' on '%s' returned %d columns and %d rows of data when " - "none was expected.", cmd.c_str(), name(), cols, rows); - } } else { @@ -1285,6 +1297,12 @@ MariaDBServer::alter_events(BinlogMode binlog_mode, const EventStatusMapper& map { if (target_events > 0) { + // Reset character set and collation. + string charset_errmsg; + if (!execute_cmd("SET NAMES latin1 COLLATE latin1_swedish_ci;", &charset_errmsg)) + { + MXS_ERROR("Could not reset character set: %s", charset_errmsg.c_str()); + } warn_event_scheduler(); } if (target_events == events_altered) @@ -1350,7 +1368,10 @@ bool MariaDBServer::events_foreach(EventManipulator& func, json_t** error_out) auto event_name_ind = event_info->get_col_index("EVENT_NAME"); auto event_definer_ind = event_info->get_col_index("DEFINER"); auto event_status_ind = event_info->get_col_index("STATUS"); - mxb_assert(db_name_ind > 0 && event_name_ind > 0 && event_definer_ind > 0 && event_status_ind > 0); + auto charset_ind = event_info->get_col_index("CHARACTER_SET_CLIENT"); + auto collation_ind = event_info->get_col_index("COLLATION_CONNECTION"); + mxb_assert(db_name_ind > 0 && event_name_ind > 0 && event_definer_ind > 0 && event_status_ind > 0 + && charset_ind > 0 && collation_ind > 0); while (event_info->next_row()) { @@ -1358,6 +1379,8 @@ bool MariaDBServer::events_foreach(EventManipulator& func, json_t** error_out) event.name = event_info->get_string(db_name_ind) + "." + event_info->get_string(event_name_ind); event.definer = event_info->get_string(event_definer_ind); event.status = event_info->get_string(event_status_ind); + event.charset = event_info->get_string(charset_ind); + event.collation = event_info->get_string(collation_ind); func(event, error_out); } return true; @@ -1395,10 +1418,13 @@ bool MariaDBServer::alter_event(const EventInfo& event, const string& target_sta quoted_definer = event.definer; } - string alter_event_query = string_printf("ALTER DEFINER = %s EVENT %s %s;", - quoted_definer.c_str(), - event.name.c_str(), - target_status.c_str()); + // Change character set and collation to the values in the event description. Otherwise, the event + // values could be changed to whatever the monitor connection happens to be using. + string alter_event_query = string_printf( + "SET NAMES %s COLLATE %s; ALTER DEFINER = %s EVENT %s %s;", + event.charset.c_str(), event.collation.c_str(), quoted_definer.c_str(), event.name.c_str(), + target_status.c_str()); + if (execute_cmd(alter_event_query, &error_msg)) { rval = true; diff --git a/server/modules/monitor/mariadbmon/mariadbserver.hh b/server/modules/monitor/mariadbmon/mariadbserver.hh index 89de0eb03..eb7c18203 100644 --- a/server/modules/monitor/mariadbmon/mariadbserver.hh +++ b/server/modules/monitor/mariadbmon/mariadbserver.hh @@ -82,6 +82,8 @@ public: std::string name; /**< Event name in form */ std::string definer; /**< Definer of the event */ std::string status; /**< Status of the event */ + std::string charset; /**< character_set_client-field */ + std::string collation; /**< collation_connection-field */ }; enum class server_type