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;
};
template <> SQLITE_EXPORT int BaseStatement::fetchValue<int>(int column) const;
template <> SQLITE_EXPORT long BaseStatement::fetchValue<long>(int column) const;
template <> SQLITE_EXPORT long long BaseStatement::fetchValue<long long>(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<>
SQLITE_EXPORT int BaseStatement::fetchValue<int>(int column) const;
template<>
SQLITE_EXPORT long BaseStatement::fetchValue<long>(int column) const;
template<>
SQLITE_EXPORT long long BaseStatement::fetchValue<long long>(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>
class StatementImplementation : public BaseStatement
@@ -207,12 +214,15 @@ public:
template<typename T>
struct is_container : std::false_type
{};
template<typename... Args>
struct is_container<std::vector<Args...>> : std::true_type
{};
template<typename... Args>
struct is_container<QList<Args...>> : std::true_type
{};
template<typename T, qsizetype Prealloc>
struct is_container<QVarLengthArray<T, Prealloc>> : 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<ResultType>(); }
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<ResultType>(); }
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<decltype(sqliteHighLevelCategory())>;
@@ -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<typename ConversionType, typename = std::enable_if_t<ConversionType::IsBasicId::value>>
@@ -694,8 +736,7 @@ private:
template<typename Callable, int... ColumnIndices>
CallbackControl callCallable(Callable &&callable, std::integer_sequence<int, ColumnIndices...>)
{
return invokeCallable(std::forward<Callable>(callable),
ValueGetter(*this, ColumnIndices)...);
return invokeCallable(std::forward<Callable>(callable), ValueGetter(*this, ColumnIndices)...);
}
template<typename Callable>

View File

@@ -2373,10 +2373,8 @@ SourceIds ProjectStorage::filterSourceIdsWithoutType(const SourceIds &updatedSou
SourceIds sourceIdsWithoutTypeSourceIds;
sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size());
std::set_difference(updatedSourceIds.begin(),
updatedSourceIds.end(),
sourceIdsOfTypes.begin(),
sourceIdsOfTypes.end(),
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>(typeId);
auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) {
auto isBasedOn = std::ranges::any_of(range, [&](TypeId currentTypeId) {
return ((currentTypeId == baseTypeIds) || ...);
});

View File

@@ -113,7 +113,10 @@ protected:
template<typename 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:
@@ -909,8 +912,7 @@ TEST_F(SqliteStatement, get_single_range_without_arguments)
{
ReadStatement<1> statement("SELECT name FROM test", database);
auto range = statement.range<Utils::SmallStringView>();
std::vector<Utils::SmallString> values{range.begin(), range.end()};
auto values = toValues(statement.range<Utils::SmallString>());
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<FooValue>();
std::vector<FooValue> values{range.begin(), range.end()};
auto values = toValues(statement.range<FooValue>());
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<Output>();
std::vector<Output> values{range.begin(), range.end()};
auto values = toValues(statement.range<Output>());
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<Utils::SmallString, 3>(40);
auto range = statement.range<Utils::SmallStringView>(40);
std::vector<Utils::SmallString> values{range.begin(), range.end()};
auto values = toValues(statement.range<Utils::SmallString>(40));
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);
database.unlock();
std::vector<Utils::SmallString> values = toValues(
statement.rangeWithTransaction<Utils::SmallString>(40));
auto values = toValues(statement.rangeWithTransaction<Utils::SmallString>(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<Tuple>("bar", "blah", 1);
std::vector<Tuple> values{range.begin(), range.end()};
auto values = toValues(statement.range<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=?",
database);
{
auto range = statement.range<Tuple>("bar", "blah");
std::vector<Tuple> values1{range.begin(), range.end()};
auto values1 = toValues(statement.range<Tuple>("bar", "blah"));
}
auto range2 = statement.range<Tuple>("bar", "blah");
std::vector<Tuple> values{range2.begin(), range2.end()};
auto values = toValues(statement.range<Tuple>("bar", "blah"));
ASSERT_THAT(values, ElementsAre(Tuple{"bar", "blah", 1}));
}