mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 11:57:28 -03:00
sqlite: add ability to interrupt statements
By encapsulating sqlite3_exec into its own standalone method and introducing the 'SQliteExecHandler' class, we enable the ability to test db statements execution failures within the unit test framework. This is used in the following-up commit to exercise a deadlock and improve our wallet db error handling code. Moreover, the future encapsulation of other sqlite functions within this class will contribute to minimize the impact of any future API changes.
This commit is contained in:
parent
fdf9f66909
commit
dca874e838
2 changed files with 20 additions and 3 deletions
|
@ -377,6 +377,11 @@ void SQLiteDatabase::Close()
|
|||
m_db = nullptr;
|
||||
}
|
||||
|
||||
int SQliteExecHandler::Exec(SQLiteDatabase& database, const std::string& statement)
|
||||
{
|
||||
return sqlite3_exec(database.m_db, statement.data(), nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(bool flush_on_close)
|
||||
{
|
||||
// We ignore flush_on_close because we don't do manual flushing for SQLite
|
||||
|
@ -607,7 +612,7 @@ std::unique_ptr<DatabaseCursor> SQLiteBatch::GetNewPrefixCursor(Span<const std::
|
|||
bool SQLiteBatch::TxnBegin()
|
||||
{
|
||||
if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) == 0) return false;
|
||||
int res = sqlite3_exec(m_database.m_db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr);
|
||||
int res = Assert(m_exec_handler)->Exec(m_database, "BEGIN TRANSACTION");
|
||||
if (res != SQLITE_OK) {
|
||||
LogPrintf("SQLiteBatch: Failed to begin the transaction\n");
|
||||
}
|
||||
|
@ -617,7 +622,7 @@ bool SQLiteBatch::TxnBegin()
|
|||
bool SQLiteBatch::TxnCommit()
|
||||
{
|
||||
if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) != 0) return false;
|
||||
int res = sqlite3_exec(m_database.m_db, "COMMIT TRANSACTION", nullptr, nullptr, nullptr);
|
||||
int res = Assert(m_exec_handler)->Exec(m_database, "COMMIT TRANSACTION");
|
||||
if (res != SQLITE_OK) {
|
||||
LogPrintf("SQLiteBatch: Failed to commit the transaction\n");
|
||||
}
|
||||
|
@ -627,7 +632,7 @@ bool SQLiteBatch::TxnCommit()
|
|||
bool SQLiteBatch::TxnAbort()
|
||||
{
|
||||
if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) != 0) return false;
|
||||
int res = sqlite3_exec(m_database.m_db, "ROLLBACK TRANSACTION", nullptr, nullptr, nullptr);
|
||||
int res = Assert(m_exec_handler)->Exec(m_database, "ROLLBACK TRANSACTION");
|
||||
if (res != SQLITE_OK) {
|
||||
LogPrintf("SQLiteBatch: Failed to abort the transaction\n");
|
||||
}
|
||||
|
|
|
@ -36,11 +36,21 @@ public:
|
|||
Status Next(DataStream& key, DataStream& value) override;
|
||||
};
|
||||
|
||||
/** Class responsible for executing SQL statements in SQLite databases.
|
||||
* Methods are virtual so they can be overridden by unit tests testing unusual database conditions. */
|
||||
class SQliteExecHandler
|
||||
{
|
||||
public:
|
||||
virtual ~SQliteExecHandler() {}
|
||||
virtual int Exec(SQLiteDatabase& database, const std::string& statement);
|
||||
};
|
||||
|
||||
/** RAII class that provides access to a WalletDatabase */
|
||||
class SQLiteBatch : public DatabaseBatch
|
||||
{
|
||||
private:
|
||||
SQLiteDatabase& m_database;
|
||||
std::unique_ptr<SQliteExecHandler> m_exec_handler{std::make_unique<SQliteExecHandler>()};
|
||||
|
||||
sqlite3_stmt* m_read_stmt{nullptr};
|
||||
sqlite3_stmt* m_insert_stmt{nullptr};
|
||||
|
@ -61,6 +71,8 @@ public:
|
|||
explicit SQLiteBatch(SQLiteDatabase& database);
|
||||
~SQLiteBatch() override { Close(); }
|
||||
|
||||
void SetExecHandler(std::unique_ptr<SQliteExecHandler>&& handler) { m_exec_handler = std::move(handler); }
|
||||
|
||||
/* No-op. See comment on SQLiteDatabase::Flush */
|
||||
void Flush() override {}
|
||||
|
||||
|
|
Loading…
Reference in a new issue