Getting query results
Prev | Chapter 3. Tutorial | Next

## Getting query results

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 `tuple`s 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 has the null value. Use `is_null` to see if this is the case:

    	if (!F.is_null())
    	cout << "Name: " << F.c_str() << endl;

In practice of course, not all data is going to consist of strings. Many fields will be integer values, or decimals, or Booleans. To convert the field's value to one of these, use its `to` method. This adapts itself to the type of variable you pass it, expecting the field value to be of an appropriate form for that type. For convenience, `to` returns `false` if the field had the null value, and `true` otherwise. In the former case, the variable will retain the value it had before the call.

    	// Pay an employee his salary.  Add bonus for managers.
    	// The employee tuple must contain the fields
    	void PaySalary(result::const_iterator empl)
    	{
    	long id;
    	float salary;
    	bool is_manager=false;

    	// Get id.  Must never be null.
    	if (!empl[0].to(id)) throw runtime_error("No id!");

    	// Get salary.  If this employee has no salary, skip payment.
    	if (!empl[1].to(salary)) return;

    	// Get manager status.  If field is null, variable's original value (false)
    	// will be preserved.
    	empl[2].to(is_manager);

    	if (is_manager) salary += Bonus;

    	TransferMoney(id, salary);
    	}

If conversion fails, e.g. when trying to convert a floating-point value to an integer variable, `to` will throw a `runtime_error` reporting the problem in its `what` message.

[4] 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 nonexistant row or field will result in undefined behaviour such as program crashes or inexplicably strange results.

[5] This is an instance of the Proxy implementation pattern, needed to allow a `result` to be indexed as if it were a two-dimensional array. C++'s array index operator doesn't allow this usage directly, but the operator can be applied to the result of a previous use of the same operator. The "result in the middle" needs to have a type of its own, though, and that's what `tuple` is for.

[6] 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. In fact it matches how arrays and pointers work in C and C++.

Prev | Up | Next
Executing queries | Home | Doing Transactions Right: Transactors