Sqlite: Improve transaction by inversion of control

Using a callable makes it possible to omit the commit call. It is now
called by the withDeferredTransaction and withImmediateTransaction
function.

Change-Id: I9b7bfa7e32f269fe8fcba2fe5e1218e73f5846d1
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Vikas Pachdha <vikas.pachdha@qt.io>
This commit is contained in:
Marco Bubke
2023-05-21 13:23:26 +02:00
parent c9649513d1
commit be824148be
8 changed files with 302 additions and 237 deletions

View File

@@ -34,57 +34,39 @@ public:
template<typename ResultType, typename... QueryTypes> template<typename ResultType, typename... QueryTypes>
auto valueWithTransaction(const QueryTypes &...queryValues) auto valueWithTransaction(const QueryTypes &...queryValues)
{ {
DeferredTransaction transaction{Base::database()}; return withDeferredTransaction(Base::database(), [&] {
return Base::template value<ResultType>(queryValues...);
auto resultValue = Base::template value<ResultType>(queryValues...); });
transaction.commit();
return resultValue;
} }
template<typename ResultType, typename... QueryTypes> template<typename ResultType, typename... QueryTypes>
auto optionalValueWithTransaction(const QueryTypes &...queryValues) auto optionalValueWithTransaction(const QueryTypes &...queryValues)
{ {
DeferredTransaction transaction{Base::database()}; return withDeferredTransaction(Base::database(), [&] {
return Base::template optionalValue<ResultType>(queryValues...);
auto resultValue = Base::template optionalValue<ResultType>(queryValues...); });
transaction.commit();
return resultValue;
} }
template<typename ResultType, typename... QueryTypes> template<typename ResultType, typename... QueryTypes>
auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues)
{ {
DeferredTransaction transaction{Base::database()}; return withDeferredTransaction(Base::database(), [&] {
return Base::template values<ResultType>(reserveSize, queryValues...);
auto resultValues = Base::template values<ResultType>(reserveSize, queryValues...); });
transaction.commit();
return resultValues;
} }
template<typename Callable, typename... QueryTypes> template<typename Callable, typename... QueryTypes>
void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues)
{ {
DeferredTransaction transaction{Base::database()}; withDeferredTransaction(Base::database(), [&] {
Base::readCallback(std::forward<Callable>(callable), queryValues...);
Base::readCallback(std::forward<Callable>(callable), queryValues...); });
transaction.commit();
} }
template<typename Container, typename... QueryTypes> template<typename Container, typename... QueryTypes>
void readToWithTransaction(Container &container, const QueryTypes &...queryValues) void readToWithTransaction(Container &container, const QueryTypes &...queryValues)
{ {
DeferredTransaction transaction{Base::database()}; withDeferredTransaction(Base::database(), [&] { Base::readTo(container, queryValues...); });
Base::readTo(container, queryValues...);
transaction.commit();
} }
protected: protected:

View File

@@ -34,66 +34,48 @@ public:
template<typename ResultType, typename... QueryTypes> template<typename ResultType, typename... QueryTypes>
auto valueWithTransaction(const QueryTypes &...queryValues) auto valueWithTransaction(const QueryTypes &...queryValues)
{ {
ImmediateTransaction transaction{Base::database()}; return withImmediateTransaction(Base::database(), [&] {
return Base::template value<ResultType>(queryValues...);
auto resultValue = Base::template value<ResultType>(queryValues...); });
transaction.commit();
return resultValue;
} }
template<typename ResultType, typename... QueryTypes> template<typename ResultType, typename... QueryTypes>
auto optionalValueWithTransaction(const QueryTypes &...queryValues) auto optionalValueWithTransaction(const QueryTypes &...queryValues)
{ {
ImmediateTransaction transaction{Base::database()}; return withImmediateTransaction(Base::database(), [&] {
return Base::template optionalValue<ResultType>(queryValues...);
auto resultValue = Base::template optionalValue<ResultType>(queryValues...); });
transaction.commit();
return resultValue;
} }
template<typename ResultType, typename... QueryTypes> template<typename ResultType, typename... QueryTypes>
auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues)
{ {
ImmediateTransaction transaction{Base::database()}; return withImmediateTransaction(Base::database(), [&] {
return Base::template values<ResultType>(reserveSize, queryValues...);
auto resultValues = Base::template values<ResultType>(reserveSize, queryValues...); });
transaction.commit();
return resultValues;
} }
template<typename Callable, typename... QueryTypes> template<typename Callable, typename... QueryTypes>
void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues) void readCallbackWithTransaction(Callable &&callable, const QueryTypes &...queryValues)
{ {
ImmediateTransaction transaction{Base::database()}; withImmediateTransaction(Base::database(), [&] {
Base::readCallback(std::forward<Callable>(callable), queryValues...);
Base::readCallback(std::forward<Callable>(callable), queryValues...); });
transaction.commit();
} }
template<typename Container, typename... QueryTypes> template<typename Container, typename... QueryTypes>
void readToWithTransaction(Container &container, const QueryTypes &...queryValues) void readToWithTransaction(Container &container, const QueryTypes &...queryValues)
{ {
ImmediateTransaction transaction{Base::database()}; withImmediateTransaction(Base::database(), [&] {
Base::readTo(container, queryValues...);
Base::readTo(container, queryValues...); });
transaction.commit();
} }
void executeWithTransaction() void executeWithTransaction()
{ {
ImmediateTransaction transaction{Base::database()}; withImmediateTransaction(Base::database(), [&] {
Base::execute();
Base::execute(); });
transaction.commit();
} }
}; };

View File

@@ -60,7 +60,6 @@ protected:
{ {
} }
protected: protected:
TransactionInterface &m_interface; TransactionInterface &m_interface;
std::unique_lock<TransactionInterface> m_locker{m_interface}; std::unique_lock<TransactionInterface> m_locker{m_interface};
@@ -183,6 +182,38 @@ public:
using Base::Base; using Base::Base;
}; };
template<typename Transaction, typename TransactionInterface, typename Callable>
auto withTransaction(TransactionInterface &transactionInterface, Callable &&callable)
-> std::invoke_result_t<Callable>
{
Transaction transaction{transactionInterface};
if constexpr (std::is_void_v<std::invoke_result_t<Callable>>) {
callable();
transaction.commit();
} else {
auto results = callable();
transaction.commit();
return results;
}
}
template<typename TransactionInterface, typename Callable>
auto withDeferredTransaction(TransactionInterface &transactionInterface, Callable &&callable)
{
if constexpr (std::is_void_v<std::invoke_result_t<Callable>>) {
withTransaction<DeferredTransaction<TransactionInterface>>(transactionInterface,
std::forward<Callable>(callable));
} else {
return withTransaction<DeferredTransaction<TransactionInterface>>(transactionInterface,
std::forward<Callable>(
callable));
}
}
template<typename TransactionInterface> template<typename TransactionInterface>
DeferredTransaction(TransactionInterface &) -> DeferredTransaction<TransactionInterface>; DeferredTransaction(TransactionInterface &) -> DeferredTransaction<TransactionInterface>;
@@ -226,6 +257,20 @@ public:
using Base::Base; using Base::Base;
}; };
template<typename TransactionInterface, typename Callable>
auto withImmediateTransaction(TransactionInterface &transactionInterface, Callable &&callable)
{
if constexpr (std::is_void_v<std::invoke_result_t<Callable>>) {
withTransaction<ImmediateTransaction<TransactionInterface>>(transactionInterface,
std::forward<Callable>(
callable));
} else {
return withTransaction<ImmediateTransaction<TransactionInterface>>(transactionInterface,
std::forward<Callable>(
callable));
}
}
template<typename TransactionInterface> template<typename TransactionInterface>
ImmediateTransaction(TransactionInterface &) -> ImmediateTransaction<TransactionInterface>; ImmediateTransaction(TransactionInterface &) -> ImmediateTransaction<TransactionInterface>;

View File

@@ -35,12 +35,8 @@ public:
ImageEntry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override ImageEntry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
{ {
try { try {
Sqlite::DeferredTransaction transaction{database}; auto optionalBlob = selectImageStatement.template optionalValueWithTransaction<
Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value);
auto optionalBlob = selectImageStatement.template optionalValue<Sqlite::ByteArrayBlob>(
name, minimumTimeStamp.value);
transaction.commit();
if (optionalBlob) if (optionalBlob)
return {readImage(optionalBlob->byteArray)}; return {readImage(optionalBlob->byteArray)};
@@ -55,12 +51,8 @@ public:
Sqlite::TimeStamp minimumTimeStamp) const override Sqlite::TimeStamp minimumTimeStamp) const override
{ {
try { try {
Sqlite::DeferredTransaction transaction{database}; auto optionalBlob = selectMidSizeImageStatement.template optionalValueWithTransaction<
Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value);
auto optionalBlob = selectMidSizeImageStatement.template optionalValue<Sqlite::ByteArrayBlob>(
name, minimumTimeStamp.value);
transaction.commit();
if (optionalBlob) if (optionalBlob)
return {readImage(optionalBlob->byteArray)}; return {readImage(optionalBlob->byteArray)};
@@ -75,12 +67,8 @@ public:
Sqlite::TimeStamp minimumTimeStamp) const override Sqlite::TimeStamp minimumTimeStamp) const override
{ {
try { try {
Sqlite::DeferredTransaction transaction{database}; auto optionalBlob = selectSmallImageStatement.template optionalValueWithTransaction<
Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value);
auto optionalBlob = selectSmallImageStatement.template optionalValue<Sqlite::ByteArrayBlob>(
name, minimumTimeStamp.value);
transaction.commit();
if (optionalBlob) if (optionalBlob)
return ImageEntry{readImage(optionalBlob->byteArray)}; return ImageEntry{readImage(optionalBlob->byteArray)};
@@ -95,12 +83,8 @@ public:
IconEntry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override IconEntry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
{ {
try { try {
Sqlite::DeferredTransaction transaction{database}; auto optionalBlob = selectIconStatement.template optionalValueWithTransaction<
Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value);
auto optionalBlob = selectIconStatement.template optionalValue<Sqlite::ByteArrayBlob>(
name, minimumTimeStamp.value);
transaction.commit();
if (optionalBlob) if (optionalBlob)
return {readIcon(optionalBlob->byteArray)}; return {readIcon(optionalBlob->byteArray)};
@@ -119,19 +103,16 @@ public:
const QImage &smallImage) override const QImage &smallImage) override
{ {
try { try {
Sqlite::ImmediateTransaction transaction{database};
auto imageBuffer = createBuffer(image); auto imageBuffer = createBuffer(image);
auto midSizeImageBuffer = createBuffer(midSizeImage); auto midSizeImageBuffer = createBuffer(midSizeImage);
auto smallImageBuffer = createBuffer(smallImage); auto smallImageBuffer = createBuffer(smallImage);
upsertImageStatement.write(name, Sqlite::withImmediateTransaction(database, [&] {
newTimeStamp.value, upsertImageStatement.write(name,
createBlobView(imageBuffer.get()), newTimeStamp.value,
createBlobView(midSizeImageBuffer.get()), createBlobView(imageBuffer.get()),
createBlobView(smallImageBuffer.get())); createBlobView(midSizeImageBuffer.get()),
createBlobView(smallImageBuffer.get()));
transaction.commit(); });
} catch (const Sqlite::StatementIsBusy &) { } catch (const Sqlite::StatementIsBusy &) {
return storeImage(name, newTimeStamp, image, midSizeImage, smallImage); return storeImage(name, newTimeStamp, image, midSizeImage, smallImage);
} }
@@ -140,12 +121,10 @@ public:
void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) override void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) override
{ {
try { try {
Sqlite::ImmediateTransaction transaction{database};
auto iconBuffer = createBuffer(icon); auto iconBuffer = createBuffer(icon);
upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get())); Sqlite::withImmediateTransaction(database, [&] {
upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get()));
transaction.commit(); });
} catch (const Sqlite::StatementIsBusy &) { } catch (const Sqlite::StatementIsBusy &) {
return storeIcon(name, newTimeStamp, icon); return storeIcon(name, newTimeStamp, icon);

View File

@@ -45,63 +45,61 @@ public:
void synchronize(Storage::Synchronization::SynchronizationPackage package) override void synchronize(Storage::Synchronization::SynchronizationPackage package) override
{ {
Sqlite::ImmediateTransaction transaction{database}; Sqlite::withImmediateTransaction(database, [&] {
AliasPropertyDeclarations insertedAliasPropertyDeclarations;
AliasPropertyDeclarations updatedAliasPropertyDeclarations;
AliasPropertyDeclarations insertedAliasPropertyDeclarations; AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
AliasPropertyDeclarations updatedAliasPropertyDeclarations; PropertyDeclarations relinkablePropertyDeclarations;
Prototypes relinkablePrototypes;
Prototypes relinkableExtensions;
TypeIds deletedTypeIds;
AliasPropertyDeclarations relinkableAliasPropertyDeclarations; TypeIds updatedTypeIds;
PropertyDeclarations relinkablePropertyDeclarations; updatedTypeIds.reserve(package.types.size());
Prototypes relinkablePrototypes;
Prototypes relinkableExtensions;
TypeIds deletedTypeIds;
TypeIds updatedTypeIds; TypeIds typeIdsToBeDeleted;
updatedTypeIds.reserve(package.types.size());
TypeIds typeIdsToBeDeleted; std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end());
std::sort(package.updatedSourceIds.begin(), package.updatedSourceIds.end()); synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds);
synchronizeImports(package.imports,
package.updatedSourceIds,
package.moduleDependencies,
package.updatedModuleDependencySourceIds,
package.moduleExportedImports,
package.updatedModuleIds);
synchronizeTypes(package.types,
updatedTypeIds,
insertedAliasPropertyDeclarations,
updatedAliasPropertyDeclarations,
relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
package.updatedSourceIds);
synchronizeFileStatuses(package.fileStatuses, package.updatedFileStatusSourceIds); deleteNotUpdatedTypes(updatedTypeIds,
synchronizeImports(package.imports, package.updatedSourceIds,
package.updatedSourceIds, typeIdsToBeDeleted,
package.moduleDependencies, relinkableAliasPropertyDeclarations,
package.updatedModuleDependencySourceIds, relinkablePropertyDeclarations,
package.moduleExportedImports, relinkablePrototypes,
package.updatedModuleIds); relinkableExtensions,
synchronizeTypes(package.types, deletedTypeIds);
updatedTypeIds,
insertedAliasPropertyDeclarations,
updatedAliasPropertyDeclarations,
relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
package.updatedSourceIds);
deleteNotUpdatedTypes(updatedTypeIds, relink(relinkableAliasPropertyDeclarations,
package.updatedSourceIds, relinkablePropertyDeclarations,
typeIdsToBeDeleted, relinkablePrototypes,
relinkableAliasPropertyDeclarations, relinkableExtensions,
relinkablePropertyDeclarations, deletedTypeIds);
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
relink(relinkableAliasPropertyDeclarations, linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds);
synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); commonTypeCache_.resetTypeIds();
});
commonTypeCache_.resetTypeIds();
transaction.commit();
} }
ModuleId moduleId(Utils::SmallStringView moduleName) const override ModuleId moduleId(Utils::SmallStringView moduleName) const override
@@ -304,38 +302,35 @@ public:
Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId) Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId)
{ {
Sqlite::DeferredTransaction transaction{database}; return Sqlite::withDeferredTransaction(database, [&] {
auto type = selectTypeByTypeIdStatement.template value<Storage::Synchronization::Type>(
typeId);
auto type = selectTypeByTypeIdStatement.template value<Storage::Synchronization::Type>(typeId); type.exportedTypes = fetchExportedTypes(typeId);
type.exportedTypes = fetchExportedTypes(typeId);
type.propertyDeclarations = fetchPropertyDeclarations(type.typeId);
type.functionDeclarations = fetchFunctionDeclarations(type.typeId);
type.signalDeclarations = fetchSignalDeclarations(type.typeId);
type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId);
transaction.commit();
return type;
}
Storage::Synchronization::Types fetchTypes()
{
Sqlite::DeferredTransaction transaction{database};
auto types = selectTypesStatement.template values<Storage::Synchronization::Type>(64);
for (Storage::Synchronization::Type &type : types) {
type.exportedTypes = fetchExportedTypes(type.typeId);
type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); type.propertyDeclarations = fetchPropertyDeclarations(type.typeId);
type.functionDeclarations = fetchFunctionDeclarations(type.typeId); type.functionDeclarations = fetchFunctionDeclarations(type.typeId);
type.signalDeclarations = fetchSignalDeclarations(type.typeId); type.signalDeclarations = fetchSignalDeclarations(type.typeId);
type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId);
}
transaction.commit(); return type;
});
}
return types; Storage::Synchronization::Types fetchTypes()
{
return Sqlite::withDeferredTransaction(database, [&] {
auto types = selectTypesStatement.template values<Storage::Synchronization::Type>(64);
for (Storage::Synchronization::Type &type : types) {
type.exportedTypes = fetchExportedTypes(type.typeId);
type.propertyDeclarations = fetchPropertyDeclarations(type.typeId);
type.functionDeclarations = fetchFunctionDeclarations(type.typeId);
type.signalDeclarations = fetchSignalDeclarations(type.typeId);
type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId);
}
return types;
});
} }
bool fetchIsProtype(TypeId type, TypeId prototype) bool fetchIsProtype(TypeId type, TypeId prototype)
@@ -358,13 +353,9 @@ public:
SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath)
{ {
try { try {
Sqlite::DeferredTransaction transaction{database}; return Sqlite::withDeferredTransaction(database, [&] {
return fetchSourceContextIdUnguarded(sourceContextPath);
auto sourceContextId = fetchSourceContextIdUnguarded(sourceContextPath); });
transaction.commit();
return sourceContextId;
} catch (const Sqlite::ConstraintPreventsModification &) { } catch (const Sqlite::ConstraintPreventsModification &) {
return fetchSourceContextId(sourceContextPath); return fetchSourceContextId(sourceContextPath);
} }
@@ -372,18 +363,16 @@ public:
Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const
{ {
Sqlite::DeferredTransaction transaction{database}; return Sqlite::withDeferredTransaction(database, [&] {
auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement
.template optionalValue<Utils::PathString>(
sourceContextId);
auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement if (!optionalSourceContextPath)
.template optionalValue<Utils::PathString>( throw SourceContextIdDoesNotExists();
sourceContextId);
if (!optionalSourceContextPath) return std::move(*optionalSourceContextPath);
throw SourceContextIdDoesNotExists(); });
transaction.commit();
return std::move(*optionalSourceContextPath);
} }
auto fetchAllSourceContexts() const auto fetchAllSourceContexts() const
@@ -394,13 +383,9 @@ public:
SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{ {
Sqlite::DeferredTransaction transaction{database}; return Sqlite::withDeferredTransaction(database, [&] {
return fetchSourceIdUnguarded(sourceContextId, sourceName);
auto sourceId = fetchSourceIdUnguarded(sourceContextId, sourceName); });
transaction.commit();
return sourceId;
} }
auto fetchSourceNameAndSourceContextId(SourceId sourceId) const auto fetchSourceNameAndSourceContextId(SourceId sourceId) const
@@ -416,12 +401,10 @@ public:
void clearSources() void clearSources()
{ {
Sqlite::ImmediateTransaction transaction{database}; Sqlite::withImmediateTransaction(database, [&] {
deleteAllSourceContextsStatement.execute();
deleteAllSourceContextsStatement.execute(); deleteAllSourcesStatement.execute();
deleteAllSourcesStatement.execute(); });
transaction.commit();
} }
SourceContextId fetchSourceContextId(SourceId sourceId) const SourceContextId fetchSourceContextId(SourceId sourceId) const
@@ -523,24 +506,15 @@ private:
ModuleId fetchModuleId(Utils::SmallStringView moduleName) ModuleId fetchModuleId(Utils::SmallStringView moduleName)
{ {
Sqlite::DeferredTransaction transaction{database}; return Sqlite::withDeferredTransaction(database,
[&] { return fetchModuleIdUnguarded(moduleName); });
ModuleId moduleId = fetchModuleIdUnguarded(moduleName);
transaction.commit();
return moduleId;
} }
auto fetchModuleName(ModuleId id) auto fetchModuleName(ModuleId id)
{ {
Sqlite::DeferredTransaction transaction{database}; return Sqlite::withDeferredTransaction(database, [&] {
return fetchModuleNameUnguarded(id);
auto moduleName = fetchModuleNameUnguarded(id); });
transaction.commit();
return moduleName;
} }
auto fetchAllModules() const auto fetchAllModules() const

View File

@@ -8,6 +8,7 @@
SqliteReadStatementMockBase::SqliteReadStatementMockBase(Utils::SmallStringView sqlStatement, SqliteReadStatementMockBase::SqliteReadStatementMockBase(Utils::SmallStringView sqlStatement,
SqliteDatabaseMock &databaseMock) SqliteDatabaseMock &databaseMock)
: sqlStatement(sqlStatement) : sqlStatement(sqlStatement)
, databaseMock(databaseMock)
{ {
databaseMock.prepare(sqlStatement); databaseMock.prepare(sqlStatement);
} }

View File

@@ -11,6 +11,7 @@
#include <projectstorageids.h> #include <projectstorageids.h>
#include <sqliteblob.h> #include <sqliteblob.h>
#include <sqlitetimestamp.h> #include <sqlitetimestamp.h>
#include <sqlitetransaction.h>
#include <utils/smallstring.h> #include <utils/smallstring.h>
#include <QImage> #include <QImage>
@@ -198,7 +199,9 @@ public:
template<typename ResultType, typename... QueryTypes> template<typename ResultType, typename... QueryTypes>
auto optionalValueWithTransaction(const QueryTypes &...queryValues) auto optionalValueWithTransaction(const QueryTypes &...queryValues)
{ {
return optionalValue<ResultType>(queryValues...); return Sqlite::withDeferredTransaction(databaseMock, [&] {
return optionalValue<ResultType>(queryValues...);
});
} }
template<typename ResultType, typename... QueryType> template<typename ResultType, typename... QueryType>
@@ -261,6 +264,7 @@ public:
public: public:
Utils::SmallString sqlStatement; Utils::SmallString sqlStatement;
SqliteDatabaseMock &databaseMock;
}; };
template<int ResultCount, int BindParameterCount = 0> template<int ResultCount, int BindParameterCount = 0>

View File

@@ -21,8 +21,13 @@ using Sqlite::ImmediateTransaction;
class SqliteTransaction : public testing::Test class SqliteTransaction : public testing::Test
{ {
protected:
SqliteTransaction() { ON_CALL(callableWithReturnMock, Call()).WillByDefault(Return(212)); }
protected: protected:
NiceMock<MockSqliteTransactionBackend> mockTransactionBackend; NiceMock<MockSqliteTransactionBackend> mockTransactionBackend;
NiceMock<MockFunction<void()>> callableMock;
NiceMock<MockFunction<int()>> callableWithReturnMock;
}; };
TEST_F(SqliteTransaction, DeferredTransactionCommit) TEST_F(SqliteTransaction, DeferredTransactionCommit)
@@ -38,19 +43,6 @@ TEST_F(SqliteTransaction, DeferredTransactionCommit)
transaction.commit(); transaction.commit();
} }
TEST_F(SqliteTransaction, DeferredTransactionCommitCallsInterface)
{
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
EXPECT_CALL(mockTransactionBackend, deferredBegin());
EXPECT_CALL(mockTransactionBackend, commit());
EXPECT_CALL(mockTransactionBackend, unlock());
DeferredTransaction transaction{mockTransactionBackend};
transaction.commit();
}
TEST_F(SqliteTransaction, DeferredTransactionRollBack) TEST_F(SqliteTransaction, DeferredTransactionRollBack)
{ {
InSequence s; InSequence s;
@@ -337,4 +329,110 @@ TEST_F(SqliteTransaction, ImmediateSessionTransactionBeginThrowsAndNotRollback)
ASSERT_ANY_THROW(ImmediateSessionTransaction{mockTransactionBackend}); ASSERT_ANY_THROW(ImmediateSessionTransaction{mockTransactionBackend});
} }
TEST_F(SqliteTransaction, WithDeferredTransactionNoReturnCommit)
{
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
EXPECT_CALL(mockTransactionBackend, deferredBegin());
EXPECT_CALL(callableMock, Call());
EXPECT_CALL(mockTransactionBackend, commit());
EXPECT_CALL(mockTransactionBackend, unlock());
Sqlite::withDeferredTransaction(mockTransactionBackend, callableMock.AsStdFunction());
}
TEST_F(SqliteTransaction, WithDeferredTransactionWithReturnCommit)
{
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
EXPECT_CALL(mockTransactionBackend, deferredBegin());
EXPECT_CALL(callableWithReturnMock, Call());
EXPECT_CALL(mockTransactionBackend, commit());
EXPECT_CALL(mockTransactionBackend, unlock());
Sqlite::withDeferredTransaction(mockTransactionBackend, callableWithReturnMock.AsStdFunction());
}
TEST_F(SqliteTransaction, WithDeferredTransactionReturnsValue)
{
auto callable = callableWithReturnMock.AsStdFunction();
auto value = Sqlite::withDeferredTransaction(mockTransactionBackend,
callableWithReturnMock.AsStdFunction());
ASSERT_THAT(value, Eq(212));
}
TEST_F(SqliteTransaction, WithDeferredTransactionRollsbackForException)
{
InSequence s;
ON_CALL(callableMock, Call()).WillByDefault(Throw(std::exception{}));
EXPECT_CALL(mockTransactionBackend, lock());
EXPECT_CALL(mockTransactionBackend, deferredBegin());
EXPECT_CALL(callableMock, Call());
EXPECT_CALL(mockTransactionBackend, rollback());
EXPECT_CALL(mockTransactionBackend, unlock());
try {
Sqlite::withDeferredTransaction(mockTransactionBackend, callableMock.AsStdFunction());
} catch (...) {
}
}
TEST_F(SqliteTransaction, WithImmediateTransactionNoReturnCommit)
{
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
EXPECT_CALL(mockTransactionBackend, immediateBegin());
EXPECT_CALL(callableMock, Call());
EXPECT_CALL(mockTransactionBackend, commit());
EXPECT_CALL(mockTransactionBackend, unlock());
Sqlite::withImmediateTransaction(mockTransactionBackend, callableMock.AsStdFunction());
}
TEST_F(SqliteTransaction, WithImmediateTransactionWithReturnCommit)
{
InSequence s;
EXPECT_CALL(mockTransactionBackend, lock());
EXPECT_CALL(mockTransactionBackend, immediateBegin());
EXPECT_CALL(callableWithReturnMock, Call());
EXPECT_CALL(mockTransactionBackend, commit());
EXPECT_CALL(mockTransactionBackend, unlock());
Sqlite::withImmediateTransaction(mockTransactionBackend, callableWithReturnMock.AsStdFunction());
}
TEST_F(SqliteTransaction, WithImmediateTransactionReturnsValue)
{
auto callable = callableWithReturnMock.AsStdFunction();
auto value = Sqlite::withImmediateTransaction(mockTransactionBackend,
callableWithReturnMock.AsStdFunction());
ASSERT_THAT(value, Eq(212));
}
TEST_F(SqliteTransaction, WithImmediateTransactionRollsbackForException)
{
InSequence s;
ON_CALL(callableMock, Call()).WillByDefault(Throw(std::exception{}));
EXPECT_CALL(mockTransactionBackend, lock());
EXPECT_CALL(mockTransactionBackend, immediateBegin());
EXPECT_CALL(callableMock, Call());
EXPECT_CALL(mockTransactionBackend, rollback());
EXPECT_CALL(mockTransactionBackend, unlock());
try {
Sqlite::withImmediateTransaction(mockTransactionBackend, callableMock.AsStdFunction());
} catch (...) {
}
}
} // namespace } // namespace