diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index b4f478a..6309111 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -75,9 +75,12 @@ 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. 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 +338,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 @@ -346,6 +350,19 @@ public: */ 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 +376,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) @@ -610,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 f4f430c..5e03061 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -51,11 +51,16 @@ Statement::~Statement() // Reset the statement to make it ready for a new execution (see also #clearBindings() bellow) void Statement::reset() +{ + const int ret = tryReset(); + check(ret); +} + +int Statement::tryReset() noexcept { mbOk = false; mbDone = false; - const int ret = sqlite3_reset(mStmtPtr); - check(ret); + return sqlite3_reset(mStmtPtr); } // Clears away all the bindings of a prepared statement (can be associated with #reset() above). @@ -238,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) { @@ -255,49 +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) } -// 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) 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);