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.
This commit is contained in:
Esa Korhonen
2018-03-22 14:15:35 +02:00
parent 3b26db8e01
commit a4a5641f5b
4 changed files with 187 additions and 8 deletions

View File

@ -15,7 +15,7 @@
#include <inttypes.h>
#include <sstream>
#include <maxscale/mysql_utils.h>
#include "utilities.hh"
Gtid::Gtid()
@ -116,3 +116,18 @@ int64_t MariaDBServer::relay_log_events()
return -1;
}
std::auto_ptr<QueryResult> MariaDBServer::execute_query(const string& query)
{
auto conn = server_base->con;
std::auto_ptr<QueryResult> rval;
MYSQL_RES *result = NULL;
if (mxs_mysql_query(conn, query.c_str()) == 0 && (result = mysql_store_result(conn)) != NULL)
{
rval = std::auto_ptr<QueryResult>(new QueryResult(result));
}
else
{
mon_report_query_error(server_base);
}
return rval;
}

View File

@ -16,10 +16,10 @@
#include <maxscale/cppdefs.hh>
#include <string>
#include <memory>
#include <maxscale/monitor.h>
#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<QueryResult> execute_query(const std::string& query);
};

View File

@ -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;
}

View File

@ -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<string, int64_t> 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
};