From 8191046ea5e36f9471f44cc27defab04ae5de727 Mon Sep 17 00:00:00 2001 From: Henrik Jakobsson Majava Date: Mon, 28 Aug 2017 11:00:17 +0200 Subject: [PATCH 1/4] Added tryExecuteStep and tryReset --- include/SQLiteCpp/Statement.h | 19 +++++++++++++++++++ src/Statement.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index b4f478a..13a122e 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -77,6 +77,10 @@ public: /// Reset the statement to make it ready for a new execution. void reset(); + + /// Reset the statement to make it ready for a new execution. Returns the sqlite result code + /// instead of throwing an exception on error. + int tryReset() noexcept; /** * @brief Clears away all the bindings of a prepared statement. @@ -335,6 +339,7 @@ public: * thru the getColumn() method * * @see exec() execute a one-step prepared statement with no expected result + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. * @see Database::exec() is a shortcut to execute one or multiple statements without results * * @return - true (SQLITE_ROW) if there is another row ready : you can call getColumn(N) to get it @@ -345,6 +350,19 @@ public: * @throw SQLite::Exception in case of error */ bool executeStep(); + + /** + * @brief Try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. + * + * + * + * @see exec() execute a one-step prepared statement with no expected result + * @see executeStep() execute a step of the prepared query to fetch one row of results + * @see Database::exec() is a shortcut to execute one or multiple statements without results + * + * @return the sqlite result code. + */ + int tryExecuteStep() noexcept; /** * @brief Execute a one-step query with no expected result. @@ -359,6 +377,7 @@ public: * - reusing it allows for better performances (efficient for multiple insertion). * * @see executeStep() execute a step of the prepared query to fetch one row of results + * @see tryExecuteStep() try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. * @see Database::exec() is a shortcut to execute one or multiple statements without results * * @return number of row modified by this SQL statement (INSERT, UPDATE or DELETE) diff --git a/src/Statement.cpp b/src/Statement.cpp index f4f430c..11068a6 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -58,6 +58,14 @@ void Statement::reset() check(ret); } +int Statement::tryReset() noexcept +{ + mbOk = false; + mbDone = false; + const int ret = sqlite3_reset(mStmtPtr); + return ret; +} + // Clears away all the bindings of a prepared statement (can be associated with #reset() above). void Statement::clearBindings() { @@ -266,6 +274,26 @@ bool Statement::executeStep() return mbOk; // true only if one row is accessible by getColumn(N) } +int Statement::tryExecuteStep() noexcept { + const int ret = sqlite3_step(mStmtPtr); + if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it + { + mbOk = true; + } + else if (SQLITE_DONE == ret) // no (more) row ready : the query has finished executing + { + mbOk = false; + mbDone = true; + } + else + { + mbOk = false; + mbDone = false; + } + + return ret; +} + // Execute a one-step query with no expected result int Statement::exec() { From f4947e7a03bbc9fc9ba081f236d079d6a064a12e Mon Sep 17 00:00:00 2001 From: Henrik Jakobsson Majava Date: Mon, 28 Aug 2017 15:10:56 +0200 Subject: [PATCH 2/4] Added tests for tryExecuteStep and tryReset --- tests/Statement_test.cpp | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/Statement_test.cpp b/tests/Statement_test.cpp index 8bd626c..e8e0870 100644 --- a/tests/Statement_test.cpp +++ b/tests/Statement_test.cpp @@ -147,6 +147,51 @@ TEST(Statement, executeStep) { EXPECT_THROW(insert2.exec(), SQLite::Exception); } +TEST(Statement, tryExecuteStep) { + // Create a new database + SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); + EXPECT_EQ(SQLite::OK, db.getErrorCode()); + + // Create a new table + EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT, int INTEGER, double REAL)")); + EXPECT_EQ(SQLite::OK, db.getErrorCode()); + + // Create a first row + EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)")); + EXPECT_EQ(1, db.getLastInsertRowid()); + + // Compile a SQL query + SQLite::Statement query(db, "SELECT * FROM test"); + EXPECT_STREQ("SELECT * FROM test", query.getQuery().c_str()); + EXPECT_EQ(4, query.getColumnCount()); + + // Get the first row + EXPECT_EQ(query.tryExecuteStep(), SQLITE_ROW); + EXPECT_TRUE (query.isOk()); + EXPECT_FALSE(query.isDone()); + const int64_t id = query.getColumn(0); + const std::string msg = query.getColumn(1); + const int integer = query.getColumn(2); + const long integer2= query.getColumn(2); + const double real = query.getColumn(3); + EXPECT_EQ(1, id); + EXPECT_EQ("first", msg); + EXPECT_EQ(123, integer); + EXPECT_EQ(123, integer2); + EXPECT_EQ(0.123, real); + + // Step one more time to discover there is nothing more + EXPECT_EQ(query.tryExecuteStep(), SQLITE_DONE); + EXPECT_FALSE(query.isOk()); + EXPECT_TRUE (query.isDone()); // "done" is "the end" + + // Try to insert a new row with the same PRIMARY KEY: "UNIQUE constraint failed: test.id" + SQLite::Statement insert(db, "INSERT INTO test VALUES (1, \"impossible\", 456, 0.456)"); + EXPECT_EQ(insert.tryExecuteStep(), SQLITE_CONSTRAINT); + // in this case, reset() do throw again the same error + EXPECT_EQ(insert.tryReset(), SQLITE_CONSTRAINT); +} + TEST(Statement, bindings) { // Create a new database SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); From 94c7897d1b93344c7d3874fb6c109b6d67816430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Rombauts?= Date: Mon, 28 Aug 2017 16:00:50 +0200 Subject: [PATCH 3/4] Cleanup on PR #142 : remove whitespaces and mutualize some code --- include/SQLiteCpp/Statement.h | 9 ++++----- src/Statement.cpp | 12 +++++------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 13a122e..31f60cb 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -75,11 +75,10 @@ public: /// Finalize and unregister the SQL query from the SQLite Database Connection. ~Statement(); - /// Reset the statement to make it ready for a new execution. + /// Reset the statement to make it ready for a new execution. Throws an exception on error. void reset(); - - /// Reset the statement to make it ready for a new execution. Returns the sqlite result code - /// instead of throwing an exception on error. + + /// Reset the statement. Returns the sqlite result code instead of throwing an exception on error. int tryReset() noexcept; /** @@ -350,7 +349,7 @@ public: * @throw SQLite::Exception in case of error */ bool executeStep(); - + /** * @brief Try to execute a step of the prepared query to fetch one row of results, returning the sqlite result code. * diff --git a/src/Statement.cpp b/src/Statement.cpp index 11068a6..4b07ae1 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -52,9 +52,7 @@ Statement::~Statement() // Reset the statement to make it ready for a new execution (see also #clearBindings() bellow) void Statement::reset() { - mbOk = false; - mbDone = false; - const int ret = sqlite3_reset(mStmtPtr); + const int ret = tryReset(); check(ret); } @@ -62,8 +60,7 @@ int Statement::tryReset() noexcept { mbOk = false; mbDone = false; - const int ret = sqlite3_reset(mStmtPtr); - return ret; + return sqlite3_reset(mStmtPtr); } // Clears away all the bindings of a prepared statement (can be associated with #reset() above). @@ -274,7 +271,8 @@ bool Statement::executeStep() return mbOk; // true only if one row is accessible by getColumn(N) } -int Statement::tryExecuteStep() noexcept { +int Statement::tryExecuteStep() noexcept +{ const int ret = sqlite3_step(mStmtPtr); if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it { @@ -290,7 +288,7 @@ int Statement::tryExecuteStep() noexcept { mbOk = false; mbDone = false; } - + return ret; } From c14d884ba5b35179ec66e4603780f0c783a4c99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Rombauts?= Date: Mon, 28 Aug 2017 16:04:35 +0200 Subject: [PATCH 4/4] Mutualize code into tryExecuteStep() from PR #142 using SQLITE_MISUSE when statement needs to be reseted --- include/SQLiteCpp/Statement.h | 2 +- src/Statement.cpp | 92 +++++++++++++---------------------- 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 31f60cb..6309111 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -628,7 +628,7 @@ private: } /** - * @brief Check if there is a row of result returnes by executeStep(), else throw a SQLite::Exception. + * @brief Check if there is a row of result returned by executeStep(), else throw a SQLite::Exception. */ inline void checkRow() const { diff --git a/src/Statement.cpp b/src/Statement.cpp index 4b07ae1..5e03061 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -243,6 +243,37 @@ void Statement::bind(const char* apName) // Execute a step of the query to fetch one row of results bool Statement::executeStep() +{ + const int ret = tryExecuteStep(); + if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem + { + throw SQLite::Exception(mStmtPtr, ret); + } + + return mbOk; // true only if one row is accessible by getColumn(N) +} + +// Execute a one-step query with no expected result +int Statement::exec() +{ + const int ret = tryExecuteStep(); + if (SQLITE_DONE != ret) // the statement has finished executing successfully + { + if (SQLITE_ROW == ret) + { + throw SQLite::Exception("exec() does not expect results. Use executeStep."); + } + else + { + throw SQLite::Exception(mStmtPtr, ret); + } + } + + // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE) + return sqlite3_changes(mStmtPtr); +} + +int Statement::tryExecuteStep() noexcept { if (false == mbDone) { @@ -260,70 +291,17 @@ bool Statement::executeStep() { mbOk = false; mbDone = false; - throw SQLite::Exception(mStmtPtr, ret); } + + return ret; } else { - throw SQLite::Exception("Statement needs to be reseted."); + // Statement needs to be reseted ! + return SQLITE_MISUSE; } - - return mbOk; // true only if one row is accessible by getColumn(N) } -int Statement::tryExecuteStep() noexcept -{ - const int ret = sqlite3_step(mStmtPtr); - if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it - { - mbOk = true; - } - else if (SQLITE_DONE == ret) // no (more) row ready : the query has finished executing - { - mbOk = false; - mbDone = true; - } - else - { - mbOk = false; - mbDone = false; - } - - return ret; -} - -// Execute a one-step query with no expected result -int Statement::exec() -{ - if (false == mbDone) - { - const int ret = sqlite3_step(mStmtPtr); - if (SQLITE_DONE == ret) // the statement has finished executing successfully - { - mbOk = false; - mbDone = true; - } - else if (SQLITE_ROW == ret) - { - mbOk = false; - mbDone = false; - throw SQLite::Exception("exec() does not expect results. Use executeStep."); - } - else - { - mbOk = false; - mbDone = false; - throw SQLite::Exception(mStmtPtr, ret); - } - } - else - { - throw SQLite::Exception("Statement need to be reseted."); - } - - // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE) - return sqlite3_changes(mStmtPtr); -} // Return a copy of the column data specified by its index starting at 0 // (use the Column copy-constructor)