diff --git a/src/SQLiteC++/Statement.cpp b/src/SQLiteC++/Statement.cpp index 9e6be6f..4f18eec 100644 --- a/src/SQLiteC++/Statement.cpp +++ b/src/SQLiteC++/Statement.cpp @@ -18,26 +18,33 @@ namespace SQLite // Compile and register the SQL query for the provided SQLite Database Connection Statement::Statement(Database &aDatabase, const char* apQuery) : // throw(SQLite::Exception) mpStmt(NULL), - mDatabase(aDatabase), + mpStmtRefCount(NULL), + mpSQLite(aDatabase.mpSQLite), mQuery(apQuery), mbOk(false), mbDone(false) { - int ret = sqlite3_prepare_v2(mDatabase.mpSQLite, mQuery.c_str(), mQuery.size(), &mpStmt, NULL); + int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), mQuery.size(), &mpStmt, NULL); check(ret); mColumnCount = sqlite3_column_count(mpStmt); + mpStmtRefCount = new unsigned int; + *mpStmtRefCount = 1; } // Finalize and unregister the SQL query from the SQLite Database Connection. Statement::~Statement(void) throw() // nothrow { - int ret = sqlite3_finalize(mpStmt); - if (SQLITE_OK != ret) + (*mpStmtRefCount)--; + if (0 == *mpStmtRefCount) { - // Never throw an exception in a destructor - //std::cout << sqlite3_errmsg(mDatabase.mpSQLite); + int ret = sqlite3_finalize(mpStmt); + if (SQLITE_OK != ret) + { + // Never throw an exception in a destructor + //std::cout << sqlite3_errmsg(mpSQLite); + } + mpStmt = NULL; } - mpStmt = NULL; } // Reset the statement to make it ready for a new execution @@ -158,9 +165,13 @@ bool Statement::executeStep(void) // throw(SQLite::Exception) } else { - throw SQLite::Exception(sqlite3_errmsg(mDatabase.mpSQLite)); + throw SQLite::Exception(sqlite3_errmsg(mpSQLite)); } } + else + { + throw SQLite::Exception("Statement need to be reseted"); + } return mbOk; } @@ -177,7 +188,7 @@ Statement::Column Statement::getColumn(const int aIndex) const // throw(SQLite:: throw SQLite::Exception("Column index out of range"); } - return Column(mDatabase.mpSQLite, mpStmt, aIndex); + return Column(mpSQLite, mpStmt, mpStmtRefCount, aIndex); } // Test if the column is NULL @@ -195,12 +206,13 @@ bool Statement::isColumnNull(const int aIndex) const // throw(SQLite::Exception) return (SQLITE_NULL == sqlite3_column_type(mpStmt, aIndex)); } + // Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message void Statement::check(const int aRet) const // throw(SQLite::Exception) { if (SQLITE_OK != aRet) { - throw SQLite::Exception(sqlite3_errmsg(mDatabase.mpSQLite)); + throw SQLite::Exception(sqlite3_errmsg(mpSQLite)); } } @@ -208,21 +220,29 @@ void Statement::check(const int aRet) const // throw(SQLite::Exception) //////////////////////////////////////////////////////////////////////////////// // Implementation of the inner class Statement::Column // -// Warning : you should never try to use directly a Column object, nor copying it; -// is is only an Adapter class designed to convert the result value -// of Statement::getColumn() to the data type you want -// // Encapsulation of a Column in a Row of the result. -Statement::Column::Column(sqlite3* apSQLite, sqlite3_stmt* apStmt, int aIndex) throw() : // nothrow +Statement::Column::Column(sqlite3* apSQLite, sqlite3_stmt* apStmt, unsigned int* apStmtRefCount, int aIndex) throw() : // nothrow mpSQLite(apSQLite), mpStmt(apStmt), + mpStmtRefCount(apStmtRefCount), mIndex(aIndex) { + (*mpStmtRefCount)++; } Statement::Column::~Column(void) throw() // nothrow { + (*mpStmtRefCount)--; + if (0 == *mpStmtRefCount) + { + int ret = sqlite3_finalize(mpStmt); + if (SQLITE_OK != ret) + { + throw SQLite::Exception(sqlite3_errmsg(mpSQLite)); + } + mpStmt = NULL; + } } // Return the integer value of the column specified by its index starting at 0 @@ -243,8 +263,8 @@ double Statement::Column::getDouble(void) const throw() // nothrow return sqlite3_column_double(mpStmt, mIndex); } -// Return the text value (NULL terminated string) of the column specified by its index starting at 0 -const char * Statement::Column::getText(void) const throw() // nothrow +// Return a pointer to the text value (NULL terminated string) of the column specified by its index starting at 0 +const char* Statement::Column::getText(void) const throw() // nothrow { return (const char*)sqlite3_column_text(mpStmt, mIndex); } diff --git a/src/SQLiteC++/Statement.h b/src/SQLiteC++/Statement.h index ccdc0e3..f5db1a2 100644 --- a/src/SQLiteC++/Statement.h +++ b/src/SQLiteC++/Statement.h @@ -125,12 +125,12 @@ public: * @warning The resulting Column object must not be copied or memorized as it is only valid for a short time, * only while the row from the Statement remains valid, that is only until next executeStep */ - Column getColumn (const int aIndex) const; // throw(SQLite::Exception); + Column getColumn(const int aIndex) const; // throw(SQLite::Exception); /** * @brief Test if the column is NULL */ - bool isColumnNull (const int aIndex) const; // throw(SQLite::Exception); + bool isColumnNull(const int aIndex) const; // throw(SQLite::Exception); //////////////////////////////////////////////////////////////////////////// @@ -177,24 +177,23 @@ public: */ class Column { - /// Standard std::ostream inserter - friend std::ostream& operator<<(std::ostream &stream, const Column& column); - public: /** * @brief Compile and register the SQL query for the provided SQLite Database Connection */ - explicit Column(sqlite3* apSQLite, sqlite3_stmt* apStmt, int aIndex) throw(); // nothrow + explicit Column(sqlite3* apSQLite, sqlite3_stmt* apStmt, unsigned int* apStmtRefCount, int aIndex) throw(); // nothrow /// Basic destructor virtual ~Column(void) throw(); // nothrow /// Return the integer value of the column. int getInt (void) const throw(); - // Return the 64bits integer value of the column. + /// Return the 64bits integer value of the column. sqlite3_int64 getInt64 (void) const throw(); - // Return the double (64bits float) value of the column. + /// Return the double (64bits float) value of the column. double getDouble(void) const throw(); - // Return the text value (NULL terminated string) of the column. + /// Return a pointer to the text value (NULL terminated string) of the column. + /// Warning, the value pointed at is only valid while the statement is valid (ie. not finalized), + /// thus you must copy it before using it beyond its scope (to a std::string for instance). const char* getText (void) const throw(); /// Inline cast operator to int @@ -229,9 +228,10 @@ public: Column& operator=(const Column&); private: - sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle - sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object - int mIndex; //!< Index of the column in the row of result + sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle + sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object + unsigned int* mpStmtRefCount; //!< Pointer to the reference counter of the (shared) Statement Object + int mIndex; //!< Index of the column in the row of result }; private: @@ -247,7 +247,8 @@ private: private: sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object - Database& mDatabase; //!< Reference to the SQLite Database Connection + unsigned int* mpStmtRefCount; //!< Pointer to the reference counter of the (shared) Statement Object + sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle std::string mQuery; //!< UTF-8 SQL Query int mColumnCount; //!< Number of column in the result of the prepared statement bool mbOk; //!< True when a row has been fetched with executeStep() @@ -255,4 +256,8 @@ private: }; +/// Standard std::ostream inserter +std::ostream& operator<<(std::ostream &stream, const Statement::Column& column); + + }; // namespace SQLite