Obviously not all queries are commands--the more common kind actually returns useful data. Result data in libpqxx™ are encapsulated in a result object, which acts as a container similar to the STL's vector template.
result R = T.exec("SELECT firstname FROM employee WHERE lastname='Ng'");
Two ways are provided to get at individual rows in a result: first, through indexing with the array index operator [] or the at member function, and second, through random-access iterators. Either will give you a result::tuple object that in turn can be addressed with the array index operator (or the at member function) or using iterators to get at its individual fields [4].
Thus, R[0] will return the first ("zeroth") tuple in R. You won't normally want to bother with tuples though; they don't contain the actual data, but rather serve as placeholders to later tell result which fields to fetch when field values from the tuple are requested. The class exists mostly for technical reasons related to the array index operators [5]. What you'll usually do is index the row directly to get at the field you want, e.g. R[0][0] to get the first field of the first row.
Array indexing of tuples also works with the fields' names instead of their numbers, eg.:
// Process employees' names one by one. ProcessNames() doesn't know exactly
// what columns are going to be in R, but there must be one called "lastname".
void ProcessNames(result R)
{
for (result::size_type i = 0; i != R.size(); ++i)
Process(R[i]["lastname"]);
}
As for the alternative, accessing the result tuples through an iterator, only const iterators are provided so the contents of the result cannot be modified. Use these iterators as random-access iterators like with any STL-like container:
for (result::const_iterator i = R.begin(); i != R.end(); ++i)
Process(*i);
Iterators may be incremented or decremented (whether pre- or post-), they may be added to or subtracted from to jump through the result tuples; their positions may be compared (provided they point into the same result, and they may be dereferenced through the * or -> operators.
Finally, the iterated tuples' fields may be addressed using the array index operator on the iterator directly, eg. R.begin()[0] gets you the first field of R's first row, just like R[0][0] would [6].
Either way, once you've indexed the result::tuple you get a result::field--which is another placeholder, but this time encapsulates an actual field value in our query result. A field F also knows its column name, which can be obtained as F.Name().
Again, there is more than one way to read the field's value. Let's start out with the easy one, c_str, which reads the value as a C string:
cout << "Name: " << F.c_str() << endl;
This will return the empty string ("") if field F > ustar root root 0000000 0000000
Writing database code can be tricky. One of the most complicated areas is dealing with unexpected error conditions, such as losing one's connection to the database server. For long-running processes you'll frequently find yourself rewriting code for a simple transaction to make it:
Attempt to perform the transaction.
Check for "connection lost" errors.
Attempt to restore the connection.
Repeat until the transaction succeeds.
This is bad for the heart, and clutters up your code besides. The transactor framework will take this work out of your hands if you let it.
The mechanism is based on the concept of Functors, a powerful object-oriented design pattern that replaces the older practice of passing callback functions (or hooks, as they're sometimes called, or exits) to foreign code. Unlike classic callback functions, Functors provide an elegant way of maintaining custom state in your callback code, when the exact form or size of that state was not known in advance to the writer of the foreign code that will eventually invoke your callback.
Functors in C++ are simple objects that can be invoked just like functions or function pointers can, by virtue of providing the function invocation operator, operator().
A simple functor could look like this:
struct HelloFunctor
{
void operator()() { cout << "Hello World" << endl; }
};
...And once an object of this functor type has been created, it can be "invoked" just as if it were a function:
HelloFunctor Hi; Hi();
But the invoking code may also be foreign code knowing nothing about the type of your functor. The foreign code is usually a template, so it automatically becomes "specialized" to your type of functor when the one meets the other:
template