PrevUpHomeNext

Executing Queries

With Multi-Record Output: begin() and const_iterator
With (at most) Single-Record Output: get()
With Single-Value Output: evaluate()

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;

PrevUpHomeNext