diff --git a/example.db3 b/example.db3 new file mode 100644 index 0000000..d5b967a Binary files /dev/null and b/example.db3 differ diff --git a/src/example1/main.cpp b/src/example1/main.cpp index e7f4c49..0bae5ce 100644 --- a/src/example1/main.cpp +++ b/src/example1/main.cpp @@ -7,13 +7,22 @@ int main (void) std::cout << "Hello SQLite.hpp\n"; try { - SQLite::Database db("sqlite.db3"); + SQLite::Database db("example.db3"); + std::cout << db.getFilename().c_str() << " onpened\n"; + + SQLite::Statement stmt(db, "SELECT * FROM test"); + std::cout << "statement created\n"; + + while (stmt.executeStep()) + { + std::cout << "executeStep\n"; + } } catch (std::exception& e) { - std::cout << e.what() << std::endl; + std::cout << "SQLite exception: " << e.what() << std::endl; } std::cout << "Bye SQLite.hpp\n"; - + return 0; } diff --git a/src/sqlite.hpp b/src/sqlite.hpp index 158ad41..12c02a4 100644 --- a/src/sqlite.hpp +++ b/src/sqlite.hpp @@ -1,30 +1,196 @@ #include #include +#include +#include namespace SQLite { +class Statement; + +/** + * @brief Management of a SQLite Database Connection. + * + * A Database object manage a list of all SQLite Statements associated with the + * underlying SQLite 3 database connection. + */ class Database { + friend class Statement; + public: - explicit Database(const char* apFilenameUt8, const bool abReadOnly = false) : - mpSQLite(NULL) + /** + * @brief Open the provided database UTF-8 filename. + * + * Exception is thrown in case of error, then the Database object is NOT constructed. + */ + explicit Database(const char* apFilename, const bool abReadOnly = true, const bool abCreate = false) : + mpSQLite(NULL), + mFilename (apFilename) { - int err = sqlite3_open_v2(apFilenameUt8, &mpSQLite, abReadOnly?SQLITE_OPEN_READONLY:SQLITE_OPEN_READWRITE, NULL); - if (SQLITE_OK != err) + int flags = abReadOnly?SQLITE_OPEN_READONLY:SQLITE_OPEN_READWRITE; + if (abCreate) + { + flags |= SQLITE_OPEN_CREATE; + } + + int ret = sqlite3_open_v2(apFilename, &mpSQLite, flags, NULL); + if (SQLITE_OK != ret) { std::string strerr = sqlite3_errmsg(mpSQLite); sqlite3_close(mpSQLite); throw std::runtime_error(strerr); } } + /** + * @brief Close the SQLite database connection. + * + * All SQLite statements must have been finalized before, + * so all Statement objects must have been unregistered. + */ virtual ~Database(void) { - sqlite3_close(mpSQLite); + // check for undestroyed statements + std::vector::iterator iStatement; + for (iStatement = mStatementList.begin(); + iStatement != mStatementList.end(); + iStatement++) + { + // TODO (*iStatement)->Finalize(); ? + std::cout << "Unregistered statement!\n"; + } + + int ret = sqlite3_close(mpSQLite); + if (SQLITE_OK != ret) + { + std::cout << sqlite3_errmsg(mpSQLite); + } + } + + /// Register a Statement object (a SQLite query) + void registerStatement (Statement& aStatement) + { + mStatementList.push_back (&aStatement); + } + /// Unregister a Statement object + void unregisterStatement (Statement& aStatement) + { + std::vector::iterator iStatement; + iStatement = std::find (mStatementList.begin(), mStatementList.end(), &aStatement); + if (mStatementList.end() != iStatement) + { + mStatementList.erase (iStatement); + } + } + + /// Filename used to open the database + inline const std::string& getFilename(void) const + { + return mFilename; } private: - sqlite3* mpSQLite; + sqlite3* mpSQLite; //!< Pointer to SQLite Database Connection Handle + std::string mFilename; //!< UTF-8 filename used to open the database + std::vector mStatementList; //!< Liste of SQL statements used with this database connexion }; + +/** + * @brief Encapsulation of a SQLite Statement. + * + * A Statement is a compiled SQL query ready to be executed step by step + * to provide results one row at a time. + */ +class Statement +{ +public: + /** + * @brief Compile and register the SQL query for the provided SQLite Database Connection + * + * Exception is thrown in case of error, then the Statement object is NOT constructed. + */ + explicit Statement(Database &aDatabase, const char* apQuery) : + mDatabase(aDatabase), + mQuery(apQuery), + mbDone(false) + { + int ret = sqlite3_prepare_v2(mDatabase.mpSQLite, mQuery.c_str(), mQuery.size(), &mpStmt, NULL); + if (SQLITE_OK != ret) + { + throw std::runtime_error(sqlite3_errmsg(mDatabase.mpSQLite)); + } + mDatabase.registerStatement(*this); + } + /** + * @brief Finalize and unregister the SQL query from the SQLite Database Connection. + */ + virtual ~Statement(void) + { + int ret = sqlite3_finalize(mpStmt); + if (SQLITE_OK != ret) + { + std::cout << sqlite3_errmsg(mDatabase.mpSQLite); + } + mDatabase.unregisterStatement(*this); + } + + /// Reset the statement to make it ready for a new execution + void reset (void) + { + mbDone = false; + int ret = sqlite3_reset(mpStmt); + if (SQLITE_OK != ret) + { + throw std::runtime_error(sqlite3_errmsg(mDatabase.mpSQLite)); + } + } + + // TODO bind + + /// Execute a step of the query to fetch one row of results + bool executeStep (void) + { + bool bOk = false; + + if (false == mbDone) + { + int ret = sqlite3_step(mpStmt); + if (SQLITE_ROW == ret) + { + bOk = true; + } + else if (SQLITE_DONE == ret) + { + bOk = true; + mbDone = true; + } + else + { + throw std::runtime_error(sqlite3_errmsg(mDatabase.mpSQLite)); + } + } + + return bOk; + } + + /// UTF-8 SQL Query + inline const std::string& getQuery(void) const + { + return mQuery; + } + /// True when the last row is fetched with executeStep() + inline bool isDone(void) const + { + return mbDone; + } + +private: + sqlite3_stmt* mpStmt; //!< Pointeur to SQLite Statement Object + Database& mDatabase; //!< Reference to the SQLite Database Connection + std::string mQuery; //!< UTF-8 SQL Query + bool mbDone; //!< True when the last row is fetched with executeStep() }; + + +}; // namespace SQLite