forked from qt-creator/qt-creator
Sqlite: Add progress handler
Sqlite::ProgressHandler handler{ [] { return Sqlite::Progress::Continue; }, 1000, database}; is setting up a progress handler for this scope. If the handler is destructed it will automatically reset the handler on the database. The handler is active for the whole database connection. Task-number: QDS-9216 Change-Id: I59831f40d32c062eefdfb0c4dfbf3045058e1fd2 Reviewed-by: Vikas Pachdha <vikas.pachdha@qt.io>
This commit is contained in:
@@ -33,6 +33,7 @@ add_qtc_library(Sqlite
|
||||
sqliteexception.cpp sqliteexception.h
|
||||
sqliteglobal.cpp sqliteglobal.h
|
||||
sqliteindex.h
|
||||
sqliteprogresshandler.h
|
||||
sqlitereadstatement.h
|
||||
sqlitereadwritestatement.h
|
||||
sqlitesessionchangeset.cpp sqlitesessionchangeset.h
|
||||
|
@@ -459,6 +459,36 @@ void DatabaseBackend::setBusyHandler(DatabaseBackend::BusyHandler &&busyHandler)
|
||||
registerBusyHandler();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int progressHandlerCallback(void *userData)
|
||||
{
|
||||
auto &&progressHandler = *static_cast<DatabaseBackend::ProgressHandler *>(userData);
|
||||
|
||||
return progressHandler() == Progress::Interrupt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void DatabaseBackend::setProgressHandler(int operationCount, ProgressHandler &&progressHandler)
|
||||
{
|
||||
m_progressHandler = std::move(progressHandler);
|
||||
|
||||
if (m_progressHandler)
|
||||
sqlite3_progress_handler(sqliteDatabaseHandle(),
|
||||
operationCount,
|
||||
&progressHandlerCallback,
|
||||
&m_progressHandler);
|
||||
else {
|
||||
sqlite3_progress_handler(sqliteDatabaseHandle(), 0, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseBackend::resetProgressHandler()
|
||||
{
|
||||
sqlite3_progress_handler(sqliteDatabaseHandle(), 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void DatabaseBackend::throwExceptionStatic(const char *whatHasHappens)
|
||||
{
|
||||
throw Exception(whatHasHappens);
|
||||
|
@@ -15,10 +15,13 @@ namespace Sqlite {
|
||||
|
||||
class Database;
|
||||
|
||||
enum class Progress { Interrupt, Continue };
|
||||
|
||||
class SQLITE_EXPORT DatabaseBackend
|
||||
{
|
||||
public:
|
||||
using BusyHandler = std::function<bool(int count)>;
|
||||
using ProgressHandler = std::function<Progress()>;
|
||||
|
||||
DatabaseBackend(Database &database);
|
||||
~DatabaseBackend();
|
||||
@@ -76,6 +79,8 @@ public:
|
||||
void resetUpdateHook();
|
||||
|
||||
void setBusyHandler(BusyHandler &&busyHandler);
|
||||
void setProgressHandler(int operationCount, ProgressHandler &&progressHandler);
|
||||
void resetProgressHandler();
|
||||
|
||||
void registerBusyHandler();
|
||||
|
||||
@@ -112,6 +117,7 @@ private:
|
||||
Database &m_database;
|
||||
sqlite3 *m_databaseHandle;
|
||||
BusyHandler m_busyHandler;
|
||||
ProgressHandler m_progressHandler;
|
||||
};
|
||||
|
||||
} // namespace Sqlite
|
||||
|
31
src/libs/sqlite/sqliteprogresshandler.h
Normal file
31
src/libs/sqlite/sqliteprogresshandler.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sqlitedatabase.h"
|
||||
|
||||
namespace Sqlite {
|
||||
|
||||
class ProgressHandler
|
||||
{
|
||||
public:
|
||||
template<typename Callable>
|
||||
ProgressHandler(Callable &&callable, int operationCount, Database &database)
|
||||
: m_database{database}
|
||||
{
|
||||
std::unique_lock<TransactionInterface> locker{m_database};
|
||||
m_database.backend().setProgressHandler(operationCount, std::forward<Callable>(callable));
|
||||
}
|
||||
|
||||
~ProgressHandler()
|
||||
{
|
||||
std::unique_lock<TransactionInterface> locker{m_database};
|
||||
m_database.backend().resetProgressHandler();
|
||||
}
|
||||
|
||||
private:
|
||||
Database &m_database;
|
||||
};
|
||||
|
||||
} // namespace Sqlite
|
@@ -6,6 +6,7 @@
|
||||
#include "spydummy.h"
|
||||
|
||||
#include <sqlitedatabase.h>
|
||||
#include <sqliteprogresshandler.h>
|
||||
#include <sqlitereadstatement.h>
|
||||
#include <sqlitetable.h>
|
||||
#include <sqlitewritestatement.h>
|
||||
@@ -32,16 +33,10 @@ class SqliteDatabase : public ::testing::Test
|
||||
protected:
|
||||
SqliteDatabase()
|
||||
{
|
||||
database.lock();
|
||||
database.setJournalMode(JournalMode::Memory);
|
||||
database.setDatabaseFilePath(databaseFilePath);
|
||||
Table table;
|
||||
table.setName("test");
|
||||
table.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
|
||||
table.addColumn("name");
|
||||
|
||||
database.open();
|
||||
|
||||
table.initialize(database);
|
||||
}
|
||||
|
||||
@@ -49,7 +44,6 @@ protected:
|
||||
{
|
||||
if (database.isOpen())
|
||||
database.close();
|
||||
database.unlock();
|
||||
}
|
||||
|
||||
std::vector<Utils::SmallString> names() const
|
||||
@@ -68,17 +62,18 @@ protected:
|
||||
|
||||
protected:
|
||||
SpyDummy spyDummy;
|
||||
QString databaseFilePath{":memory:"};
|
||||
mutable Sqlite::Database database;
|
||||
Table table;
|
||||
mutable Sqlite::Database database{":memory:", JournalMode::Memory};
|
||||
Sqlite::TransactionInterface &transactionInterface = database;
|
||||
MockFunction<void(Sqlite::ChangeType tupe, char const *, char const *, long long)> callbackMock;
|
||||
std::function<void(Sqlite::ChangeType tupe, char const *, char const *, long long)>
|
||||
callback = callbackMock.AsStdFunction();
|
||||
std::unique_lock<Sqlite::Database> lock{database};
|
||||
};
|
||||
|
||||
TEST_F(SqliteDatabase, SetDatabaseFilePath)
|
||||
{
|
||||
ASSERT_THAT(database.databaseFilePath(), databaseFilePath);
|
||||
ASSERT_THAT(database.databaseFilePath(), ":memory:");
|
||||
}
|
||||
|
||||
TEST_F(SqliteDatabase, SetJournalMode)
|
||||
@@ -353,7 +348,41 @@ TEST_F(SqliteDatabase, SessionsRollback)
|
||||
.write(2, "hoo");
|
||||
database.applyAndUpdateSessions();
|
||||
|
||||
ASSERT_THAT(names(), ElementsAre("foo", "hoo"));
|
||||
ASSERT_THAT(names(), UnorderedElementsAre("foo", "hoo"));
|
||||
}
|
||||
|
||||
TEST_F(SqliteDatabase, ProgressHandlerInterrupts)
|
||||
{
|
||||
Sqlite::WriteStatement<1> statement("INSERT INTO test(name) VALUES (?)", database);
|
||||
lock.unlock();
|
||||
Sqlite::ProgressHandler handler{[] { return Sqlite::Progress::Interrupt; }, 1, database};
|
||||
lock.lock();
|
||||
|
||||
ASSERT_THROW(statement.write(42), Sqlite::ExecutionInterrupted);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
TEST_F(SqliteDatabase, ProgressHandlerContinues)
|
||||
{
|
||||
Sqlite::WriteStatement<1> statement("INSERT INTO test(name) VALUES (?)", database);
|
||||
lock.unlock();
|
||||
Sqlite::ProgressHandler handler{[] { return Sqlite::Progress::Continue; }, 1, database};
|
||||
lock.lock();
|
||||
|
||||
ASSERT_NO_THROW(statement.write(42));
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
TEST_F(SqliteDatabase, ProgressHandlerResetsAfterLeavingScope)
|
||||
{
|
||||
lock.unlock();
|
||||
{
|
||||
Sqlite::ProgressHandler handler{[] { return Sqlite::Progress::Interrupt; }, 1, database};
|
||||
}
|
||||
lock.lock();
|
||||
Sqlite::WriteStatement<1> statement("INSERT INTO test(name) VALUES (?)", database);
|
||||
|
||||
ASSERT_NO_THROW(statement.write(42));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user