Added a reference counter to the SQLite Statement Object handle

- manages its lifespan (tracks ownerchip) across the copy to a Column object
This commit is contained in:
Sébastien Rombauts 2012-04-16 06:51:07 +02:00
parent 17124b3025
commit 9c2544ef16
2 changed files with 55 additions and 30 deletions

View File

@ -18,26 +18,33 @@ namespace SQLite
// Compile and register the SQL query for the provided SQLite Database Connection // Compile and register the SQL query for the provided SQLite Database Connection
Statement::Statement(Database &aDatabase, const char* apQuery) : // throw(SQLite::Exception) Statement::Statement(Database &aDatabase, const char* apQuery) : // throw(SQLite::Exception)
mpStmt(NULL), mpStmt(NULL),
mDatabase(aDatabase), mpStmtRefCount(NULL),
mpSQLite(aDatabase.mpSQLite),
mQuery(apQuery), mQuery(apQuery),
mbOk(false), mbOk(false),
mbDone(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); check(ret);
mColumnCount = sqlite3_column_count(mpStmt); mColumnCount = sqlite3_column_count(mpStmt);
mpStmtRefCount = new unsigned int;
*mpStmtRefCount = 1;
} }
// Finalize and unregister the SQL query from the SQLite Database Connection. // Finalize and unregister the SQL query from the SQLite Database Connection.
Statement::~Statement(void) throw() // nothrow Statement::~Statement(void) throw() // nothrow
{ {
(*mpStmtRefCount)--;
if (0 == *mpStmtRefCount)
{
int ret = sqlite3_finalize(mpStmt); int ret = sqlite3_finalize(mpStmt);
if (SQLITE_OK != ret) if (SQLITE_OK != ret)
{ {
// Never throw an exception in a destructor // Never throw an exception in a destructor
//std::cout << sqlite3_errmsg(mDatabase.mpSQLite); //std::cout << sqlite3_errmsg(mpSQLite);
} }
mpStmt = NULL; mpStmt = NULL;
}
} }
// Reset the statement to make it ready for a new execution // Reset the statement to make it ready for a new execution
@ -158,9 +165,13 @@ bool Statement::executeStep(void) // throw(SQLite::Exception)
} }
else 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; return mbOk;
} }
@ -177,7 +188,7 @@ Statement::Column Statement::getColumn(const int aIndex) const // throw(SQLite::
throw SQLite::Exception("Column index out of range"); 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 // 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)); return (SQLITE_NULL == sqlite3_column_type(mpStmt, aIndex));
} }
// Check if aRet equal SQLITE_OK, else throw a SQLite::Exception with the SQLite error message // 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) void Statement::check(const int aRet) const // throw(SQLite::Exception)
{ {
if (SQLITE_OK != aRet) 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 // 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. // 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), mpSQLite(apSQLite),
mpStmt(apStmt), mpStmt(apStmt),
mpStmtRefCount(apStmtRefCount),
mIndex(aIndex) mIndex(aIndex)
{ {
(*mpStmtRefCount)++;
} }
Statement::Column::~Column(void) throw() // nothrow 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 // 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 sqlite3_column_double(mpStmt, mIndex);
} }
// Return the text value (NULL terminated string) of the column specified by its index starting at 0 // 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 const char* Statement::Column::getText(void) const throw() // nothrow
{ {
return (const char*)sqlite3_column_text(mpStmt, mIndex); return (const char*)sqlite3_column_text(mpStmt, mIndex);
} }

View File

@ -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, * @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 * 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 * @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 class Column
{ {
/// Standard std::ostream inserter
friend std::ostream& operator<<(std::ostream &stream, const Column& column);
public: public:
/** /**
* @brief Compile and register the SQL query for the provided SQLite Database Connection * @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 /// Basic destructor
virtual ~Column(void) throw(); // nothrow virtual ~Column(void) throw(); // nothrow
/// Return the integer value of the column. /// Return the integer value of the column.
int getInt (void) const throw(); 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(); 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(); 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(); const char* getText (void) const throw();
/// Inline cast operator to int /// Inline cast operator to int
@ -231,6 +230,7 @@ public:
private: private:
sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle
sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object 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 int mIndex; //!< Index of the column in the row of result
}; };
@ -247,7 +247,8 @@ private:
private: private:
sqlite3_stmt* mpStmt; //!< Pointer to SQLite Statement Object 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 std::string mQuery; //!< UTF-8 SQL Query
int mColumnCount; //!< Number of column in the result of the prepared statement int mColumnCount; //!< Number of column in the result of the prepared statement
bool mbOk; //!< True when a row has been fetched with executeStep() 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 }; // namespace SQLite