////////////////////////////////////////////////////////////////////// // 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" #include namespace DBA { ////////////////////////////////////////////////////////////////////// // Constructor for the Connection baseclase. Connection::Connection() : m_executed(false) , m_error(0) , m_affectedRows(0) , m_lastInsertID(0) , m_rows(0) , m_currentRow(0) , m_cols(0) , m_currentColumn(0) , m_currentResult(NULL) { } ////////////////////////////////////////////////////////////////////// // Destructor for the Connection baseclase. Make sure any current // query is destructed. Connection::~Connection() { if ( m_currentResult != NULL ) m_currentResult->Release() ; } ////////////////////////////////////////////////////////////////////// // Execute an SQL statement void Connection::Execute(size_t stlen, const char* statement) { // Make sure any prior result goes away. ReleaseResult() ; m_executed = false ; m_error = 0 ; m_affectedRows = 0 ; m_lastInsertID = 0 ; m_rows = 0 ; m_currentRow = 0 ; m_cols = 0 ; m_currentColumn = 0 ; CheckConnected() ; if ( statement == NULL ) throw std::invalid_argument("NULL query statement") ; if ( stlen == 0 ) throw std::invalid_argument("0 length statement") ; // Find the first non-space characters in the query while ( stlen > 0 && *statement && isspace(*statement) ) { ++statement ; --stlen ; } // Validate the statement length argument. if ( (stlen == 0 && statement[0] != 0) #ifdef DEBUG || strlen(statement) != stlen #endif ) { throw std::invalid_argument("stlen argument does not match statement length") ; } if ( statement[0] == 0 ) throw std::invalid_argument("Empty statement") ; _execute(stlen, statement) ; _processResultSet() ; } ////////////////////////////////////////////////////////////////////// // Clean up the last set of results. void Connection::ReleaseResult() { _releaseResultSet() ; m_executed = false ; m_error = 0 ; m_rows = m_affectedRows = m_currentRow = 0 ; m_cols = m_currentColumn = 0 ; m_lastInsertID = 0 ; } ////////////////////////////////////////////////////////////////////// // Constructor for the ResultSet interface class. Attaches us to the // parent connection and ensures only one ResultSet is in use per // connection at a time. ResultSet::ResultSet(Connection& conn, size_t stlen, const char* statement) : m_conn(NULL) { if ( conn.m_currentResult != NULL ) throw std::logic_error("Cannot have multiple concurrent result sets active per connection.") ; m_conn = &conn ; m_conn->ReleaseResult() ; m_conn->m_currentResult = this ; m_conn->Execute(stlen, statement) ; } ////////////////////////////////////////////////////////////////////// // Varadic, snprintf style constructor. maxStatementLen is the maximum // length of the resulting statement you wish to allow. The inclusion of // this parameter helps (a) efficiency, (b) disambiguate the resulting // function fingerprint from that of the future ResultSet(conn, statement) ; ResultSet::ResultSet(Connection& conn, const char* format, size_t maxStatementLen, ...) { // Input validation. if ( format == NULL ) throw std::invalid_argument("format specifier must not be NULL") ; if ( maxStatementLen == 0 ) throw std::invalid_argument("maxStatementLen must be non-zero") ; #ifndef NDEBUG size_t formatLen = strlen(format) ; if ( maxStatementLen < formatLen ) throw std::invalid_argument("maxStatementLen must be >= length of the format mask") ; #endif // G++ allows dynamic array sizes on the stack. #ifdef __GNUC__ char statement[maxStatementLen + 1] ; #else char* statement = new char[maxStatementLen + 1] ; #endif va_list args ; va_start(args, maxStatementLen) ; int stLen = vsnprintf(statement, maxStatementLen, format, args) ; va_end(args) ; // Ensure we add the trailing 0. statement[stLen] = 0 ; ResultSet::ResultSet(conn, stLen, statement) ; #ifndef __GNUC__ delete [] statement ; #endif } ////////////////////////////////////////////////////////////////////// // Destructor for the ResultSet interface class: Ensures we detach // from the parent connection and release any resources used by the // query we are associated with. ResultSet::~ResultSet() { Release() ; if ( m_conn ) { if ( m_conn->m_currentResult == this ) { m_conn->ReleaseResult() ; m_conn->m_currentResult = NULL ; } } } ////////////////////////////////////////////////////////////////////// // Re-use a result set for a second query. void ResultSet::Do(size_t stlen, const char* statement) { if ( m_conn == NULL ) throw std::logic_error("Result set is not associated with any connection") ; if ( m_conn->m_currentResult != this ) throw std::logic_error("Database connection is no-longer associated with this result set") ; m_conn->Execute(stlen, statement) ; } void ResultSet::Do(const char* format, size_t maxStatementLen, ...) { // G++ allows dynamic array sizes on the stack. #ifdef __GNUC__ char statement[maxStatementLen + 1] ; #else char* statement = new char[maxStatementLen + 1] ; #endif va_list args ; va_start(args, maxStatementLen) ; int stLen = vsnprintf(statement, maxStatementLen, format, args) ; va_end(args) ; // Ensure we add the trailing 0. statement[stLen] = 0 ; m_conn->Execute(stLen, statement) ; #ifndef __GNUC__ delete [] statement ; #endif } ////////////////////////////////////////////////////////////////////// // Release resources used by a result set and detach it from the // connection. void ResultSet::Release() { if ( m_conn ) { if ( m_conn->m_currentResult == this ) { m_conn->ReleaseResult() ; m_conn->m_currentResult = NULL ; } } } }