diff --git a/TODO.txt b/TODO.txt index 1a3c70d..a1143f7 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,6 +1,6 @@ C++11 explicit support -Adding an encapsulation to the statement ref counter +Adding mpSQLite to the statement Shared Pointer => V0.5.0 diff --git a/src/SQLiteC++/Column.cpp b/src/SQLiteC++/Column.cpp index 3a1491b..0077891 100644 --- a/src/SQLiteC++/Column.cpp +++ b/src/SQLiteC++/Column.cpp @@ -15,72 +15,40 @@ namespace SQLite { // Encapsulation of a Column in a Row of the result. -Column::Column(sqlite3* apSQLite, sqlite3_stmt* apStmt, unsigned int* apStmtRefCount, int aIndex) throw() : // nothrow - mpSQLite (apSQLite), - mpStmt (apStmt), - mpStmtRefCount (apStmtRefCount), - mIndex (aIndex) +Column::Column(Statement::Ptr aStmtPtr, int aIndex) throw() : // nothrow + mStmtPtr (aStmtPtr), + mIndex (aIndex) { - // Increment the reference counter of the sqlite3_stmt, - // telling the Statement object not to finalize the sqlite3_stmt during the lifetime of this Column objet - (*mpStmtRefCount)++; -} - -// Copy constructor -Column::Column(const Column& copy) throw() : // nothrow - mpSQLite (copy.mpSQLite), - mpStmt (copy.mpStmt), - mpStmtRefCount (copy.mpStmtRefCount), - mIndex (copy.mIndex) -{ - // Increment the reference counter of the sqlite3_stmt, - // telling the Statement object not to finalize the sqlite3_stmt during the lifetime of this Column objet - (*mpStmtRefCount)++; } // Finalize and unregister the SQL query from the SQLite Database Connection. Column::~Column(void) throw() // nothrow { - // Decrement and check the reference counter of the sqlite3_stmt - (*mpStmtRefCount)--; - if (0 == *mpStmtRefCount) - { - // When count reaches zero, finalize the sqlite3_stmt, as no Column nor Statement object use it any more - int ret = sqlite3_finalize(mpStmt); - // Never throw an exception in a destructor - //std::cout << sqlite3_errmsg(mpSQLite) << std::endl; - SQLITE_CPP_ASSERT (SQLITE_OK == ret); - - mpStmt = NULL; - - // and delete the reference counter - delete mpStmtRefCount; - } - // else, the finalization will be done by the Statement or another Column object (the last one) + // the finalization will be done by the destructor of the last shared pointer } // Return the integer value of the column specified by its index starting at 0 int Column::getInt(void) const throw() // nothrow { - return sqlite3_column_int(mpStmt, mIndex); + return sqlite3_column_int(mStmtPtr, mIndex); } // Return the 64bits integer value of the column specified by its index starting at 0 sqlite3_int64 Column::getInt64(void) const throw() // nothrow { - return sqlite3_column_int64(mpStmt, mIndex); + return sqlite3_column_int64(mStmtPtr, mIndex); } // Return the double value of the column specified by its index starting at 0 double Column::getDouble(void) const throw() // nothrow { - return sqlite3_column_double(mpStmt, mIndex); + return sqlite3_column_double(mStmtPtr, mIndex); } // Return a pointer to the text value (NULL terminated string) of the column specified by its index starting at 0 const char* Column::getText(void) const throw() // nothrow { - return (const char*)sqlite3_column_text(mpStmt, mIndex); + return (const char*)sqlite3_column_text(mStmtPtr, mIndex); } diff --git a/src/SQLiteC++/Column.h b/src/SQLiteC++/Column.h index c4c9897..8e82524 100644 --- a/src/SQLiteC++/Column.h +++ b/src/SQLiteC++/Column.h @@ -11,6 +11,7 @@ #include #include "Exception.h" +#include "Statement.h" namespace SQLite { @@ -24,15 +25,12 @@ namespace SQLite class Column { public: - /** - * @brief Compile and register the SQL query for the provided SQLite Database Connection - */ - explicit Column(sqlite3* apSQLite, sqlite3_stmt* apStmt, unsigned int* apStmtRefCount, int aIndex) throw(); // nothrow + /// Encapsulation of a Column in a Row of the result. + explicit Column(Statement::Ptr aStmtPtr, int aIndex) throw(); // nothrow /// Simple destructor virtual ~Column(void) throw(); // nothrow - /// @brief copy constructor : only way to copy a valid instance - Column(const Column& aOther) throw(); // nothrow + // default copy constructor and asignement operator are enough /// Return the integer value of the column. int getInt (void) const throw(); @@ -79,16 +77,8 @@ public: #endif private: - // Forbid default constructor and assignment operator (no implementation) - // so that there is no way of having a Column instance not initialized - Column(void); - Column& operator=(const Column&); - -private: - sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle - sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object - unsigned int* mpStmtRefCount; //!< Pointer to the heap allocated reference counter of the sqlite3_stmt (shared with the Statement object) - int mIndex; //!< Index of the column in the row of result + Statement::Ptr mStmtPtr; //!< Shared Pointer to the prepared SQLite Statement Object + int mIndex; //!< Index of the column in the row of result }; /// Standard std::ostream inserter diff --git a/src/SQLiteC++/Statement.cpp b/src/SQLiteC++/Statement.cpp index 4b85143..57641f8 100644 --- a/src/SQLiteC++/Statement.cpp +++ b/src/SQLiteC++/Statement.cpp @@ -17,42 +17,20 @@ 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), - mpStmtRefCount(NULL), mpSQLite(aDatabase.mpSQLite), // need Database friendship mQuery(apQuery), + mStmtPtr(mpSQLite, mQuery), // prepare the SQL query, and ref count mColumnCount(0), mbOk(false), mbDone(false) { - int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), mQuery.size(), &mpStmt, NULL); - check(ret); - mColumnCount = sqlite3_column_count(mpStmt); - // Initialize the reference counter of the sqlite3_stmt : used to share the mpStmt with Column objects; - // This is needed to enable Column objects to live longer than the Statement objet it refers to. - mpStmtRefCount = new unsigned int; - *mpStmtRefCount = 1; + mColumnCount = sqlite3_column_count(mStmtPtr); } // Finalize and unregister the SQL query from the SQLite Database Connection. Statement::~Statement(void) throw() // nothrow { - // Decrement and check the reference counter of the sqlite3_stmt - (*mpStmtRefCount)--; - if (0 == *mpStmtRefCount) - { - // If count reaches zero, finalize the sqlite3_stmt, as no Column objet use it anymore - int ret = sqlite3_finalize(mpStmt); - // Never throw an exception in a destructor - //std::cout << sqlite3_errmsg(mpSQLite) << std::endl; - SQLITE_CPP_ASSERT (SQLITE_OK == ret); - - mpStmt = NULL; - - // and delete the reference counter - delete mpStmtRefCount; - } - // else, the finalization will be done by the last Column object + // the finalization will be done by the destructor of the last shared pointer } // Reset the statement to make it ready for a new execution @@ -60,49 +38,49 @@ void Statement::reset(void) // throw(SQLite::Exception) { mbOk = false; mbDone = false; - int ret = sqlite3_reset(mpStmt); + int ret = sqlite3_reset(mStmtPtr); check(ret); } // Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const int& aValue) // throw(SQLite::Exception) { - int ret = sqlite3_bind_int(mpStmt, aIndex, aValue); + int ret = sqlite3_bind_int(mStmtPtr, aIndex, aValue); check(ret); } // Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const sqlite3_int64& aValue) // throw(SQLite::Exception) { - int ret = sqlite3_bind_int64(mpStmt, aIndex, aValue); + int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue); check(ret); } // Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const double& aValue) // throw(SQLite::Exception) { - int ret = sqlite3_bind_double(mpStmt, aIndex, aValue); + int ret = sqlite3_bind_double(mStmtPtr, aIndex, aValue); check(ret); } // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const std::string& aValue) // throw(SQLite::Exception) { - int ret = sqlite3_bind_text(mpStmt, aIndex, aValue.c_str(), aValue.size(), SQLITE_TRANSIENT); + int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(), aValue.size(), SQLITE_TRANSIENT); check(ret); } // Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const char* apValue) // throw(SQLite::Exception) { - int ret = sqlite3_bind_text(mpStmt, aIndex, apValue, -1, SQLITE_TRANSIENT); + int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT); check(ret); } // Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex) // throw(SQLite::Exception) { - int ret = sqlite3_bind_null(mpStmt, aIndex); + int ret = sqlite3_bind_null(mStmtPtr, aIndex); check(ret); } @@ -110,48 +88,48 @@ void Statement::bind(const int aIndex) // throw(SQLite::Exception) // Bind an int value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const int& aValue) // throw(SQLite::Exception) { - int index = sqlite3_bind_parameter_index(mpStmt, apName); - int ret = sqlite3_bind_int(mpStmt, index, aValue); + int index = sqlite3_bind_parameter_index(mStmtPtr, apName); + int ret = sqlite3_bind_int(mStmtPtr, index, aValue); check(ret); } // Bind a 64bits int value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const sqlite3_int64& aValue) // throw(SQLite::Exception) { - int index = sqlite3_bind_parameter_index(mpStmt, apName); - int ret = sqlite3_bind_int64(mpStmt, index, aValue); + int index = sqlite3_bind_parameter_index(mStmtPtr, apName); + int ret = sqlite3_bind_int64(mStmtPtr, index, aValue); check(ret); } // Bind a double (64bits float) value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const double& aValue) // throw(SQLite::Exception) { - int index = sqlite3_bind_parameter_index(mpStmt, apName); - int ret = sqlite3_bind_double(mpStmt, index, aValue); + int index = sqlite3_bind_parameter_index(mStmtPtr, apName); + int ret = sqlite3_bind_double(mStmtPtr, index, aValue); check(ret); } // Bind a string value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const std::string& aValue) // throw(SQLite::Exception) { - int index = sqlite3_bind_parameter_index(mpStmt, apName); - int ret = sqlite3_bind_text(mpStmt, index, aValue.c_str(), aValue.size(), SQLITE_TRANSIENT); + int index = sqlite3_bind_parameter_index(mStmtPtr, apName); + int ret = sqlite3_bind_text(mStmtPtr, index, aValue.c_str(), aValue.size(), SQLITE_TRANSIENT); check(ret); } // Bind a text value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const char* apValue) // throw(SQLite::Exception) { - int index = sqlite3_bind_parameter_index(mpStmt, apName); - int ret = sqlite3_bind_text(mpStmt, index, apValue, -1, SQLITE_TRANSIENT); + int index = sqlite3_bind_parameter_index(mStmtPtr, apName); + int ret = sqlite3_bind_text(mStmtPtr, index, apValue, -1, SQLITE_TRANSIENT); check(ret); } // Bind a NULL value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName) // throw(SQLite::Exception) { - int index = sqlite3_bind_parameter_index(mpStmt, apName); - int ret = sqlite3_bind_null(mpStmt, index); + int index = sqlite3_bind_parameter_index(mStmtPtr, apName); + int ret = sqlite3_bind_null(mStmtPtr, index); check(ret); } @@ -161,7 +139,7 @@ bool Statement::executeStep(void) // throw(SQLite::Exception) { if (false == mbDone) { - int ret = sqlite3_step(mpStmt); + int ret = sqlite3_step(mStmtPtr); if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it { mbOk = true; @@ -191,7 +169,7 @@ int Statement::exec(void) // throw(SQLite::Exception) { if (false == mbDone) { - int ret = sqlite3_step(mpStmt); + int ret = sqlite3_step(mStmtPtr); if (SQLITE_DONE == ret) // the statement has finished executing successfully { mbOk = false; @@ -233,7 +211,7 @@ Column Statement::getColumn(const int aIndex) const // throw(SQLite::Exception) } // Share the Statement Object handle with the new Column created - return Column(mpSQLite, mpStmt, mpStmtRefCount, aIndex); + return Column(mStmtPtr, aIndex); } // Test if the column is NULL @@ -248,10 +226,9 @@ bool Statement::isColumnNull(const int aIndex) const // throw(SQLite::Exception) throw SQLite::Exception("Column index out of range"); } - return (SQLITE_NULL == sqlite3_column_type(mpStmt, aIndex)); + return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, 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) { @@ -261,4 +238,73 @@ void Statement::check(const int aRet) const // throw(SQLite::Exception) } } + +//////////////////////////////////////////////////////////////////////////////// +// Internal class : shared pointer to the sqlite3_stmt SQLite Statement Object +//////////////////////////////////////////////////////////////////////////////// + +/** + * @brief Prepare the statement and initialize its reference counter + * + * @param[in] apSQLite The sqlite3 database connexion + * @param[in] aQuery The SQL query string to prepare + */ +Statement::Ptr::Ptr(sqlite3* apSQLite, std::string& aQuery) : + mpStmt(NULL), + mpRefCount(NULL) +{ + int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), aQuery.size(), &mpStmt, NULL); + if (SQLITE_OK != ret) + { + throw SQLite::Exception(sqlite3_errmsg(apSQLite)); + } + // Initialize the reference counter of the sqlite3_stmt : + // used to share the mStmtPtr between Statement and Column objects; + // This is needed to enable Column objects to live longer than the Statement objet it refers to. + mpRefCount = new unsigned int(1); +} + +/** + * Copy constructor increments the ref counter + * + * @param[in] aPtr Pointer to copy + */ +Statement::Ptr::Ptr (const Statement::Ptr& aPtr) : + mpStmt(aPtr.mpStmt), + mpRefCount(aPtr.mpRefCount) +{ + assert(NULL != mpRefCount); + assert(0 != *mpRefCount); + + // Increment the reference counter of the sqlite3_stmt, + // asking not to finalize the sqlite3_stmt during the lifetime of the new objet + ++(*mpRefCount); +} + +// Decrement the ref counter and finalize the sqlite3_stmt when it reaches 0 +Statement::Ptr::~Ptr(void) +{ + assert(NULL != mpRefCount); + assert(0 != *mpRefCount); + + // Decrement and check the reference counter of the sqlite3_stmt + --(*mpRefCount); + if (0 == *mpRefCount) + { + // If count reaches zero, finalize the sqlite3_stmt, + // as no Statement not Column objet use it anymore + int ret = sqlite3_finalize(mpStmt); + // Never throw an exception in a destructor + // TODO : Add mpSQLite to the pointer ? + //std::cout << sqlite3_errmsg(mpSQLite) << std::endl; + SQLITE_CPP_ASSERT (SQLITE_OK == ret); + + // and delete the reference counter + delete mpRefCount; + mpRefCount = NULL; + mpStmt = NULL; + } + // else, the finalization will be done later, by the last object +} + } // namespace SQLite diff --git a/src/SQLiteC++/Statement.h b/src/SQLiteC++/Statement.h index 7d71b66..48a8413 100644 --- a/src/SQLiteC++/Statement.h +++ b/src/SQLiteC++/Statement.h @@ -32,6 +32,8 @@ class Column; class Statement { public: + class Ptr; + /** * @brief Compile and register the SQL query for the provided SQLite Database Connection * @@ -191,6 +193,34 @@ public: return mbDone; } +public: + /** + * @brief Shared pointer to the sqlite3_stmt SQLite Statement Object + * + * This is a internal class, not part of the API (hence full documentation is in the cpp) + */ + class Statement::Ptr + { + public: + // Prepare the statement and initialize its reference counter + Ptr(sqlite3* apSQLite, std::string& aQuery); + // Copy constructor increments the ref counter + Ptr(const Ptr& aPtr); + // Decrement the ref counter and finalize the sqlite3_stmt when it reaches 0 + ~Ptr(void) throw(); // nothrow (no virtual destructor needed here) + + /// Inline cast operator returning the pointer to SQLite Statement Object + inline operator sqlite3_stmt*() const { return mpStmt; } + + private: + // Unused/forbidden copy operator + Ptr& operator=(const Ptr& aPtr); + + private: + sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object + unsigned int* mpRefCount; //!< Pointer to the heap allocated reference counter of the sqlite3_stmt (to share it with Column objects) + }; + private: // Statement must not be copyable Statement(void); @@ -203,10 +233,9 @@ private: void check(const int aRet) const; // throw(SQLite::Exception); private: - sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object - unsigned int* mpStmtRefCount; //!< Pointer to the heap allocated reference counter of the sqlite3_stmt (to share it with Column objects) sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle std::string mQuery; //!< UTF-8 SQL Query + Ptr mStmtPtr; //!< Shared Pointer to the prepared SQLite Statement Object int mColumnCount; //!< Number of column in the result of the prepared statement bool mbOk; //!< True when a row has been fetched with executeStep() bool mbDone; //!< True when the last executeStep() had no more row to fetch