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

View File

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

View File

@@ -60,7 +60,6 @@ protected:
{
}
protected:
TransactionInterface &m_interface;
std::unique_lock<TransactionInterface> m_locker{m_interface};
@@ -183,6 +182,38 @@ public:
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>
DeferredTransaction(TransactionInterface &) -> DeferredTransaction<TransactionInterface>;
@@ -226,6 +257,20 @@ public:
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>
ImmediateTransaction(TransactionInterface &) -> ImmediateTransaction<TransactionInterface>;

View File

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

View File

@@ -45,63 +45,61 @@ public:
void synchronize(Storage::Synchronization::SynchronizationPackage package) override
{
Sqlite::ImmediateTransaction transaction{database};
Sqlite::withImmediateTransaction(database, [&] {
AliasPropertyDeclarations insertedAliasPropertyDeclarations;
AliasPropertyDeclarations updatedAliasPropertyDeclarations;
AliasPropertyDeclarations insertedAliasPropertyDeclarations;
AliasPropertyDeclarations updatedAliasPropertyDeclarations;
AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
PropertyDeclarations relinkablePropertyDeclarations;
Prototypes relinkablePrototypes;
Prototypes relinkableExtensions;
TypeIds deletedTypeIds;
AliasPropertyDeclarations relinkableAliasPropertyDeclarations;
PropertyDeclarations relinkablePropertyDeclarations;
Prototypes relinkablePrototypes;
Prototypes relinkableExtensions;
TypeIds deletedTypeIds;
TypeIds updatedTypeIds;
updatedTypeIds.reserve(package.types.size());
TypeIds updatedTypeIds;
updatedTypeIds.reserve(package.types.size());
TypeIds typeIdsToBeDeleted;
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);
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);
deleteNotUpdatedTypes(updatedTypeIds,
package.updatedSourceIds,
typeIdsToBeDeleted,
relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
deleteNotUpdatedTypes(updatedTypeIds,
package.updatedSourceIds,
typeIdsToBeDeleted,
relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
relink(relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
relink(relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes,
relinkableExtensions,
deletedTypeIds);
linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations);
synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds);
synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds);
commonTypeCache_.resetTypeIds();
transaction.commit();
commonTypeCache_.resetTypeIds();
});
}
ModuleId moduleId(Utils::SmallStringView moduleName) const override
@@ -304,38 +302,35 @@ public:
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.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.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;
});
}
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)
@@ -358,13 +353,9 @@ public:
SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath)
{
try {
Sqlite::DeferredTransaction transaction{database};
auto sourceContextId = fetchSourceContextIdUnguarded(sourceContextPath);
transaction.commit();
return sourceContextId;
return Sqlite::withDeferredTransaction(database, [&] {
return fetchSourceContextIdUnguarded(sourceContextPath);
});
} catch (const Sqlite::ConstraintPreventsModification &) {
return fetchSourceContextId(sourceContextPath);
}
@@ -372,18 +363,16 @@ public:
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
.template optionalValue<Utils::PathString>(
sourceContextId);
if (!optionalSourceContextPath)
throw SourceContextIdDoesNotExists();
if (!optionalSourceContextPath)
throw SourceContextIdDoesNotExists();
transaction.commit();
return std::move(*optionalSourceContextPath);
return std::move(*optionalSourceContextPath);
});
}
auto fetchAllSourceContexts() const
@@ -394,13 +383,9 @@ public:
SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName)
{
Sqlite::DeferredTransaction transaction{database};
auto sourceId = fetchSourceIdUnguarded(sourceContextId, sourceName);
transaction.commit();
return sourceId;
return Sqlite::withDeferredTransaction(database, [&] {
return fetchSourceIdUnguarded(sourceContextId, sourceName);
});
}
auto fetchSourceNameAndSourceContextId(SourceId sourceId) const
@@ -416,12 +401,10 @@ public:
void clearSources()
{
Sqlite::ImmediateTransaction transaction{database};
deleteAllSourceContextsStatement.execute();
deleteAllSourcesStatement.execute();
transaction.commit();
Sqlite::withImmediateTransaction(database, [&] {
deleteAllSourceContextsStatement.execute();
deleteAllSourcesStatement.execute();
});
}
SourceContextId fetchSourceContextId(SourceId sourceId) const
@@ -523,24 +506,15 @@ private:
ModuleId fetchModuleId(Utils::SmallStringView moduleName)
{
Sqlite::DeferredTransaction transaction{database};
ModuleId moduleId = fetchModuleIdUnguarded(moduleName);
transaction.commit();
return moduleId;
return Sqlite::withDeferredTransaction(database,
[&] { return fetchModuleIdUnguarded(moduleName); });
}
auto fetchModuleName(ModuleId id)
{
Sqlite::DeferredTransaction transaction{database};
auto moduleName = fetchModuleNameUnguarded(id);
transaction.commit();
return moduleName;
return Sqlite::withDeferredTransaction(database, [&] {
return fetchModuleNameUnguarded(id);
});
}
auto fetchAllModules() const

View File

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

View File

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

View File

@@ -21,8 +21,13 @@ using Sqlite::ImmediateTransaction;
class SqliteTransaction : public testing::Test
{
protected:
SqliteTransaction() { ON_CALL(callableWithReturnMock, Call()).WillByDefault(Return(212)); }
protected:
NiceMock<MockSqliteTransactionBackend> mockTransactionBackend;
NiceMock<MockFunction<void()>> callableMock;
NiceMock<MockFunction<int()>> callableWithReturnMock;
};
TEST_F(SqliteTransaction, DeferredTransactionCommit)
@@ -38,19 +43,6 @@ TEST_F(SqliteTransaction, DeferredTransactionCommit)
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)
{
InSequence s;
@@ -337,4 +329,110 @@ TEST_F(SqliteTransaction, ImmediateSessionTransactionBeginThrowsAndNotRollback)
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