Skip to content

Conversation

trueqbit
Copy link
Collaborator

@trueqbit trueqbit commented Jun 11, 2024

This PR introduces iteration over result sets of select statements (possibly with common table expressions).

auto db = make_storage("");
std::ranges::view auto int_view = db.iterate(select(1));
for (int i : int_view) {
    // ...
}

Technically speaking, the feature consists of:

  • A C++ view over a result set named internal::result_set_view.
    • Conforms to the standard concepts std::ranges::view, std::ranges::borrowed_range.
  • A C++ iterator over a result set named internal::result_set_iterator.
    • Conforms to the standard concept std::input_iterator.
  • std::default_sentinel_t is taken as a range sentinel.

The feature currently requires a C++20 compiler, but can also be used with implementations of the C++14 standard library.

Additional changes:

  • renamed internal::view_t to internal::mapped_view
  • renamed internal::iterator_t to internal::mapped_iterator

@trueqbit trueqbit linked an issue Jun 11, 2024 that may be closed by this pull request
@trueqbit trueqbit marked this pull request as ready for review June 13, 2024 13:09
@trueqbit trueqbit requested a review from fnc12 June 13, 2024 13:09
@trueqbit trueqbit requested a review from fnc12 June 16, 2024 19:06
@trueqbit trueqbit merged commit ca95210 into dev Jun 17, 2024
@trueqbit trueqbit deleted the iterate_result_set branch June 17, 2024 05:32
@juandent
Copy link
Contributor

What is the appropriate syntax for using iterate? iterate seems to need a Table type but that does not work for general selects!

    auto rows = storage.iterate(select(columns(&Doctor::id, &Doctor::name, &Visit::patientName, &Visit::vdate),
        left_join<Visit>(on(c(&Doctor::id) == &Visit::doctorId))));
    for( auto& row : rows)
    {
             auto id = get<0>(row);  // does not work!!
     }

@trevornagy
Copy link

trevornagy commented Jun 20, 2024

@trueqbit, maybe a discussion for another place. But I'm trying to create a wrapper around result_set_view so I'm only exposing STL types/don't need to include the ORM code everywhere (type erasure). I'm running into the issue that result_set_iterator doesn't seem to implement the != operator. Am I doing this wrong? Basically, my goal is to have some type, let's call it Iterator where I can do the following:

std::ranges::view auto SomeQuery() const
{
    auto result = storage.iterate<MarvelHero>(where(length(&MarvelHero::name) < 6)));
    return Iterator<MarvelHero>(result.begin(), result.end());
}

The error is: binary '!=': no operator found which takes a left-hand operand of type 'const TIterator' (or there is no acceptable conversion) with TIterator=sqlite_orm::internal::result_set_sentinel_t

Any thoughts?

@trueqbit
Copy link
Collaborator Author

std::ranges::view auto SomeQuery() const
{
    auto result = storage.iterate<MarvelHero>(where(length(&MarvelHero::name) < 6)));
    return Iterator<MarvelHero>(result.begin(), result.end());
}

The error is: binary '!=': no operator found which takes a left-hand operand of type 'const TIterator' (or there is no acceptable conversion) with TIterator=sqlite_orm::internal::result_set_sentinel_t

Any thoughts?

The specific query you posted here does not return a view on a result set of a select statement, but a view on "mapped objects", so there is a mismatch between the error you described and the iterator method you used.

The "mapped objects" view returns the same iterator type for start and end.
The view on a result set returns a sentinel as the end of the range (which is std::default_sentinel_t). Since a result set iterator is a single-pass input iterator, you can only compare it to the sentinel.

Of course, I have no insight into the specifics of your application. However, nowadays there is a very nice way of type erasure that usually saves me from writing view or iterator adapters: range generators.

You can have a function that returns a range generator of MarvelHeroS:

std::generator<MarvelHero> query_heroes() {
    for (MarvelHero hero : storage.iterate</*...*/>(/*...*/)) {
        co_yield hero;
    }
}

I use the reference implementation of the C++ WG21 proposal P2168, which is available on Lewis' Repository. There are probably improved implementations since the C++ WG21 proposal P2502.

@xiaopi-ouo
Copy link

@trevornagy
Thanks for your work on this!
I'm trying to use it in the case of inner_join

for (auto& row : storage.iterate(
         select(columns(&ExecutionSegment::tree_uid, &FunctionCallTree::uid),
                inner_join<FunctionCallTree>(
                    on(c(&ExecutionSegment::tree_uid) == &FunctionCallTree::uid)))))
{
    ...
}

but I got compiled error cannot deduce template parameter 'T'.
I’m wondering if I might be using it incorrectly.

@trueqbit
Copy link
Collaborator Author

What is the appropriate syntax for using iterate? iterate seems to need a Table type but that does not work for general selects!

    auto rows = storage.iterate(select(columns(&Doctor::id, &Doctor::name, &Visit::patientName, &Visit::vdate),
        left_join<Visit>(on(c(&Doctor::id) == &Visit::doctorId))));
    for( auto& row : rows)
    {
             auto id = get<0>(row);  // does not work!!
     }

@juandent This has been an oversight on my side. Tracked in issue #1443, fixed with PR #1448.

I'm trying to use it in the case of inner_join

for (auto& row : storage.iterate(
         select(columns(&ExecutionSegment::tree_uid, &FunctionCallTree::uid),
                inner_join<FunctionCallTree>(
                    on(c(&ExecutionSegment::tree_uid) == &FunctionCallTree::uid)))))
{
    ...
}

but I got compiled error cannot deduce template parameter 'T'. I’m wondering if I might be using it incorrectly.

@xiaopi-ouo This has been an oversight on my side. Tracked in your issue #1443, fixed with PR #1448.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iterate-like interface but for select with columns

5 participants