diff --git a/include/ArgParse.hpp b/include/ArgParse.hpp index fb249b4..d9df2fc 100644 --- a/include/ArgParse.hpp +++ b/include/ArgParse.hpp @@ -42,11 +42,11 @@ public: * @param name short and/or long form: "-f,--foo" * The value can be retreived with this string passed to the argAs... functions. * @param help Description of the argument, printed when --help is given. + * @param valueType Type of the paramterer, required by the argument. + * @param valueRequired Parameter requiered/optional after the argument. * @param argRequired Argument is required or optional. * It's a bad practice to have a required argument, * "options", shall be optional. - * @param valueType Type of the paramterer, required by the argument. - * @param valueRequired Parameter requiered/optional after the argument. * @param valueName Default is the type. But some short text can be better. * @param choices Comma separeted list of strings without whitespaces: * "yes,no,maybe" @@ -55,8 +55,8 @@ public: void addArgument(const std::string name, const std::string help, const ValueType valueType = NONE, - const Required argRequired = OPTIONAL, const Required valueRequired = REQUIRED, + const Required argRequired = OPTIONAL, const std::string valueName = std::string(""), const std::string choices = std::string("")); @@ -121,8 +121,8 @@ private: struct Argument { const std::string m_help; const ValueType m_type; - const Required m_argRequired; const Required m_valueRequired; + const Required m_argRequired; const std::string m_valueName; const std::string m_choices; std::string m_value; @@ -131,8 +131,8 @@ private: Argument (const std::string help, const ValueType type = NONE, - const Required argRequired = OPTIONAL, const Required valueRequired = REQUIRED, + const Required argRequired = OPTIONAL, const std::string valueName = std::string(""), const std::string choices = std::string(""), const std::string value = std::string("")); diff --git a/include/MysqlClient.hpp b/include/MysqlClient.hpp index f76c551..211a9c5 100644 --- a/include/MysqlClient.hpp +++ b/include/MysqlClient.hpp @@ -11,54 +11,41 @@ class MysqlClient public: - /** - * For more details about the params, - * check the documentation of mysql_real_connect - * - * @note Call init_client_errs() / finish_client_errs() before / after. - * - * @param host May be either a host name or an IP address. - * If host is NULL or the string "localhost", - * a connection to the local host is assumed. - * @param user If user is NULL or the empty string "", - * the current user is assumed. - * @param passwd If passwd is NULL, only entries in the user table - * for the user that have a blank (empty) password field - * are checked for a match. - * @param db If db is not NULL, the connection sets the default - * database to this value. - * @param port If port is not 0, the value is used as the port number - * for the TCP/IP connection. Note that the host parameter determines - * the type of the connection. - * @param unix_socket If unix_socket is not NULL, the string specifies - * the socket or named pipe that should be used. Note that the host parameter - * determines the type of the connection. - * @param clientflag Usually 0, but can be set to a combination - * of flags to enable certain features. - */ - MysqlClient ( const char *host = NULL, - const char *user = NULL, - const char *passwd = NULL, - const char *db = NULL, - unsigned int port = 0, - const char *unix_socket = NULL, - unsigned long clientflag = 0 ); + /// @note Call init_client_errs() / finish_client_errs() before / after + MysqlClient ( const char *host = NULL, + const char *user = NULL, + const char *passwd = NULL, + const char *db = NULL, + const unsigned int port = 0, + const char *unix_socket = NULL, + const unsigned long clientflag = 0, + const int maxRetry = 5 + ); ~MysqlClient(); + /// @note clients side shall handle connect failure (reconnect) bool connect(); - bool querty(const std::string queryLine, - std::list &result); + /// @note clients side shall handle querty failure (reconnect) + /// @note call mysql_free_result( result ) after + bool querty(const char* queryMsg, + const int queryMsgLen, + MYSQL_RES **result); + + bool reconnect(); + + /// @note Slow. Use only to log during debug + /// @todo optimize this + static void queryResultToStringList(const MYSQL_RES *res_set, + std::list &result); + private: MysqlClient(const MysqlClient&); MysqlClient& operator=(const MysqlClient&); - /// @todo optimize this - void queryResultToStringList(MYSQL_RES *res_set, - std::list &result); const char *m_host; const char *m_user; @@ -67,6 +54,7 @@ private: unsigned int m_port; const char *m_unix_socket; unsigned long m_clientflag; + int m_maxRetry; bool m_connected; MYSQL *m_connection; diff --git a/other/mysqlclient_main.cpp b/other/mysqlclient_main.cpp index a3ea231..49d4445 100644 --- a/other/mysqlclient_main.cpp +++ b/other/mysqlclient_main.cpp @@ -1,5 +1,6 @@ -// g++ mysqlclient_main.cpp src/Logger.cpp src/MysqlClient.cpp src/ArgParse.cpp -I./include -lmysqlclient +// gpp mysqlclient_main.cpp ../src/Logger.cpp ../src/MysqlClient.cpp ../src/ArgParse.cpp -I../include -lmysqlclient -o mysqlclient +// ./mysqlclient -u USER -db MYSQL_DB -p PASS #include "Logger.hpp" #include "Common.hpp" @@ -15,21 +16,23 @@ void setUpArgs(ArgParse &argParse) { + TRACE_STATIC; + argParse.addArgument("--host", "Hostname/IP", ArgParse::STRING ); argParse.addArgument("-u, --user", "Username", ArgParse::STRING, - ArgParse::REQUIRED ); + ArgParse::REQUIRED, ArgParse::REQUIRED ); argParse.addArgument("-db, --database", "Database", ArgParse::STRING, - ArgParse::REQUIRED ); + ArgParse::REQUIRED, ArgParse::REQUIRED ); argParse.addArgument("-p, --password", "Password", ArgParse::STRING, - ArgParse::REQUIRED ); + ArgParse::REQUIRED, ArgParse::REQUIRED ); argParse.addArgument("-port", "Port", ArgParse::INT ); @@ -39,6 +42,11 @@ void setUpArgs(ArgParse &argParse) argParse.addArgument("-f, --client-flags", "Client flags", ArgParse::INT ); + argParse.addArgument("-r, --max-reconnect", + "Maximum number of retries if connection is lost. " + "Default is 5.", + ArgParse::INT, + ArgParse::REQUIRED ); } @@ -50,8 +58,11 @@ void getArgs( int argc, char* argv[], std::string &pass, std::string &unixsocket, int &port, - int &clientflags ) + int &clientflags, + int &retry ) { + TRACE_STATIC; + argParse.parseArgs(argc, argv); argParse.argAsString("--host", host); @@ -61,11 +72,14 @@ void getArgs( int argc, char* argv[], argParse.argAsInt("-port", port); argParse.argAsString("-s, --unix-socket", unixsocket); argParse.argAsInt("-f, --client-flags", clientflags); + argParse.argAsInt("-r, --max-reconnect", retry); } void printResults(std::list &results) { + TRACE_STATIC; + LOG ( Logger::DEBUG, std::string("Got query result number of rows: "). append(TToStr(results.size())).c_str() ); @@ -89,13 +103,13 @@ int main(int argc, char* argv[] ) setUpArgs(argParse); std::string host, user, db, pass, unixsocket; - int port, clientflags; + int port, clientflags, retry; try { getArgs( argc, argv, argParse, host, user, db, pass, unixsocket, - port, clientflags ); + port, clientflags, retry ); } catch (std::runtime_error e) { if ( argParse.foundArg("-h, --help") ) { std::cout << argParse.usage() << std::endl; @@ -121,17 +135,42 @@ int main(int argc, char* argv[] ) argParse.foundArg("-db, --database") ? db .c_str() : NULL, argParse.foundArg("-port") ? port : 0, argParse.foundArg("-s, --unix-socket") ? unixsocket.c_str() : NULL, - argParse.foundArg("-f, --client-flags") ? clientflags : 0 ); + argParse.foundArg("-f, --client-flags") ? clientflags : 0, + argParse.foundArg("-r, --max-reconnect") ? retry : 5 ); - std::list results; - if ( !mysqlClient.querty("SELECT * FROM seats", results) ) { - LOG ( Logger::ERR, "Could not execute query." ); - } else { - printResults(results); + if ( !mysqlClient.connect() && !mysqlClient.reconnect() ) { + finish_client_errs(); + Logger::destroy(); + return 0; } - finish_client_errs(); + std::string queryMsg("SELECT * FROM seats"); + MYSQL_RES *queryResult; + if ( !mysqlClient.querty(queryMsg.c_str(), queryMsg.length(), &queryResult) ) { + LOG( Logger::ERR, "Could not execute query." ); + if ( !mysqlClient.reconnect() ) { + LOG( Logger::ERR, "Reconnect failed, exiting." ); + finish_client_errs(); + Logger::destroy(); + return 0; + } + + LOG( Logger::ERR, "Trying query again." ); + if ( !mysqlClient.querty(queryMsg.c_str(), queryMsg.length(), &queryResult) ) { + LOG( Logger::ERR, "Query failed again, exiting." ); + finish_client_errs(); + Logger::destroy(); + return 0; + } + } + + std::list results; + MysqlClient::queryResultToStringList(queryResult, results); + printResults(results); + + mysql_free_result(queryResult); + finish_client_errs(); Logger::destroy(); return 0; } diff --git a/src/ArgParse.cpp b/src/ArgParse.cpp index 533c034..4e051e5 100644 --- a/src/ArgParse.cpp +++ b/src/ArgParse.cpp @@ -31,8 +31,8 @@ ArgParse::ArgParse(const std::string description, void ArgParse::addArgument(const std::string arg, const std::string help, const ValueType valueType, - const Required argRequired, const Required valueRequired, + const Required argRequired, const std::string valueName, const std::string choices) { @@ -62,8 +62,8 @@ void ArgParse::addArgument(const std::string arg, Argument argument(help, valueType, - argRequired, valueRequired, + argRequired, typeToString(valueType, valueName), choices, ""); @@ -408,15 +408,15 @@ void ArgParse::validateBool( const std::string name, ArgParse::Argument::Argument (const std::string help, const ValueType type, - const Required argRequired, const Required valueRequired, + const Required argRequired, const std::string valueName, const std::string choices, const std::string value) : m_help(help) , m_type(type) - , m_argRequired(argRequired) , m_valueRequired(valueRequired) + , m_argRequired(argRequired) , m_valueName(valueName) , m_choices(choices) , m_value(value) diff --git a/src/MysqlClient.cpp b/src/MysqlClient.cpp index ddcf291..f74a7d8 100644 --- a/src/MysqlClient.cpp +++ b/src/MysqlClient.cpp @@ -10,9 +10,11 @@ MysqlClient::MysqlClient( const char *host, const char *user, const char *passwd, const char *db, - unsigned int port, + const unsigned int port, const char *unix_socket, - unsigned long clientflag ) + const unsigned long clientflag, + const int maxRetry + ) : m_host(host) , m_user(user) , m_passwd(passwd) @@ -20,6 +22,7 @@ MysqlClient::MysqlClient( const char *host, , m_port(port) , m_unix_socket(unix_socket) , m_clientflag(clientflag) + , m_maxRetry(maxRetry) , m_connected(false) , m_connection(0) { @@ -60,10 +63,10 @@ MysqlClient::connect() return false; } - LOG ( Logger::DEBUG, - std::string("MySQL client connected to ").append(m_host ? m_host : "NULL") - .append(", on port: ").append(TToStr(m_port)) - .append(", as user: ").append(m_user ? m_user : "NULL") + LOG ( Logger::INFO, + std::string("MySQL client connected to ").append(m_host ? m_host : "localhost") + .append(", on port: ").append(m_port ? TToStr(m_port) : "TCP/IP") + .append(", as user: ").append(m_user ? m_user : "CURRENT_USER") .append(", with passwd: ").append(m_passwd ? m_passwd : "NULL") .append(", to DB: ").append(m_db ? m_db : "NULL") .append(", with unix_socket: ").append(m_unix_socket ? m_unix_socket : "NULL") @@ -76,17 +79,21 @@ MysqlClient::connect() bool -MysqlClient::querty(const std::string queryLine, - std::list &result) +MysqlClient::querty(const char* queryMsg, + const int queryMsgLen, + MYSQL_RES **result) { TRACE; - if ( !m_connected && !connect() ) + if ( !m_connected ) { + LOG( Logger::ERR, "Not connected to MySQL server." ); return false; + } - if ( mysql_query(m_connection,queryLine.c_str()) != 0 ) { + if ( mysql_real_query(m_connection, queryMsg, queryMsgLen) != 0 ) { LOG ( Logger::ERR, std::string("MySQL query failed! "). append(mysql_error(m_connection)).c_str()); + return false; } @@ -99,24 +106,48 @@ MysqlClient::querty(const std::string queryLine, return false; } - queryResultToStringList( res_set, result); - - mysql_free_result( res_set ); + *result = res_set; return true; } + +bool +MysqlClient::reconnect() +{ + TRACE; + + for (int i = 0; i < m_maxRetry; ++i ) { + + LOG( Logger::INFO, std::string("Reconnecting to MySQL server, "). + append(TToStr(i+1)). + append(" try of maximum: "). + append(TToStr(m_maxRetry)).c_str() ); + + if ( connect() ) + return true; + + sleep(1); + } + + LOG( Logger::ERR, "Maximum number of retries reached, giving up." ); + return false; +} + + void -MysqlClient::queryResultToStringList(MYSQL_RES *res_set, +MysqlClient::queryResultToStringList(const MYSQL_RES *res_set, std::list &result) { - unsigned int numrows = mysql_num_rows(res_set); - LOG( Logger::DEBUG, std::string("MySQL query returned number of rows: "). - append(TToStr(numrows)).c_str()); + TRACE_STATIC; + +// unsigned int numrows = mysql_num_rows(res_set); +// LOG( Logger::DEBUG, std::string("MySQL query returned number of rows: "). +// append(TToStr(numrows)).c_str()); - unsigned int num_fields = mysql_num_fields(res_set); + unsigned int num_fields = mysql_num_fields(const_cast(res_set)); MYSQL_ROW row; - while ((row = mysql_fetch_row(res_set)) != NULL) { + while ((row = mysql_fetch_row(const_cast(res_set))) != NULL) { std::string rowString; for( unsigned int i = 0; i < num_fields; ++i ) { rowString.append(row[i] ? row[i] : "NULL");