Result/row iterator rewrite

(Updated 2025-04-15)

I'm rewriting the result/row iterator classes so they're completely separate from the row and field classes. No more deep, intricate inheritance trees!

The end result I hope will be more efficient, more standard-compliant iterators.

A lot of things will change. Most of these won't affect most code, but there are some incompatible changes.

Indexing operator

As it turns out, pqxx::result::const_iterator::operator[] never properly complied with the C++ standard! A call to my_iterator[n] should return *(my_iterator + n), not index column number n on the current row.

Honestly, this was probably the most painful change for me — I had to change my code in a few places. But there was no choice: I want those iterators to be proper, standard, C++ iterators.

So, where your code writes my_result[row_num][col_num], you'll have to change that to my *(my_result[row_num])[col_num]. It's ugly, but... this is how it was always meant to be.

Litetimes

Iterators will no longer increment the the refernce count for the result object they're iterating. So you'll need to be a bit more careful with lifetimes.

Now, when you iterate a result (or a row within a result), it's up to you to ensure that that result object does not get destroyed, or moved to a different location in memory.

Based on my experience prototyping the change, this one shouldn't affect many people. This has always been the normal way to work with iterators. I'm removing a feature that nobody asked for, and which in perfomance terms, not a lot of people will want to "pay" for.

Lightweight "row" and "field" classes

We already had pqxx::row and pqxx::field types. These classes are reference-counted, meaning that you can get a row or field from a result object, then destroy the result object, and still access data from the row or field. That's not because the data is stored in the row or field object, but because pqxx::result is essentially a reference-counted pointer to the underlying data received from the server, preventing it from getting destroyed until all copies of the result object are destroyed.

But as with the iterators, most of the time, this isn't actually helpful. So in 8.0 I'm adding more lightweight variants, pqxx::row_ref and pqxx::field_ref which don't do this. You'll have to make sure that the pqxx::result is not destroyed, nor moved to a different location in meory, until you're done with the rows and fields on that result.

The new iterators build on these new types, where the old ones were built on the row and field types.

Some of the features in row and field do not exist in row_ref and field_ref. This may change over time, but let's see which ones we can do withou. All these classes have grown remarkably "fat" interfaces over the years, and it's time to reset them and start over.