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 <thomas.hartmann@qt.io>
This commit is contained in:
Marco Bubke
2025-02-07 00:30:44 +01:00
parent 50b2ff3c50
commit 2c0d5b3f7a
3 changed files with 95 additions and 61 deletions

View File

@@ -140,13 +140,20 @@ private:
Database &m_database; Database &m_database;
}; };
template <> SQLITE_EXPORT int BaseStatement::fetchValue<int>(int column) const; template<>
template <> SQLITE_EXPORT long BaseStatement::fetchValue<long>(int column) const; SQLITE_EXPORT int BaseStatement::fetchValue<int>(int column) const;
template <> SQLITE_EXPORT long long BaseStatement::fetchValue<long long>(int column) const; template<>
template <> SQLITE_EXPORT double BaseStatement::fetchValue<double>(int column) const; SQLITE_EXPORT long BaseStatement::fetchValue<long>(int column) const;
extern template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(int column) const; template<>
extern template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue<Utils::SmallString>(int column) const; SQLITE_EXPORT long long BaseStatement::fetchValue<long long>(int column) const;
extern template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathString>(int column) const; template<>
SQLITE_EXPORT double BaseStatement::fetchValue<double>(int column) const;
extern template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(
int column) const;
extern template SQLITE_EXPORT Utils::SmallString BaseStatement::fetchValue<Utils::SmallString>(
int column) const;
extern template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathString>(
int column) const;
template<typename BaseStatement, int ResultCount, int BindParameterCount> template<typename BaseStatement, int ResultCount, int BindParameterCount>
class StatementImplementation : public BaseStatement class StatementImplementation : public BaseStatement
@@ -207,12 +214,15 @@ public:
template<typename T> template<typename T>
struct is_container : std::false_type struct is_container : std::false_type
{}; {};
template<typename... Args> template<typename... Args>
struct is_container<std::vector<Args...>> : std::true_type struct is_container<std::vector<Args...>> : std::true_type
{}; {};
template<typename... Args> template<typename... Args>
struct is_container<QList<Args...>> : std::true_type struct is_container<QList<Args...>> : std::true_type
{}; {};
template<typename T, qsizetype Prealloc> template<typename T, qsizetype Prealloc>
struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
{}; {};
@@ -436,6 +446,9 @@ public:
class BaseSqliteResultRange class BaseSqliteResultRange
{ {
public: public:
class SqliteResultSentinel
{};
class SqliteResultIteratator class SqliteResultIteratator
{ {
public: public:
@@ -447,44 +460,58 @@ public:
SqliteResultIteratator(StatementImplementation &statement, SqliteResultIteratator(StatementImplementation &statement,
const source_location &sourceLocation) const source_location &sourceLocation)
: m_statement{statement} : m_statement{&statement}
, m_sourceLocation{sourceLocation} , m_sourceLocation{&sourceLocation}
, m_hasNext{m_statement.next(sourceLocation)} , m_hasNext{m_statement->next(sourceLocation)}
{} {}
SqliteResultIteratator(StatementImplementation &statement, SqliteResultIteratator(StatementImplementation &statement,
const source_location &sourceLocation, const source_location &sourceLocation,
bool hasNext) bool hasNext)
: m_statement{statement} : m_statement{&statement}
, m_sourceLocation{sourceLocation} , m_sourceLocation{&sourceLocation}
, m_hasNext{hasNext} , 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++() SqliteResultIteratator &operator++()
{ {
m_hasNext = m_statement.next(m_sourceLocation); m_hasNext = m_statement->next(*m_sourceLocation);
return *this; return *this;
} }
void operator++(int) { m_hasNext = m_statement.next(m_sourceLocation); } friend bool operator==(const SqliteResultIteratator &first, SqliteResultSentinel)
friend bool operator==(const SqliteResultIteratator &first,
const SqliteResultIteratator &second)
{ {
return first.m_hasNext == second.m_hasNext; return !first.m_hasNext;
} }
friend bool operator!=(const SqliteResultIteratator &first, friend bool operator!=(const SqliteResultIteratator &first, SqliteResultSentinel)
const SqliteResultIteratator &second)
{ {
return !(first == second); return first.m_hasNext;
} }
value_type operator*() const { return m_statement.createValue<ResultType>(); } void operator++(int) { m_hasNext = m_statement->next(*m_sourceLocation); }
private: value_type operator*() const { return m_statement->createValue<ResultType>(); }
StatementImplementation &m_statement;
const source_location &m_sourceLocation; public:
StatementImplementation *m_statement;
const source_location *m_sourceLocation;
bool m_hasNext = false; bool m_hasNext = false;
}; };
@@ -496,19 +523,20 @@ public:
BaseSqliteResultRange(StatementImplementation &statement, const source_location &sourceLocation) BaseSqliteResultRange(StatementImplementation &statement, const source_location &sourceLocation)
: m_statement{statement} : m_statement{statement}
, m_sourceLocation{sourceLocation} , m_sourceLocation{sourceLocation}
{ {}
}
BaseSqliteResultRange(BaseSqliteResultRange &) = delete; BaseSqliteResultRange(const BaseSqliteResultRange &) = default;
BaseSqliteResultRange &operator=(BaseSqliteResultRange &) = delete; BaseSqliteResultRange(BaseSqliteResultRange &&) = default;
BaseSqliteResultRange &operator=(BaseSqliteResultRange &&) = delete; BaseSqliteResultRange &operator=(const BaseSqliteResultRange &) = default;
BaseSqliteResultRange &operator=(BaseSqliteResultRange &&) = default;
iterator begin() & { return iterator{m_statement, m_sourceLocation}; } 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 begin() const & { return iterator{m_statement}; }
const_iterator end() const & { return iterator{m_statement, false}; }
auto end() const & { return SqliteResultSentinel{}; }
private: private:
using TracerCategory = std::decay_t<decltype(sqliteHighLevelCategory())>; using TracerCategory = std::decay_t<decltype(sqliteHighLevelCategory())>;
@@ -587,10 +615,17 @@ private:
Resetter(Resetter &) = delete; Resetter(Resetter &) = delete;
Resetter &operator=(Resetter &) = delete; Resetter &operator=(Resetter &) = delete;
Resetter(Resetter &&other) Resetter(Resetter &&other) noexcept
: statement{std::exchange(other.statement, nullptr)} : statement{std::exchange(other.statement, nullptr)}
{} {}
Resetter &operator=(Resetter &&other) noexcept
{
statement = std::exchange(other.statement, nullptr);
return *this;
}
void reset() noexcept void reset() noexcept
{ {
if (statement) if (statement)
@@ -601,7 +636,7 @@ private:
~Resetter() noexcept { reset(); } ~Resetter() noexcept { reset(); }
StatementImplementation *statement; StatementImplementation *statement = nullptr;
}; };
struct ValueGetter struct ValueGetter
@@ -612,12 +647,19 @@ private:
{} {}
explicit operator bool() const { return statement.fetchIntValue(column); } explicit operator bool() const { return statement.fetchIntValue(column); }
operator int() const { return statement.fetchIntValue(column); } operator int() const { return statement.fetchIntValue(column); }
operator long() const { return statement.fetchLongValue(column); } operator long() const { return statement.fetchLongValue(column); }
operator long long() const { return statement.fetchLongLongValue(column); } operator long long() const { return statement.fetchLongLongValue(column); }
operator double() const { return statement.fetchDoubleValue(column); } operator double() const { return statement.fetchDoubleValue(column); }
operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); } operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); }
operator BlobView() { return statement.fetchBlobValue(column); } operator BlobView() { return statement.fetchBlobValue(column); }
operator ValueView() { return statement.fetchValueView(column); } operator ValueView() { return statement.fetchValueView(column); }
template<typename ConversionType, typename = std::enable_if_t<ConversionType::IsBasicId::value>> template<typename ConversionType, typename = std::enable_if_t<ConversionType::IsBasicId::value>>
@@ -694,8 +736,7 @@ private:
template<typename Callable, int... ColumnIndices> template<typename Callable, int... ColumnIndices>
CallbackControl callCallable(Callable &&callable, std::integer_sequence<int, ColumnIndices...>) CallbackControl callCallable(Callable &&callable, std::integer_sequence<int, ColumnIndices...>)
{ {
return invokeCallable(std::forward<Callable>(callable), return invokeCallable(std::forward<Callable>(callable), ValueGetter(*this, ColumnIndices)...);
ValueGetter(*this, ColumnIndices)...);
} }
template<typename Callable> template<typename Callable>

View File

@@ -2373,11 +2373,9 @@ SourceIds ProjectStorage::filterSourceIdsWithoutType(const SourceIds &updatedSou
SourceIds sourceIdsWithoutTypeSourceIds; SourceIds sourceIdsWithoutTypeSourceIds;
sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size()); sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size());
std::set_difference(updatedSourceIds.begin(), std::ranges::set_difference(updatedSourceIds,
updatedSourceIds.end(), sourceIdsOfTypes,
sourceIdsOfTypes.begin(), std::back_inserter(sourceIdsWithoutTypeSourceIds));
sourceIdsOfTypes.end(),
std::back_inserter(sourceIdsWithoutTypeSourceIds));
return sourceIdsWithoutTypeSourceIds; return sourceIdsWithoutTypeSourceIds;
} }
@@ -2395,8 +2393,8 @@ TypeIds ProjectStorage::fetchTypeIds(const SourceIds &sourceIds)
void ProjectStorage::unique(SourceIds &sourceIds) void ProjectStorage::unique(SourceIds &sourceIds)
{ {
std::ranges::sort(sourceIds); std::ranges::sort(sourceIds);
auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); auto removed = std::ranges::unique(sourceIds);
sourceIds.erase(newEnd, sourceIds.end()); sourceIds.erase(removed.begin(), removed.end());
} }
void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) 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>(typeId); auto range = s->selectPrototypeAndExtensionIdsStatement.rangeWithTransaction<TypeId>(typeId);
auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { auto isBasedOn = std::ranges::any_of(range, [&](TypeId currentTypeId) {
return ((currentTypeId == baseTypeIds) || ...); return ((currentTypeId == baseTypeIds) || ...);
}); });

View File

@@ -113,7 +113,10 @@ protected:
template<typename Range> template<typename Range>
static auto toValues(Range &&range) static auto toValues(Range &&range)
{ {
return std::vector<typename Range::value_type>{range.begin(), range.end()}; std::vector<typename Range::value_type> values;
for (auto &&elem : range)
values.push_back(std::move(elem));
return values;
} }
protected: protected:
@@ -909,8 +912,7 @@ TEST_F(SqliteStatement, get_single_range_without_arguments)
{ {
ReadStatement<1> statement("SELECT name FROM test", database); ReadStatement<1> statement("SELECT name FROM test", database);
auto range = statement.range<Utils::SmallStringView>(); auto values = toValues(statement.range<Utils::SmallString>());
std::vector<Utils::SmallString> values{range.begin(), range.end()};
ASSERT_THAT(values, UnorderedElementsAre("bar", "foo", "poo")); 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); ReadStatement<1> statement("SELECT number FROM test", database);
database.execute("INSERT INTO test VALUES (NULL, NULL, NULL)"); database.execute("INSERT INTO test VALUES (NULL, NULL, NULL)");
auto range = statement.range<FooValue>(); auto values = toValues(statement.range<FooValue>());
std::vector<FooValue> values{range.begin(), range.end()};
ASSERT_THAT(values, UnorderedElementsAre(Eq("blah"), Eq(23.3), Eq(40), IsNull())); 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); ReadStatement<3> statement("SELECT name, number, value FROM test", database);
auto range = statement.range<Output>(); auto values = toValues(statement.range<Output>());
std::vector<Output> values{range.begin(), range.end()};
ASSERT_THAT(values, ASSERT_THAT(values,
UnorderedElementsAre(Output{"bar", "blah", 1}, 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); ReadStatement<1, 1> statement("SELECT name FROM test WHERE number=?", database);
statement.values<Utils::SmallString, 3>(40); statement.values<Utils::SmallString, 3>(40);
auto range = statement.range<Utils::SmallStringView>(40); auto values = toValues(statement.range<Utils::SmallString>(40));
std::vector<Utils::SmallString> values{range.begin(), range.end()};
ASSERT_THAT(values, ElementsAre("poo")); ASSERT_THAT(values, ElementsAre("poo"));
} }
@@ -1044,8 +1043,7 @@ TEST_F(SqliteStatement, get_range_with_transaction_for_single_output_with_bindin
statement.values<Utils::SmallString, 3>(40); statement.values<Utils::SmallString, 3>(40);
database.unlock(); database.unlock();
std::vector<Utils::SmallString> values = toValues( auto values = toValues(statement.rangeWithTransaction<Utils::SmallString>(40));
statement.rangeWithTransaction<Utils::SmallString>(40));
ASSERT_THAT(values, ElementsAre("poo")); ASSERT_THAT(values, ElementsAre("poo"));
database.lock(); database.lock();
@@ -1068,8 +1066,7 @@ TEST_F(SqliteStatement, get_range_for_multiple_output_values_and_multiple_query_
ReadStatement<3, 3> statement( ReadStatement<3, 3> statement(
"SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database); "SELECT name, number, value FROM test WHERE name=? AND number=? AND value=?", database);
auto range = statement.range<Tuple>("bar", "blah", 1); auto values = toValues(statement.range<Tuple>("bar", "blah", 1));
std::vector<Tuple> values{range.begin(), range.end()};
ASSERT_THAT(values, ElementsAre(Tuple{"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=?", ReadStatement<3, 2> statement("SELECT name, number, value FROM test WHERE name=? AND number=?",
database); database);
{ {
auto range = statement.range<Tuple>("bar", "blah"); auto values1 = toValues(statement.range<Tuple>("bar", "blah"));
std::vector<Tuple> values1{range.begin(), range.end()};
} }
auto range2 = statement.range<Tuple>("bar", "blah"); auto values = toValues(statement.range<Tuple>("bar", "blah"));
std::vector<Tuple> values{range2.begin(), range2.end()};
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1})); ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
} }