userver: uPg: Typed PostgreSQL results
Loading...
Searching...
No Matches
uPg: Typed PostgreSQL results

The ResultSet provides access to a generic PostgreSQL result buffer wrapper with access to individual column buffers and means to parse the buffers into a certain type.

For a user that wishes to get the results in a form of a sequence or a container of C++ tuples or structures, the driver provides a way to coerce the generic result set into a typed result set or a container of tuples or structures that fulfill certain conditions.

TypedResultSet provides container interface for typed result rows for iteration or random access without converting all the result set at once. The iterators in the TypedResultSet satisfy requirements for a constant RandomAccessIterator with the exception of dereferencing iterators.

Warning
The operator* of the iterators returns value (not a reference to it) and the iterators don't have the operator->.
Data row extraction

The data rows can be obtained as:

  • std::tuple;
  • aggregate class as is;
  • non-aggregate class with some augmentation.

Data members of the tuple or the classes must be supported by the driver. For more information on supported data types please see uPg: Supported data types

std::tuple.

The first option is to convert ResultSet's row to std::tuples.

using MyRowType = std::tuple<int, string>;
auto trx = ...;
auto generic_result = trx.Execute("select a, b from my_table");
auto iteration = generic_result.AsSetOf<MyRowType>();
for (auto row : iteration) {
static_assert(std::is_same_v<decltype(row), MyRowType>,
"Iterate over tuples");
auto [a, b] = row;
std::cout << "a = " << a << "; b = " << b << "\n";
}
auto data = geric_result.AsContainer<std::vector<MyRowType>>();
Aggregate classes.

A data row can be coerced to an aggregate class.

An aggregate class (C++03 8.5.1 §1) is a class that with no base classes, no protected or private non-static data members, no user-declared constructors and no virtual functions.

struct MyRowType {
int a;
std::string b;
};
auto generic_result = trx.Execute("select a, b from my_table");
auto iteration = generic_result.AsSetOf<MyRowType>();
for (auto row : iteration) {
static_assert(std::is_same_v<decltype(row), MyRowType>,
"Iterate over aggregate classes");
std::cout << "a = " << row.a << "; b = " << row.b << "\n";
}
auto data = geric_result.AsContainer<std::vector<MyRowType>>();
Non-aggregate classes.

Classes that do not satisfy the aggregate class requirements can be used to be created from data rows by providing additional Introspect non-static member function. The function should return a tuple of references to member data fields. The class must be default constructible.

class MyRowType {
private:
int a_;
std::string b_;
public:
MyRowType() = default; // default ctor is required
explicit MyRowType(int x);
auto Introspect() {
return std::tie(a_, b_);
}
int GetA() const;
const std::string& GetB() const;
};
auto generic_result = trx.Execute("select a, b from my_table");
auto iteration = generic_result.AsSetOf<MyRowType>();
for (auto row : iteration) {
static_assert(std::is_same_v<decltype(row), MyRowType>,
"Iterate over non-aggregate classes");
std::cout << "a = " << row.GetA() << "; b = " << row.GetB() << "\n";
}
auto data = geric_result.AsContainer<std::vector<MyRowType>>();
Single-column result set

A single-column result set can be used to extract directly to the column type. User types mapped to PostgreSQL will work as well. If you need to extract the whole row into such a structure, you will need to disambiguate the call with the kRowTag.

auto string_set = generic_result.AsSetOf<std::string>();
std::string s = string_set[0];
auto string_vec = generic_result.AsContainer<std::vector<std::string>>();
// Extract first column into the composite type
auto foo_set = generic_result.AsSetOf<FooBar>();
auto foo_vec = generic_result.AsContainer<std::vector<FooBar>>();
// Extract the whole row, disambiguation
auto foo_set = generic_result.AsSetOf<FooBar>(kRowTag);