Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dev/functional/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
#define SQLITE_ORM_CPP20_RANGES_SUPPORTED
#endif

#if __cpp_lib_generator >= 202207L
#define SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
#endif

#ifdef SQLITE_ORM_STATIC_CALL_OPERATOR_SUPPORTED
#define SQLITE_ORM_STATIC_CALLOP static
#define SQLITE_ORM_OR_CONST_CALLOP
Expand Down
60 changes: 60 additions & 0 deletions dev/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#include <tuple> // std::tuple_size, std::tuple, std::make_tuple, std::tie
#include <utility> // std::forward, std::pair
#include <algorithm> // std::for_each, std::ranges::for_each
#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
#include <generator>
#endif
#endif
#include "functional/cxx_optional.h"

Expand Down Expand Up @@ -328,6 +331,63 @@ namespace sqlite_orm {
return {this->db_objects, std::move(connection), std::move(expression)};
}
#endif
#endif

#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
/*
* Iterate over objects of a type mapped as a table, lazily fetched from a result set in a coroutine.
*/
template<class T, class O = mapped_type_proxy_t<T>, class... Args>
std::generator<O> yield(Args&&... args) {
this->assert_mapped_type<O>();
// implementation note: instead of using `this->iterate<O>()` we iterate over a select statement,
// because a `mapped_view` has a legacy input iterator that returns a reference to an object.
// For a generator we want to yield objects by value that can be moved from.
for (O obj:
this->iterate(sqlite_orm::select(struct_<O>(asterisk<T>(true)), std::forward<Args>(args)...))) {
co_yield obj;
}
}

/*
* Iterate over objects of a type mapped as a table, lazily fetched from a result set in a coroutine.
*/
template<orm_refers_to_table auto mapped, class O = mapped_type_proxy_t<decltype(mapped)>, class... Args>
std::generator<O> yield(Args&&... args) {
this->assert_mapped_type<O>();
// implementation note: instead of using `this->iterate<O>()` we iterate over a select statement,
// because a `mapped_view` has a legacy input iterator that returns a reference to an object.
// For a generator we want to yield objects by value that can be moved from.
for (O obj: this->iterate(sqlite_orm::select(struct_<O>(asterisk<decltype(mapped)>(true)),
std::forward<Args>(args)...))) {
co_yield obj;
}
}

/*
* Iterate over a result set of a select statement in a coroutine.
*/
template<class Select>
requires (is_select_v<Select>)
auto yield(Select expression) -> std::generator<decltype(*this->iterate(std::move(expression)).begin())> {
for (auto row: this->iterate(std::move(expression))) {
co_yield row;
}
}

#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
/*
* Iterate over a result set of a select statement involving a common table expression in a coroutine.
*/
template<class... CTEs, class E>
requires (is_select_v<E>)
auto yield(with_t<E, CTEs...> expression)
-> std::generator<decltype(*this->iterate(std::move(expression)).begin())> {
for (auto row: this->iterate(std::move(expression))) {
co_yield row;
}
}
#endif
#endif

/**
Expand Down
64 changes: 64 additions & 0 deletions include/sqlite_orm/sqlite_orm.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ using std::nullptr_t;
#define SQLITE_ORM_CPP20_RANGES_SUPPORTED
#endif

#if __cpp_lib_generator >= 202207L
#define SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
#endif

#ifdef SQLITE_ORM_STATIC_CALL_OPERATOR_SUPPORTED
#define SQLITE_ORM_STATIC_CALLOP static
#define SQLITE_ORM_OR_CONST_CALLOP
Expand Down Expand Up @@ -325,6 +329,9 @@ namespace sqlite_orm {
#include <tuple> // std::tuple_size, std::tuple, std::make_tuple, std::tie
#include <utility> // std::forward, std::pair
#include <algorithm> // std::for_each, std::ranges::for_each
#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
#include <generator>
#endif
#endif
// #include "functional/cxx_optional.h"

Expand Down Expand Up @@ -23437,6 +23444,63 @@ namespace sqlite_orm {
return {this->db_objects, std::move(connection), std::move(expression)};
}
#endif
#endif

#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
/*
* Iterate over objects of a type mapped as a table, lazily fetched from a result set in a coroutine.
*/
template<class T, class O = mapped_type_proxy_t<T>, class... Args>
std::generator<O> yield(Args&&... args) {
this->assert_mapped_type<O>();
// implementation note: instead of using `this->iterate<O>()` we iterate over a select statement,
// because a `mapped_view` has a legacy input iterator that returns a reference to an object.
// For a generator we want to yield objects by value that can be moved from.
for (O obj:
this->iterate(sqlite_orm::select(struct_<O>(asterisk<T>(true)), std::forward<Args>(args)...))) {
co_yield obj;
}
}

/*
* Iterate over objects of a type mapped as a table, lazily fetched from a result set in a coroutine.
*/
template<orm_refers_to_table auto mapped, class O = mapped_type_proxy_t<decltype(mapped)>, class... Args>
std::generator<O> yield(Args&&... args) {
this->assert_mapped_type<O>();
// implementation note: instead of using `this->iterate<O>()` we iterate over a select statement,
// because a `mapped_view` has a legacy input iterator that returns a reference to an object.
// For a generator we want to yield objects by value that can be moved from.
for (O obj: this->iterate(sqlite_orm::select(struct_<O>(asterisk<decltype(mapped)>(true)),
std::forward<Args>(args)...))) {
co_yield obj;
}
}

/*
* Iterate over a result set of a select statement in a coroutine.
*/
template<class Select>
requires (is_select_v<Select>)
auto yield(Select expression) -> std::generator<decltype(*this->iterate(std::move(expression)).begin())> {
for (auto row: this->iterate(std::move(expression))) {
co_yield row;
}
}

#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
/*
* Iterate over a result set of a select statement involving a common table expression in a coroutine.
*/
template<class... CTEs, class E>
requires (is_select_v<E>)
auto yield(with_t<E, CTEs...> expression)
-> std::generator<decltype(*this->iterate(std::move(expression)).begin())> {
for (auto row: this->iterate(std::move(expression))) {
co_yield row;
}
}
#endif
#endif

/**
Expand Down
40 changes: 36 additions & 4 deletions tests/iterate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ TEST_CASE("Iterate select statement") {

bool operator==(const Test&) const = default;
};
#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED
constexpr orm_table_reference auto test_table = c<Test>();
constexpr orm_table_alias auto test_alias = "t"_alias.for_<Test>();
#endif

auto db =
make_storage("",
Expand Down Expand Up @@ -105,10 +109,38 @@ TEST_CASE("Iterate select statement") {

#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
constexpr auto x = "x"_cte;
std::input_iterator auto begin =
db.iterate(with(x().as(select(asterisk<Test>())), select(struct_<Test>(asterisk<x>())))).begin();
REQUIRE(*begin == expected);
SECTION("with") {
constexpr auto x = "x"_cte;
std::input_iterator auto begin =
db.iterate(with(x().as(select(asterisk<Test>())), select(struct_<Test>(asterisk<x>())))).begin();
REQUIRE(*begin == expected);
}
#endif
#endif

#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
SECTION("object generator, classic") {
auto view = db.yield<Test>();
REQUIRE(std::vector<Test>{std::from_range, view} == expected_vec);
}
SECTION("object generator, table reference") {
auto view = db.yield<test_table>();
REQUIRE(std::vector<Test>{std::from_range, view} == expected_vec);
}
SECTION("object generator, alias") {
auto view = db.yield<test_alias>();
REQUIRE(std::vector<Test>{std::from_range, view} == expected_vec);
}
SECTION("select generator") {
auto view = db.yield(select(object<Test>()));
REQUIRE(std::vector<Test>{std::from_range, view} == expected_vec);
}
#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
SECTION("with generator") {
constexpr auto x = "x"_cte;
auto view = db.yield(with(x().as(select(asterisk<Test>())), select(struct_<Test>(asterisk<x>()))));
REQUIRE(std::vector<Test>{std::from_range, view} == expected_vec);
}
#endif
#endif
}
Expand Down
42 changes: 42 additions & 0 deletions tests/static_tests/iterator_t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ concept storage_iterate_mapped = requires(S& storage_type) {
{ storage_type.template iterate<O>() } -> std::same_as<mapped_view<O, S>>;
{ storage_type.template iterate<O>() } -> can_view_mapped<O, DBOs>;
};

template<class S, auto mapped, class O, class DBOs = typename S::db_objects_type>
concept storage_iterate_mapped_ref = requires(S& storage_type) {
{ storage_type.template iterate<mapped>() } -> std::same_as<mapped_view<O, S>>;
{ storage_type.template iterate<mapped>() } -> can_view_mapped<O, DBOs>;
};
#endif

#if defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) && defined(SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED)
Expand Down Expand Up @@ -103,8 +109,29 @@ concept storage_iterate_result_set = requires(S& storage_type, Select select) {
};
#endif

#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
template<class S, class T, class O>
concept storage_yield_mapped = requires(S& storage_type) {
{ storage_type.template yield<T>() } -> std::same_as<std::generator<O>>;
};

template<class S, auto mapped, class O>
concept storage_yield_mapped_ref = requires(S& storage_type) {
{ storage_type.template yield<mapped>() } -> std::same_as<std::generator<O>>;
};

template<class S, class Select, class ColResult>
concept storage_yield_result_set = requires(S& storage_type, Select select) {
{ storage_type.yield(select) } -> std::same_as<std::generator<ColResult>>;
};
#endif

namespace {
struct Object {};
#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED
constexpr orm_table_alias auto object_alias = "o"_alias.for_<Object>();
constexpr orm_table_reference auto object_table = c<Object>();
#endif
}

TEST_CASE("can view and iterate mapped") {
Expand Down Expand Up @@ -152,6 +179,14 @@ TEST_CASE("can view and iterate mapped") {

#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED
STATIC_REQUIRE(storage_iterate_mapped<storage_type, Object>);
STATIC_REQUIRE(storage_iterate_mapped_ref<storage_type, object_table, Object>);
STATIC_REQUIRE(storage_iterate_mapped_ref<storage_type, object_alias, Object>);
#endif

#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
STATIC_REQUIRE(storage_yield_mapped<storage_type, Object, Object>);
STATIC_REQUIRE(storage_yield_mapped_ref<storage_type, object_table, Object>);
STATIC_REQUIRE(storage_yield_mapped_ref<storage_type, object_alias, Object>);
#endif
}

Expand Down Expand Up @@ -195,5 +230,12 @@ TEST_CASE("can view and iterate result set") {
STATIC_REQUIRE(storage_iterate_result_set<storage_type, decltype(with(x(i).as(select(1)), select(x->*i))), int>);
#endif
#endif

#ifdef SQLITE_ORM_CPP23_GENERATOR_SUPPORTED
STATIC_REQUIRE(storage_yield_result_set<empty_storage_type, decltype(select(42)), int>);
#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
STATIC_REQUIRE(storage_yield_result_set<storage_type, decltype(with(x(i).as(select(1)), select(x->*i))), int>);
#endif
#endif
}
#endif