Merge branch 'try_execute'

This commit is contained in:
Sébastien Rombauts 2017-08-28 17:17:04 +02:00
commit 473a307b6d
3 changed files with 107 additions and 40 deletions

View File

@ -75,9 +75,12 @@ public:
/// Finalize and unregister the SQL query from the SQLite Database Connection. /// Finalize and unregister the SQL query from the SQLite Database Connection.
~Statement(); ~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(); 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. * @brief Clears away all the bindings of a prepared statement.
* *
@ -335,6 +338,7 @@ public:
* thru the getColumn() method * thru the getColumn() method
* *
* @see exec() execute a one-step prepared statement with no expected result * @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 * @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 * @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(); 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. * @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). * - 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 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 * @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) * @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 inline void checkRow() const
{ {

View File

@ -51,11 +51,16 @@ Statement::~Statement()
// Reset the statement to make it ready for a new execution (see also #clearBindings() bellow) // Reset the statement to make it ready for a new execution (see also #clearBindings() bellow)
void Statement::reset() void Statement::reset()
{
const int ret = tryReset();
check(ret);
}
int Statement::tryReset() noexcept
{ {
mbOk = false; mbOk = false;
mbDone = false; mbDone = false;
const int ret = sqlite3_reset(mStmtPtr); return sqlite3_reset(mStmtPtr);
check(ret);
} }
// Clears away all the bindings of a prepared statement (can be associated with #reset() above). // 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 // Execute a step of the query to fetch one row of results
bool Statement::executeStep() 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) if (false == mbDone)
{ {
@ -255,49 +291,17 @@ bool Statement::executeStep()
{ {
mbOk = false; mbOk = false;
mbDone = false; mbDone = false;
throw SQLite::Exception(mStmtPtr, ret);
} }
return ret;
} }
else 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 // Return a copy of the column data specified by its index starting at 0
// (use the Column copy-constructor) // (use the Column copy-constructor)

View File

@ -147,6 +147,51 @@ TEST(Statement, executeStep) {
EXPECT_THROW(insert2.exec(), SQLite::Exception); 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) { TEST(Statement, bindings) {
// Create a new database // Create a new database
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE); SQLite::Database db(":memory:", SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE);