forked from qt-creator/qt-creator
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:
@@ -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>
|
||||||
|
@@ -2373,10 +2373,8 @@ 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(),
|
|
||||||
sourceIdsOfTypes.end(),
|
|
||||||
std::back_inserter(sourceIdsWithoutTypeSourceIds));
|
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) || ...);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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}));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user