From a4a5641f5bc29a845ac11d3a672eb19cf3e4cf60 Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Thu, 22 Mar 2018 14:15:35 +0200 Subject: [PATCH] MXS-1703 Add convenience function + class for querying and storing results An object of the class is returned as an auto_ptr to simplify memory management. --- .../monitor/mariadbmon/mariadbserver.cc | 17 +++- .../monitor/mariadbmon/mariadbserver.hh | 24 ++++-- .../modules/monitor/mariadbmon/utilities.cc | 78 +++++++++++++++++++ .../modules/monitor/mariadbmon/utilities.hh | 76 ++++++++++++++++++ 4 files changed, 187 insertions(+), 8 deletions(-) diff --git a/server/modules/monitor/mariadbmon/mariadbserver.cc b/server/modules/monitor/mariadbmon/mariadbserver.cc index 108ba5b8e..e720ef852 100644 --- a/server/modules/monitor/mariadbmon/mariadbserver.cc +++ b/server/modules/monitor/mariadbmon/mariadbserver.cc @@ -15,7 +15,7 @@ #include #include - +#include #include "utilities.hh" Gtid::Gtid() @@ -116,3 +116,18 @@ int64_t MariaDBServer::relay_log_events() return -1; } +std::auto_ptr MariaDBServer::execute_query(const string& query) +{ + auto conn = server_base->con; + std::auto_ptr rval; + MYSQL_RES *result = NULL; + if (mxs_mysql_query(conn, query.c_str()) == 0 && (result = mysql_store_result(conn)) != NULL) + { + rval = std::auto_ptr(new QueryResult(result)); + } + else + { + mon_report_query_error(server_base); + } + return rval; +} \ No newline at end of file diff --git a/server/modules/monitor/mariadbmon/mariadbserver.hh b/server/modules/monitor/mariadbmon/mariadbserver.hh index ebadde825..c0cb77f29 100644 --- a/server/modules/monitor/mariadbmon/mariadbserver.hh +++ b/server/modules/monitor/mariadbmon/mariadbserver.hh @@ -16,10 +16,10 @@ #include #include - +#include #include +#include "utilities.hh" -using std::string; enum mysql_server_version { MYSQL_SERVER_VERSION_100, @@ -47,7 +47,7 @@ public: bool operator == (const Gtid& rhs) const; - string to_string() const; + std::string to_string() const; /** * Generate a MASTER_GTID_WAIT()-query to this gtid. @@ -55,7 +55,7 @@ public: * @param timeout Maximum wait time in seconds * @return The query */ - string generate_master_gtid_wait_cmd(double timeout) const; + std::string generate_master_gtid_wait_cmd(double timeout) const; private: void parse_triplet(const char* str); @@ -67,17 +67,17 @@ class SlaveStatusInfo public: int64_t master_server_id; /**< The master's server_id value. Valid ids are 32bit unsigned. -1 is * unread/error. */ - string master_host; /**< Master server host name. */ + std::string master_host; /**< Master server host name. */ int master_port; /**< Master server port. */ bool slave_io_running; /**< Whether the slave I/O thread is running and connected. */ bool slave_sql_running; /**< Whether or not the SQL thread is running. */ - string master_log_file; /**< Name of the master binary log file that the I/O thread is currently + std::string master_log_file; /**< Name of the master binary log file that the I/O thread is currently * reading from. */ uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master * binary log file. */ Gtid gtid_io_pos; /**< Gtid I/O position of the slave thread. Only shows the triplet with * the current master domain. */ - string last_error; /**< Last IO or SQL error encountered. */ + std::string last_error; /**< Last IO or SQL error encountered. */ SlaveStatusInfo(); }; @@ -137,4 +137,14 @@ public: * an error in the gtid-values. */ int64_t relay_log_events(); + + /** + * Execute a query which returns data. The results are returned as an auto-pointer to a QueryResult + * object. + * + * @param query The query + * @return Pointer to query results, or an empty auto-ptr on failure. Currently, the column names of the + * results are assumed unique. + */ + std::auto_ptr execute_query(const std::string& query); }; \ No newline at end of file diff --git a/server/modules/monitor/mariadbmon/utilities.cc b/server/modules/monitor/mariadbmon/utilities.cc index bc57ec336..c428d0a5a 100644 --- a/server/modules/monitor/mariadbmon/utilities.cc +++ b/server/modules/monitor/mariadbmon/utilities.cc @@ -113,3 +113,81 @@ string monitored_servers_to_string(const ServerVector& array) } return rval; } + +QueryResult::QueryResult(MYSQL_RES* resultset) + : m_resultset(resultset) + , m_columns(-1) + , m_rowdata(NULL) + , m_current_row(-1) +{ + if (m_resultset) + { + m_columns = mysql_num_fields(m_resultset); + MYSQL_FIELD* field_info = mysql_fetch_fields(m_resultset); + for (int64_t column_index = 0; column_index < m_columns; column_index++) + { + string key(field_info[column_index].name); + // TODO: Think of a way to handle duplicate names nicely. Currently this should only be used + // for known queries. + ss_dassert(m_col_indexes.count(key) == 0); + m_col_indexes[key] = column_index; + } + } +} + +QueryResult::~QueryResult() +{ + if (m_resultset) + { + mysql_free_result(m_resultset); + } +} + +bool QueryResult::next_row() +{ + m_rowdata = mysql_fetch_row(m_resultset); + if (m_rowdata != NULL) + { + m_current_row++; + return true; + } + return false; +} + +int64_t QueryResult::get_row_index() const +{ + return m_current_row; +} + +int64_t QueryResult::get_column_count() const +{ + return m_columns; +} + +int64_t QueryResult::get_col_index(const string& col_name) const +{ + auto iter = m_col_indexes.find(col_name); + return (iter != m_col_indexes.end()) ? iter->second : -1; +} + +string QueryResult::get_string(int64_t column_ind) const +{ + ss_dassert(column_ind < m_columns); + char* data = m_rowdata[column_ind]; + return data ? data : ""; +} + +int64_t QueryResult::get_int(int64_t column_ind) const +{ + ss_dassert(column_ind < m_columns); + char* data = m_rowdata[column_ind]; + errno = 0; // strtoll sets this + return data ? strtoll(data, NULL, 10) : 0; +} + +bool QueryResult::get_bool(int64_t column_ind) const +{ + ss_dassert(column_ind < m_columns); + char* data = m_rowdata[column_ind]; + return data ? (strcmp(data,"Y") == 0 || strcmp(data, "1") == 0) : false; +} diff --git a/server/modules/monitor/mariadbmon/utilities.hh b/server/modules/monitor/mariadbmon/utilities.hh index c6ad0b50f..9ef550462 100644 --- a/server/modules/monitor/mariadbmon/utilities.hh +++ b/server/modules/monitor/mariadbmon/utilities.hh @@ -71,3 +71,79 @@ string get_connection_errors(const ServerVector& servers); * @return Server names */ string monitored_servers_to_string(const ServerVector& array); + +/** + * Helper class for simplifying working with resultsets. Used in MariaDBServer. + */ +class QueryResult +{ + // These need to be banned to avoid premature destruction. + QueryResult(const QueryResult&) = delete; + QueryResult& operator = (const QueryResult&) = delete; + +public: + QueryResult(MYSQL_RES* resultset = NULL); + ~QueryResult(); + + /** + * Advance to next row. Affects all result returning functions. + * + * @return True if the next row has data, false if the current row was the last one. + */ + bool next_row(); + + /** + * Get the index of the current row. + * + * @return Current row index, or -1 if no data or next_row() has not been called yet. + */ + int64_t get_row_index() const; + + /** + * How many columns the result set has. + * + * @return Column count, or -1 if no data. + */ + int64_t get_column_count() const; + + /** + * Get a numeric index for a column name. May give wrong results if column names are not unique. + * + * @param col_name Column name + * @return Index or -1 if not found. + */ + int64_t get_col_index(const string& col_name) const; + + /** + * Read a string value from the current row and given column. Empty string and (null) are both interpreted + * as the empty string. + * + * @param column_ind Column index + * @return Value as string + */ + string get_string(int64_t column_ind) const; + + /** + * Read an integer value from the current row and given column. No error checking is done on the parsing. + * The parsing is performed by @c strtoll(), so the caller may check errno for errors. + * + * @param column_ind Column index + * @return Value as integer + */ + int64_t get_int(int64_t column_ind) const; + + /** + * Read a boolean value from the current row and given column. + * + * @param column_ind Column index + * @return Value as boolean. Returns true if the text is either 'Y' or '1'. + */ + bool get_bool(int64_t column_ind) const; + +private: + MYSQL_RES* m_resultset; // Underlying result set, freed at dtor. + std::tr1::unordered_map m_col_indexes; // Map of column name -> index + int64_t m_columns; // How many columns does the data have. Usually equal to column index map size. + MYSQL_ROW m_rowdata; // Data for current row + int64_t m_current_row; // Index of current row +};