diff --git a/src/libs/clangsupport/filepathstorage.h b/src/libs/clangsupport/filepathstorage.h index 11c30939cd5..7670c3091db 100644 --- a/src/libs/clangsupport/filepathstorage.h +++ b/src/libs/clangsupport/filepathstorage.h @@ -87,7 +87,7 @@ public: { auto &statement = m_statementFactory.selectDirectoryIdFromDirectoriesByDirectoryPath; - return statement.template value(directoryPath); + return statement.template optionalValue(directoryPath); } int writeDirectoryId(Utils::SmallStringView directoryPath) @@ -106,7 +106,7 @@ public: auto &statement = m_statementFactory.selectDirectoryPathFromDirectoriesByDirectoryId; - auto optionalDirectoryPath = statement.template value(directoryPathId); + auto optionalDirectoryPath = statement.template optionalValue(directoryPathId); if (!optionalDirectoryPath) throw DirectoryPathIdDoesNotExists(); @@ -176,7 +176,7 @@ public: { auto &statement = m_statementFactory.selectSourceIdFromSourcesByDirectoryIdAndSourceName; - return statement.template value(directoryId, sourceName); + return statement.template optionalValue(directoryId, sourceName); } Sources::SourceNameAndDirectoryId fetchSourceNameAndDirectoryId(int sourceId) @@ -186,7 +186,7 @@ public: auto &statement = m_statementFactory.selectSourceNameAndDirectoryIdFromSourcesBySourceId; - auto optionalSourceName = statement.template value( + auto optionalSourceName = statement.template optionalValue( sourceId); if (!optionalSourceName) @@ -207,7 +207,7 @@ public: auto &statement = m_statementFactory.selectDirectoryIdFromSourcesBySourceId; - auto optionalDirectoryId = statement.template value(sourceId); + auto optionalDirectoryId = statement.template optionalValue(sourceId); if (!optionalDirectoryId) throw SourceNameIdDoesNotExists(); diff --git a/src/libs/clangsupport/projectpartsstorage.h b/src/libs/clangsupport/projectpartsstorage.h index fa211b83bc2..671b6f0ef7d 100644 --- a/src/libs/clangsupport/projectpartsstorage.h +++ b/src/libs/clangsupport/projectpartsstorage.h @@ -77,7 +77,7 @@ public: bool preCompiledHeaderWasGenerated(ProjectPartId projectPartId) const { - auto value = fetchProjectPrecompiledHeaderBuildTimeStatement.template value( + auto value = fetchProjectPrecompiledHeaderBuildTimeStatement.template optionalValue( projectPartId.projectPathId); return value && *value > 0; @@ -92,7 +92,7 @@ public: Sqlite::DeferredTransaction transaction{database}; for (ProjectPartId projectPartId : projectPartIds) { - auto value = fetchProjectPartByIdStatement.template value( + auto value = fetchProjectPartByIdStatement.template optionalValue( projectPartId.projectPathId); if (value) { value->headerPathIds = fetchHeaders(projectPartId); @@ -112,7 +112,7 @@ public: ProjectPartId fetchProjectPartIdUnguarded(Utils::SmallStringView projectPartName) const override { - auto optionalProjectPartId = fetchProjectPartIdStatement.template value( + auto optionalProjectPartId = fetchProjectPartIdStatement.template optionalValue( projectPartName); if (optionalProjectPartId) { @@ -147,8 +147,9 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto optionalProjectPartName = fetchProjectPartNameStatement.template value( - projectPartId.projectPathId); + auto optionalProjectPartName = fetchProjectPartNameStatement + .template optionalValue( + projectPartId.projectPathId); transaction.commit(); @@ -246,7 +247,7 @@ public: auto &statement = getProjectPartArtefactsBySourceId; - auto value = statement.template value(sourceId.filePathId); + auto value = statement.template optionalValue(sourceId.filePathId); transaction.commit(); @@ -263,7 +264,8 @@ public: auto &statement = getProjectPartArtefactsByProjectPartId; - auto value = statement.template value(projectPartId.projectPathId); + auto value = statement.template optionalValue( + projectPartId.projectPathId); transaction.commit(); diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 8e29b389efc..2ec0665f568 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -216,6 +216,22 @@ public: template auto value(const QueryTypes &...queryValues) + { + Resetter resetter{this}; + ResultType resultValue; + + bindValues(queryValues...); + + if (BaseStatement::next()) + resultValue = createValue(); + + resetter.reset(); + + return resultValue; + } + + template + auto optionalValue(const QueryTypes &...queryValues) { Resetter resetter{this}; Utils::optional resultValue; diff --git a/src/libs/sqlite/sqlitereadstatement.h b/src/libs/sqlite/sqlitereadstatement.h index 04618ba8ebc..09906a3d902 100644 --- a/src/libs/sqlite/sqlitereadstatement.h +++ b/src/libs/sqlite/sqlitereadstatement.h @@ -42,6 +42,7 @@ public: Base::checkColumnCount(ResultCount); } + using Base::optionalValue; using Base::range; using Base::rangeWithTransaction; using Base::readCallback; @@ -62,6 +63,18 @@ public: return resultValue; } + template + auto optionalValueWithTransaction(const QueryTypes &...queryValues) + { + DeferredTransaction transaction{Base::database()}; + + auto resultValue = Base::template optionalValue(queryValues...); + + transaction.commit(); + + return resultValue; + } + template auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) { diff --git a/src/libs/sqlite/sqlitereadwritestatement.h b/src/libs/sqlite/sqlitereadwritestatement.h index 8ea9404251e..e33b247f077 100644 --- a/src/libs/sqlite/sqlitereadwritestatement.h +++ b/src/libs/sqlite/sqlitereadwritestatement.h @@ -43,6 +43,7 @@ public: } using Base::execute; + using Base::optionalValue; using Base::readCallback; using Base::readTo; using Base::toValue; @@ -62,6 +63,18 @@ public: return resultValue; } + template + auto optionalValueWithTransaction(const QueryTypes &...queryValues) + { + ImmediateTransaction transaction{Base::database()}; + + auto resultValue = Base::template optionalValue(queryValues...); + + transaction.commit(); + + return resultValue; + } + template auto valuesWithTransaction(std::size_t reserveSize, const QueryTypes &...queryValues) { diff --git a/src/plugins/clangrefactoring/symbolquery.h b/src/plugins/clangrefactoring/symbolquery.h index 7ceab186fdc..009a41e0b0d 100644 --- a/src/plugins/clangrefactoring/symbolquery.h +++ b/src/plugins/clangrefactoring/symbolquery.h @@ -152,7 +152,7 @@ public: { auto &statement = m_statementFactory.selectLocationOfSymbol; - return statement.template value(symbolId, int(kind)); + return statement.template optionalValue(symbolId, int(kind)); } private: StatementFactory &m_statementFactory; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h index bf589d951d5..3b320634da0 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h @@ -58,7 +58,7 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto optionalBlob = selectImageStatement.template value( + auto optionalBlob = selectImageStatement.template optionalValue( name, minimumTimeStamp.value); transaction.commit(); @@ -78,7 +78,7 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto optionalBlob = selectSmallImageStatement.template value( + auto optionalBlob = selectSmallImageStatement.template optionalValue( name, minimumTimeStamp.value); transaction.commit(); @@ -98,7 +98,7 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto optionalBlob = selectIconStatement.template value( + auto optionalBlob = selectIconStatement.template optionalValue( name, minimumTimeStamp.value); transaction.commit(); diff --git a/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h b/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h index 19b3a982807..0d51b4def9f 100644 --- a/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h +++ b/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h @@ -102,7 +102,7 @@ public: { auto &statement = getLowestLastModifiedTimeOfDependencies; - return statement.template value(sourceId.filePathId).value_or(0); + return statement.template optionalValue(sourceId.filePathId).value_or(0); } void insertOrUpdateUsedMacros(const UsedMacros &usedMacros) override @@ -131,7 +131,8 @@ public: ProjectPartId fetchProjectPartId(Utils::SmallStringView projectPartName) override { - auto projectPartId = fetchProjectPartIdStatement.template value(projectPartName); + auto projectPartId = fetchProjectPartIdStatement.template optionalValue( + projectPartName); if (projectPartId) return *projectPartId; diff --git a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h index 58b3747a573..d0f596aeb2d 100644 --- a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h +++ b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h @@ -145,7 +145,7 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto value = fetchSystemPrecompiledHeaderPathStatement.template value( + auto value = fetchSystemPrecompiledHeaderPathStatement.template optionalValue( projectPartId.projectPathId); transaction.commit(); @@ -165,7 +165,7 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto value = fetchPrecompiledHeaderStatement.template value( + auto value = fetchPrecompiledHeaderStatement.template optionalValue( projectPartId.projectPathId); transaction.commit(); @@ -185,7 +185,7 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto value = fetchPrecompiledHeadersStatement.template value( + auto value = fetchPrecompiledHeadersStatement.template optionalValue( projectPartId.projectPathId); transaction.commit(); @@ -205,7 +205,7 @@ public: try { Sqlite::DeferredTransaction transaction{database}; - auto value = fetchTimeStampsStatement.template value( + auto value = fetchTimeStampsStatement.template optionalValue( projectPartId.projectPathId); transaction.commit(); diff --git a/tests/unit/unittest/sqlitereadstatementmock.h b/tests/unit/unittest/sqlitereadstatementmock.h index 4c48bd9d0e1..b3e65ec18fb 100644 --- a/tests/unit/unittest/sqlitereadstatementmock.h +++ b/tests/unit/unittest/sqlitereadstatementmock.h @@ -161,7 +161,7 @@ public: (int projectPartId)); template - auto value(const QueryTypes &...queryValues) + auto optionalValue(const QueryTypes &...queryValues) { if constexpr (std::is_same_v) return valueReturnBlob(queryValues...); @@ -196,6 +196,19 @@ public: "SqliteReadStatementMock::value does not handle result type!"); } + template + auto value(const QueryTypes &...queryValues) + { + static_assert(!std::is_same_v, + "SqliteReadStatementMock::value does not handle result type!"); + } + + template + auto optionalValueWithTransaction(const QueryTypes &...queryValues) + { + return optionalValue(queryValues...); + } + template auto values(std::size_t reserveSize, const QueryType &...queryValues) { diff --git a/tests/unit/unittest/sqlitestatement-test.cpp b/tests/unit/unittest/sqlitestatement-test.cpp index 324fe554549..3804f8b31ed 100644 --- a/tests/unit/unittest/sqlitestatement-test.cpp +++ b/tests/unit/unittest/sqlitestatement-test.cpp @@ -133,8 +133,11 @@ protected: struct Output { - Output(Utils::SmallStringView name, Utils::SmallStringView number, long long value) - : name(name), number(number), value(value) + explicit Output() = default; + explicit Output(Utils::SmallStringView name, Utils::SmallStringView number, long long value) + : name(name) + , number(number) + , value(value) {} Utils::SmallString name; @@ -559,7 +562,7 @@ TEST_F(SqliteStatement, WriteSqliteBlobValue) statement.write(Sqlite::Value{bytes}); - ASSERT_THAT(readStatement.template value(), + ASSERT_THAT(readStatement.template optionalValue(), Optional(Field(&Sqlite::Blob::bytes, Eq(bytes)))); } @@ -611,7 +614,7 @@ TEST_F(SqliteStatement, WriteSqliteBlobValueView) statement.write(Sqlite::ValueView::create(bytes)); - ASSERT_THAT(readStatement.template value(), + ASSERT_THAT(readStatement.template optionalValue(), Optional(Field(&Sqlite::Blob::bytes, Eq(bytes)))); } @@ -648,7 +651,7 @@ TEST_F(SqliteStatement, WriteBlobs) statement.write(bytes); - ASSERT_THAT(readStatement.template value(), + ASSERT_THAT(readStatement.template optionalValue(), Optional(Field(&Sqlite::Blob::bytes, Eq(bytes)))); } @@ -1049,29 +1052,29 @@ TEST_F(SqliteStatement, GetBlobValues) ASSERT_THAT(values, ElementsAre(Field(&Sqlite::Blob::bytes, Eq(bytes)))); } -TEST_F(SqliteStatement, GetEmptyBlobValueForInteger) +TEST_F(SqliteStatement, GetEmptyOptionalBlobValueForInteger) { ReadStatement<1> statement("SELECT value FROM test WHERE name='poo'", database); - auto value = statement.value(); + auto value = statement.optionalValue(); ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty()))); } -TEST_F(SqliteStatement, GetEmptyBlobValueForFloat) +TEST_F(SqliteStatement, GetEmptyOptionalBlobValueForFloat) { ReadStatement<1> statement("SELECT number FROM test WHERE name='foo'", database); - auto value = statement.value(); + auto value = statement.optionalValue(); ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty()))); } -TEST_F(SqliteStatement, GetEmptyBlobValueForText) +TEST_F(SqliteStatement, GetEmptyOptionalBlobValueForText) { ReadStatement<1> statement("SELECT number FROM test WHERE name='bar'", database); - auto value = statement.value(); + auto value = statement.optionalValue(); ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty()))); } @@ -1081,7 +1084,7 @@ TEST_F(SqliteStatement, GetOptionalSingleValueAndMultipleQueryValue) ReadStatement<1> statement("SELECT name FROM test WHERE name=? AND number=? AND value=?", database); - auto value = statement.value("bar", "blah", 1); + auto value = statement.optionalValue("bar", "blah", 1); ASSERT_THAT(value.value(), Eq("bar")); } @@ -1091,7 +1094,7 @@ TEST_F(SqliteStatement, GetOptionalOutputValueAndMultipleQueryValue) ReadStatement<3> statement( "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); - auto value = statement.value("bar", "blah", 1); + auto value = statement.optionalValue("bar", "blah", 1); ASSERT_THAT(value.value(), Eq(Output{"bar", "blah", 1})); } @@ -1102,7 +1105,7 @@ TEST_F(SqliteStatement, GetOptionalTupleValueAndMultipleQueryValue) ReadStatement<3> statement( "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); - auto value = statement.value("bar", "blah", 1); + auto value = statement.optionalValue("bar", "blah", 1); ASSERT_THAT(value.value(), Eq(Tuple{"bar", "blah", 1})); } @@ -1113,7 +1116,7 @@ TEST_F(SqliteStatement, GetOptionalValueCallsReset) EXPECT_CALL(mockStatement, reset()); - mockStatement.value("bar"); + mockStatement.optionalValue("bar"); } TEST_F(SqliteStatement, GetOptionalValueCallsResetIfExceptionIsThrown) @@ -1123,6 +1126,56 @@ TEST_F(SqliteStatement, GetOptionalValueCallsResetIfExceptionIsThrown) EXPECT_CALL(mockStatement, reset()); + EXPECT_THROW(mockStatement.optionalValue("bar"), Sqlite::StatementHasError); +} + +TEST_F(SqliteStatement, GetSingleValueAndMultipleQueryValue) +{ + ReadStatement<1> statement("SELECT name FROM test WHERE name=? AND number=? AND value=?", + database); + + auto value = statement.value("bar", "blah", 1); + + ASSERT_THAT(value, Eq("bar")); +} + +TEST_F(SqliteStatement, GetOutputValueAndMultipleQueryValue) +{ + ReadStatement<3> statement( + "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); + + auto value = statement.value("bar", "blah", 1); + + ASSERT_THAT(value, Eq(Output{"bar", "blah", 1})); +} + +TEST_F(SqliteStatement, GetTupleValueAndMultipleQueryValue) +{ + using Tuple = std::tuple; + ReadStatement<3> statement( + "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); + + auto value = statement.value("bar", "blah", 1); + + ASSERT_THAT(value, Eq(Tuple{"bar", "blah", 1})); +} + +TEST_F(SqliteStatement, GetValueCallsReset) +{ + MockSqliteStatement mockStatement{databaseMock}; + + EXPECT_CALL(mockStatement, reset()); + + mockStatement.value("bar"); +} + +TEST_F(SqliteStatement, GetValueCallsResetIfExceptionIsThrown) +{ + MockSqliteStatement mockStatement{databaseMock}; + ON_CALL(mockStatement, next()).WillByDefault(Throw(Sqlite::StatementHasError(""))); + + EXPECT_CALL(mockStatement, reset()); + EXPECT_THROW(mockStatement.value("bar"), Sqlite::StatementHasError); } @@ -1402,6 +1455,19 @@ TEST_F(SqliteStatement, ReadStatementValueWithTransactions) auto value = statement.valueWithTransaction("bar", "blah"); + ASSERT_THAT(value, Eq(Tuple{"bar", "blah", 1})); + database.lock(); +} + +TEST_F(SqliteStatement, ReadStatementOptionalValueWithTransactions) +{ + using Tuple = std::tuple; + ReadStatement<3> statement("SELECT name, number, value FROM test WHERE name=? AND number=?", + database); + database.unlock(); + + auto value = statement.optionalValueWithTransaction("bar", "blah"); + ASSERT_THAT(*value, Eq(Tuple{"bar", "blah", 1})); database.lock(); } @@ -1456,6 +1522,19 @@ TEST_F(SqliteStatement, ReadWriteStatementValueWithTransactions) auto value = statement.valueWithTransaction("bar", "blah"); + ASSERT_THAT(value, Eq(Tuple{"bar", "blah", 1})); + database.lock(); +} + +TEST_F(SqliteStatement, ReadWriteStatementOptionalValueWithTransactions) +{ + using Tuple = std::tuple; + ReadWriteStatement<3> statement( + "SELECT name, number, value FROM test WHERE name=? AND number=?", database); + database.unlock(); + + auto value = statement.optionalValueWithTransaction("bar", "blah"); + ASSERT_THAT(*value, Eq(Tuple{"bar", "blah", 1})); database.lock(); }