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.
result R = T.exec("SELECT firstname FROM employee WHERE lastname='Ng'");
This executes a query on the database, collects all matching data, and
stores it in the form of a result.
There are several ways to get at the individual rows in a
result. You can index the result with the array
index operator [], or call the
at member function. There are also random-access
iterators, and you can iterate the rows in a result. Each of these
will give you a row object. You again get the same
options for accessing the fields in the row.
[2].
Thus, R[0] will return the first ("zeroth") row
in R. You can get the same row as *R.begin().
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.
Of course you can also iterate the rows in a result, whether with C++ iteration syntax or a classic C-style loop:
for (auto const row: my_result) process(row);
for (auto row = std::begin(my_result); row != my_result.end(); row++)
process(*row);
Once you have a row, you'll want to access its fields. You can do that in all the same way you accessed rows in a result. But in addition, you can also index a row by name instead of by number:
// Process employees' names one by one. process_names() doesn't know
// exactly what columns are going to be in R, but there must be one
// called "lastname".
void process_names(result R)
{
for (result::size_type i = 0; i != R.size(); ++i)
process(R[i]["lastname"]);
}
There's one more trick for both result iterators and row iterators.
You can index them directly using the array index operator, just like
C++-native pointers can work a lot like arrays:
R.begin()[0] gets you the first field of
R's first row, just like R[0][0]
would[3].
There is also more than one way to read a 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
has the null value. Use is_null to see if this
is the case:
if (!F.is_null())
cout << "Name: " << F.c_str() << endl;
[2]
The difference between [] and
at is that the latter is guaranteed to perform
bounds-checking, throwing an exception if you pass it an illegal
index value. With the array index operator you may get slightly
better performance, but attempting to address a nonexistent row or
field will result in undefined behaviour such as program crashes or
inexplicably strange results.
[3]
Or (*R.begin())[0]. It may seem quirky to
have R.begin() and *R.begin()
mean the same thing, but it makes practical use of iterators a lot
easier.