From 2c0d5b3f7a3c336ca881a4ef3608cf272afbdeaa Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 7 Feb 2025 00:30:44 +0100 Subject: [PATCH] Sqlite: Make sqlite range usable for std::ranges Adding a sentinel and making the iterator copyable makes it possible to use it as a range in std::views. Change-Id: I100edb39b601a2e02fd424115e362c67f2de39f1 Reviewed-by: Thomas Hartmann --- src/libs/sqlite/sqlitebasestatement.h | 113 ++++++++++++------ .../projectstorage/projectstorage.cpp | 14 +-- .../unittests/sqlite/sqlitestatement-test.cpp | 29 ++--- 3 files changed, 95 insertions(+), 61 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 7d73385d95f..1010a1ae1f2 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -140,13 +140,20 @@ private: Database &m_database; }; -template <> SQLITE_EXPORT int BaseStatement::fetchValue(int column) const; -template <> SQLITE_EXPORT long BaseStatement::fetchValue(int column) const; -template <> SQLITE_EXPORT long long BaseStatement::fetchValue(int column) const; -template <> SQLITE_EXPORT double BaseStatement::fetchValue(int column) const; -extern template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue(int column) const; -extern template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue(int column) const; -extern template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue(int column) const; +template<> +SQLITE_EXPORT int BaseStatement::fetchValue(int column) const; +template<> +SQLITE_EXPORT long BaseStatement::fetchValue(int column) const; +template<> +SQLITE_EXPORT long long BaseStatement::fetchValue(int column) const; +template<> +SQLITE_EXPORT double BaseStatement::fetchValue(int column) const; +extern template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue( + int column) const; +extern template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue( + int column) const; +extern template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue( + int column) const; template class StatementImplementation : public BaseStatement @@ -207,12 +214,15 @@ public: template struct is_container : std::false_type {}; + template struct is_container> : std::true_type {}; + template struct is_container> : std::true_type {}; + template struct is_container> : std::true_type {}; @@ -436,6 +446,9 @@ public: class BaseSqliteResultRange { public: + class SqliteResultSentinel + {}; + class SqliteResultIteratator { public: @@ -447,44 +460,58 @@ public: SqliteResultIteratator(StatementImplementation &statement, const source_location &sourceLocation) - : m_statement{statement} - , m_sourceLocation{sourceLocation} - , m_hasNext{m_statement.next(sourceLocation)} + : m_statement{&statement} + , m_sourceLocation{&sourceLocation} + , m_hasNext{m_statement->next(sourceLocation)} {} SqliteResultIteratator(StatementImplementation &statement, const source_location &sourceLocation, bool hasNext) - : m_statement{statement} - , m_sourceLocation{sourceLocation} + : m_statement{&statement} + , m_sourceLocation{&sourceLocation} , m_hasNext{hasNext} {} + SqliteResultIteratator(const SqliteResultIteratator &) = delete; + SqliteResultIteratator &operator=(const SqliteResultIteratator &) = delete; + + SqliteResultIteratator(SqliteResultIteratator &&other) noexcept + : m_statement{other.m_statement} + , m_sourceLocation{other.m_sourceLocation} + , m_hasNext{std::exchange(other.m_hasNext, false)} + {} + + SqliteResultIteratator &operator=(SqliteResultIteratator &&other) noexcept + { + m_statement = other.m_statement; + m_sourceLocation = other.m_sourceLocation; + m_hasNext = std::exchange(other.m_hasNext, false); + } + SqliteResultIteratator &operator++() { - m_hasNext = m_statement.next(m_sourceLocation); + m_hasNext = m_statement->next(*m_sourceLocation); return *this; } - void operator++(int) { m_hasNext = m_statement.next(m_sourceLocation); } - - friend bool operator==(const SqliteResultIteratator &first, - const SqliteResultIteratator &second) + friend bool operator==(const SqliteResultIteratator &first, SqliteResultSentinel) { - return first.m_hasNext == second.m_hasNext; + return !first.m_hasNext; } - friend bool operator!=(const SqliteResultIteratator &first, - const SqliteResultIteratator &second) + friend bool operator!=(const SqliteResultIteratator &first, SqliteResultSentinel) { - return !(first == second); + return first.m_hasNext; } - value_type operator*() const { return m_statement.createValue(); } + void operator++(int) { m_hasNext = m_statement->next(*m_sourceLocation); } - private: - StatementImplementation &m_statement; - const source_location &m_sourceLocation; + value_type operator*() const { return m_statement->createValue(); } + + public: + StatementImplementation *m_statement; + const source_location *m_sourceLocation; bool m_hasNext = false; }; @@ -496,19 +523,20 @@ public: BaseSqliteResultRange(StatementImplementation &statement, const source_location &sourceLocation) : m_statement{statement} , m_sourceLocation{sourceLocation} - { - } + {} - BaseSqliteResultRange(BaseSqliteResultRange &) = delete; - BaseSqliteResultRange &operator=(BaseSqliteResultRange &) = delete; - BaseSqliteResultRange &operator=(BaseSqliteResultRange &&) = delete; + BaseSqliteResultRange(const BaseSqliteResultRange &) = default; + BaseSqliteResultRange(BaseSqliteResultRange &&) = default; + BaseSqliteResultRange &operator=(const BaseSqliteResultRange &) = default; + BaseSqliteResultRange &operator=(BaseSqliteResultRange &&) = default; iterator begin() & { return iterator{m_statement, m_sourceLocation}; } - iterator end() & { return iterator{m_statement, m_sourceLocation, false}; } + auto end() & { return SqliteResultSentinel{}; } const_iterator begin() const & { return iterator{m_statement}; } - const_iterator end() const & { return iterator{m_statement, false}; } + + auto end() const & { return SqliteResultSentinel{}; } private: using TracerCategory = std::decay_t; @@ -587,10 +615,17 @@ private: Resetter(Resetter &) = delete; Resetter &operator=(Resetter &) = delete; - Resetter(Resetter &&other) + Resetter(Resetter &&other) noexcept : statement{std::exchange(other.statement, nullptr)} {} + Resetter &operator=(Resetter &&other) noexcept + { + statement = std::exchange(other.statement, nullptr); + + return *this; + } + void reset() noexcept { if (statement) @@ -601,7 +636,7 @@ private: ~Resetter() noexcept { reset(); } - StatementImplementation *statement; + StatementImplementation *statement = nullptr; }; struct ValueGetter @@ -612,12 +647,19 @@ private: {} explicit operator bool() const { return statement.fetchIntValue(column); } + operator int() const { return statement.fetchIntValue(column); } + operator long() const { return statement.fetchLongValue(column); } + operator long long() const { return statement.fetchLongLongValue(column); } + operator double() const { return statement.fetchDoubleValue(column); } + operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); } + operator BlobView() { return statement.fetchBlobValue(column); } + operator ValueView() { return statement.fetchValueView(column); } template> @@ -694,8 +736,7 @@ private: template CallbackControl callCallable(Callable &&callable, std::integer_sequence) { - return invokeCallable(std::forward(callable), - ValueGetter(*this, ColumnIndices)...); + return invokeCallable(std::forward(callable), ValueGetter(*this, ColumnIndices)...); } template diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp index b758153351d..9e12164c6f6 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp @@ -2373,11 +2373,9 @@ SourceIds ProjectStorage::filterSourceIdsWithoutType(const SourceIds &updatedSou SourceIds sourceIdsWithoutTypeSourceIds; sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size()); - std::set_difference(updatedSourceIds.begin(), - updatedSourceIds.end(), - sourceIdsOfTypes.begin(), - sourceIdsOfTypes.end(), - std::back_inserter(sourceIdsWithoutTypeSourceIds)); + std::ranges::set_difference(updatedSourceIds, + sourceIdsOfTypes, + std::back_inserter(sourceIdsWithoutTypeSourceIds)); return sourceIdsWithoutTypeSourceIds; } @@ -2395,8 +2393,8 @@ TypeIds ProjectStorage::fetchTypeIds(const SourceIds &sourceIds) void ProjectStorage::unique(SourceIds &sourceIds) { std::ranges::sort(sourceIds); - auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); - sourceIds.erase(newEnd, sourceIds.end()); + auto removed = std::ranges::unique(sourceIds); + sourceIds.erase(removed.begin(), removed.end()); } void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) @@ -4847,7 +4845,7 @@ bool ProjectStorage::isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const auto range = s->selectPrototypeAndExtensionIdsStatement.rangeWithTransaction(typeId); - auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { + auto isBasedOn = std::ranges::any_of(range, [&](TypeId currentTypeId) { return ((currentTypeId == baseTypeIds) || ...); }); diff --git a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp index 4e4616192b6..8d7a551ac32 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp @@ -113,7 +113,10 @@ protected: template static auto toValues(Range &&range) { - return std::vector{range.begin(), range.end()}; + std::vector values; + for (auto &&elem : range) + values.push_back(std::move(elem)); + return values; } protected: @@ -909,8 +912,7 @@ TEST_F(SqliteStatement, get_single_range_without_arguments) { ReadStatement<1> statement("SELECT name FROM test", database); - auto range = statement.range(); - std::vector values{range.begin(), range.end()}; + auto values = toValues(statement.range()); ASSERT_THAT(values, UnorderedElementsAre("bar", "foo", "poo")); } @@ -960,8 +962,7 @@ TEST_F(SqliteStatement, get_single_sqlite_range_without_arguments) ReadStatement<1> statement("SELECT number FROM test", database); database.execute("INSERT INTO test VALUES (NULL, NULL, NULL)"); - auto range = statement.range(); - std::vector values{range.begin(), range.end()}; + auto values = toValues(statement.range()); ASSERT_THAT(values, UnorderedElementsAre(Eq("blah"), Eq(23.3), Eq(40), IsNull())); } @@ -994,8 +995,7 @@ TEST_F(SqliteStatement, get_struct_range_without_arguments) { ReadStatement<3> statement("SELECT name, number, value FROM test", database); - auto range = statement.range(); - std::vector values{range.begin(), range.end()}; + auto values = toValues(statement.range()); ASSERT_THAT(values, UnorderedElementsAre(Output{"bar", "blah", 1}, @@ -1032,8 +1032,7 @@ TEST_F(SqliteStatement, get_range_for_single_output_with_binding_multiple_times) ReadStatement<1, 1> statement("SELECT name FROM test WHERE number=?", database); statement.values(40); - auto range = statement.range(40); - std::vector values{range.begin(), range.end()}; + auto values = toValues(statement.range(40)); ASSERT_THAT(values, ElementsAre("poo")); } @@ -1044,8 +1043,7 @@ TEST_F(SqliteStatement, get_range_with_transaction_for_single_output_with_bindin statement.values(40); database.unlock(); - std::vector values = toValues( - statement.rangeWithTransaction(40)); + auto values = toValues(statement.rangeWithTransaction(40)); ASSERT_THAT(values, ElementsAre("poo")); database.lock(); @@ -1068,8 +1066,7 @@ TEST_F(SqliteStatement, get_range_for_multiple_output_values_and_multiple_query_ ReadStatement<3, 3> statement( "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); - auto range = statement.range("bar", "blah", 1); - std::vector values{range.begin(), range.end()}; + auto values = toValues(statement.range("bar", "blah", 1)); ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); } @@ -1105,12 +1102,10 @@ TEST_F(SqliteStatement, call_get_range_for_multiple_output_values_and_multiple_q ReadStatement<3, 2> statement("SELECT name, number, value FROM test WHERE name=? AND number=?", database); { - auto range = statement.range("bar", "blah"); - std::vector values1{range.begin(), range.end()}; + auto values1 = toValues(statement.range("bar", "blah")); } - auto range2 = statement.range("bar", "blah"); - std::vector values{range2.begin(), range2.end()}; + auto values = toValues(statement.range("bar", "blah")); ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); }