Skip to content

Commit

Permalink
Make streaming query result an input_iterator. (#772)
Browse files Browse the repository at this point in the history
Fixes: #771.

With these changes, the result of `transaction_base::stream()` now
implements the `std::input_iterator` concept.  It should now work as a
`std::range` in a C++ pipeline.
  • Loading branch information
jtv committed Dec 23, 2023
1 parent ec25ab6 commit 92bdf08
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 6 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
- Support parameterised versions of `query()` etc. (#646)
- Put all feature tests back in config header. (#732)
- Automate integration of feature tests into both CMake & autoconf. (#747)
- Streaming query result implements `std::input_iterator`. (#771)
- Fix broken `to_buf()` on `zview`. (#728)
- Support `PQfsize()` and `PQfmod()`. (#727)
- Implement conversion from `string_view` to string. (#728)
Expand Down
36 changes: 30 additions & 6 deletions include/pqxx/internal/stream_query_impl.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ template<typename... TYPE> class stream_query_input_iterator

public:
using value_type = std::tuple<TYPE...>;
using difference_type = long;

explicit stream_query_input_iterator(stream_t &home) :
m_home(home),
m_home(&home),
m_line{typename stream_query<TYPE...>::line_handle(
nullptr, pqxx::internal::pq::pqfreemem)}
{
Expand All @@ -54,18 +55,27 @@ public:
stream_query_input_iterator(stream_query_input_iterator const &) = default;
stream_query_input_iterator(stream_query_input_iterator &&) = default;

/// Pre-increment. (There's no post-increment.)
/// Pre-increment. This is what you'd normally want to use.
stream_query_input_iterator &operator++() &
{
assert(not done());
consume_line();
return *this;
}

/// Post-increment. Only here to satisfy input_iterator concept.
/** The iterator that this returns is in an unusable state.
*/
stream_query_input_iterator operator++(int)
{
++*this;
return {};
}

/// Dereference. There's no caching in here, so don't repeat calls.
value_type operator*() const
{
return m_home.parse_line(zview{m_line.get(), m_line_size});
return m_home->parse_line(zview{m_line.get(), m_line_size});
}

/// Are we at the end?
Expand All @@ -76,14 +86,28 @@ public:
return not done();
}

stream_query_input_iterator &operator=(stream_query_input_iterator &&rhs)
noexcept
{
if (&rhs != this)
{
m_line = std::move(rhs.m_line);
m_home = rhs.m_home;
m_line_size = rhs.m_line_size;
}
return *this;
}

private:
stream_query_input_iterator() {}

/// Have we finished?
bool done() const noexcept { return m_home.done(); }
bool done() const noexcept { return m_home->done(); }

/// Read a line from the stream, store it in the iterator.
void consume_line() &
{
auto [line, size]{m_home.read_line()};
auto [line, size]{m_home->read_line()};
m_line = std::move(line);
m_line_size = size;
if (size > 0)
Expand All @@ -97,7 +121,7 @@ private:
}
}

stream_t &m_home;
stream_t *m_home;

/// Last COPY line we read, allocated by libpq.
typename stream_t::line_handle m_line;
Expand Down
2 changes: 2 additions & 0 deletions include/pqxx/row.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ namespace pqxx
class PQXX_LIBEXPORT row
{
public:
// TODO: Some of these types conflict: class is both iterator and container.
// TODO: Set iterator nested types using std::iterator_traits.
using size_type = row_size_type;
using difference_type = row_difference_type;
using const_iterator = const_row_iterator;
Expand Down

0 comments on commit 92bdf08

Please sign in to comment.