#ifdef DBA_ENABLE_MYSQL ////////////////////////////////////////////////////////////////////// // Copyright (c) 2010, Oliver 'kfs1' Smith // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // - Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // - Neither the name of KingFisher Software nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ////////////////////////////////////////////////////////////////////// // #include "dbaConn.h" // Main include file #include // Include the MySQL error codes. #include // For stringstream. namespace DBA { ////////////////////////////////////////////////////////////////////// // Constructor for a MySQLConnection - not a lot here :) MySQLConnection::MySQLConnection() : Connection() , m_conn(NULL) , m_result(NULL) , m_columns(NULL) { } ////////////////////////////////////////////////////////////////////// // Destructor - ensure cleanup. MySQLConnection::~MySQLConnection() { m_columns = NULL ; if ( m_currentResult != NULL ) ReleaseResult() ; if ( m_conn != NULL ) Disconnect() ; } ////////////////////////////////////////////////////////////////////// // Connect to a MySQL database. void MySQLConnection::Connect(const Credentials& credentials, unsigned int flags, bool autoReconnect /* = false */) { // Initialize MySQL MYSQL* mysql = mysql_init(NULL) ; if ( mysql == NULL ) throw std::runtime_error("mysql_init failed") ; ///Terminal // Attempt to connect. m_conn = mysql_real_connect(mysql , credentials.host, credentials.username, credentials.password, credentials.database, credentials.port , NULL , flags ) ; if ( m_conn == NULL ) { std::stringstream error ; error << "Unable to connect to database " << credentials.username<<"@"<str()) ; } // Set the auto-reconnect flag on versions of MySQL that have it. # if MYSQL_VERSION_ID >= 50003 my_bool reconnect = (autoReconnect ? 1 : 0) ; mysql_options(m_conn, MYSQL_OPT_RECONNECT, &reconnect) ; # endif // Force utf8 as the connection characterset. if ( mysql_set_character_set(m_conn, "utf8") != 0 ) { std::stringstream error ; error << "Unable to set MySQL connection character set to UTF8. Error #" << mysql_errno(m_conn) << ": " << mysql_error(m_conn) ; mysql_close(m_conn) ; m_conn = NULL ; throw std::logic_error(error.rdbuf()->str()) ; } } ////////////////////////////////////////////////////////////////////// // Disconnect from database void MySQLConnection::Disconnect() { if ( m_currentResult != NULL ) m_currentResult->Release() ; if ( m_conn == NULL ) return ; mysql_close(m_conn) ; m_conn = NULL ; } ////////////////////////////////////////////////////////////////////// // Clean up result sets void MySQLConnection::_releaseResultSet() { // Clean up the MySQL result if ( m_result ) { mysql_free_result(m_result) ; m_result = NULL ; } // Drain any remaining result sets (e.g. from a CALL query) if ( m_conn != NULL ) { while ( mysql_next_result(m_conn) == 0 ) { m_result = mysql_store_result(m_conn) ; if ( m_result != NULL ) { mysql_free_result(m_result) ; m_result = NULL ; } } } } ////////////////////////////////////////////////////////////////////// // Escape a string for any illegal SQL characters. void MySQLConnection::EscapeString(const char* src, char* dest, size_t destSize) { size_t srcLen = strlen(src) ; const size_t destLen = destSize - 1 ; if ( srcLen > destLen / 2 ) srcLen = destLen / 2 ; if ( m_conn ) mysql_real_escape_string(m_conn, dest, src, srcLen) ; else mysql_escape_string(dest, src, srcLen) ; } ////////////////////////////////////////////////////////////////////// // Execute SQL statement void MySQLConnection::_execute(size_t stlen, const char* statement) { unsigned int queryResult = mysql_real_query(m_conn, statement, stlen) ; // Unless we lost the connection, we did actual work. if ( queryResult != CR_SERVER_GONE_ERROR && queryResult != CR_SERVER_LOST ) m_executed = true ; if ( queryResult != 0 ) { // Failed m_error = mysql_errno(m_conn) ; ThrowError("Statement Error: ") ; } } ////////////////////////////////////////////////////////////////////// // Retrieve results from execution void MySQLConnection::_processResultSet() { m_result = mysql_store_result(m_conn) ; if ( m_result == NULL ) { // Should there have been a result? if (mysql_field_count(m_conn) != 0 ) { m_error = mysql_errno(m_conn) ; if ( m_error != 0 ) { ThrowError("MySQL error retrieving results from query: ") ; } else throw std::logic_error("Failed to retrieve result set") ; } m_rows = m_cols = 0 ; } else { m_rows = (size_t)mysql_num_rows(m_result) ; m_cols = (size_t)mysql_num_fields(m_result) ; } m_affectedRows = (size_t)mysql_affected_rows(m_conn) ; m_lastInsertID = (size_t)mysql_insert_id(m_conn) ; } ////////////////////////////////////////////////////////////////////// // Fetch next row, or return false. bool MySQLConnection::FetchRow() { m_currentColumn = 0 ; // Conditions under which we shouldn't be getting called if ( !m_executed ) throw std::logic_error("No query executed to fetch rows from") ; if ( m_error ) throw std::logic_error("Can't fetch rows, query failed") ; if ( m_result == NULL ) throw std::logic_error("No result set to query") ; // Have we reached the end of the data, or is the result // empty columns? if ( HasRows() == false || m_cols == 0 ) { return false ; } m_columns = mysql_fetch_row(m_result) ; if ( m_columns == NULL ) { m_error = mysql_errno(m_conn) ; ThrowError("Error retrieving row: ") ; } ++m_currentRow ; return true ; } ////////////////////////////////////////////////////////////////////// // Some queries (e.g. CALL) can return multiple result sets, it's // also possible to generate multiple result sets by issuing multiple // statements in one command (e.g. "SELECT 1; SELECT 2"). bool MySQLConnection::NextResultSet() { ReleaseResult() ; m_error = mysql_next_result(m_conn) ; if ( m_error > 0 ) ThrowError("Error retrieving next result set") ; if ( m_error < 0 ) return false ; m_executed = true ; _processResultSet() ; return true ; } } ; // namespace DBA #endif // DBA_ENABLE_MYSQL