The most general way to execute a query is by calling its begin()
method. That returns an iterator, and the iterator
provides access to all of the query's output, whether it be nothing,
a single record, or so much that it can't fit in memory all at once.
On PostgreSQL, the iterator wraps an SQL CURSOR
.
q
.begin()
starts the query q
's execution but does not
wait for it to finish. It waits for output to start flowing, and when
it receives the first row, or it learns that there will be no rows, it
returns. Its return type is Q
::const_iterator
.
Thereafter the application may call Q
::const_iterator::operator++()
to indicate readiness for more output,
and the DBMS's production of data is flow-controlled accordingly.
When the end of data is reached (either by begin()
or by ++
),
the iterator enters an end state, and then ++
may not be used on it again. The end
state can be tested using the iterator's is_end()
method, or by comparing it with a past-the-end
iterator. If q
.begin()
created the iterator, then q
.end()
returns a past-the-end iterator to
compare it with.
If the iterator is destroyed before it enters the end state, then the query's execution is terminated.
An iterator may (depending on the DBMS) buffer some rows of raw DBMS
output. It also keeps a C++ representation of the current row, and its
operator*()
and operator->()
return a const reference and a const pointer, respectively, to that.
These iterators conform to the STL InputIterator concept, so for deeper treatment I refer to the documentation here, and the linked pages, especially here and here.
Those pages leave some terms for iterator implementations to define, and our query iterators define them as follows:
For any query type Q
, if I
is the type Q
::const_iterator
, then:
I
::difference_type
is size_t
.
I
::value_type
is Q
's
value type.
I
::pointer
is const
I
::value_type *
.
I
::reference
is const
I
::value_type &
.
I
::iterator_category
is std::input_iterator_tag
.
The iterator returned by Q
::end()
is a “past-the-end” iterator and also a “singular”
iterator.
fetch_size()
If q
is a query on a PostgreSQL database,
then its iterator will buffer raw DBMS output. The buffer holds 100 rows
by default, but fetch_size()
lets you control that number. For any
value n
of type uint32_t
,
q
.fetch_size(
n
)
returns a query that is like q
in every way, except that its begin()
method returns an iterator that buffers
n
rows at a time.
If q
belongs to an sqlite database, then
q
.fetch_size(
n
)
is ignored, and harmless.
If you build a query using fetch_size()
and then build further with where()
,
select()
,
order()
etc., the resulting query will be valid but its fetch size on PostgreSQL
will be unpredictable, so I don't recommend doing that. Only call fetch_size()
immediately before begin()
.
The C++11 for
loop calls
begin()
on the query you give it, so this works:
for(const point &p: points.where(points->x > 4).fetch_size(50)) std::cout << p.y << std::endl;