The driver supports user-defined PostgreSQL composite types. The C++ counterpart type must satisfy the same requirements as for the row types, (uPg: Typed PostgreSQL results) and must provide a specialization of CppToUserPg template (uPg: Mapping a C++ type to PostgreSQL user type).
Parsing a composite structure from PostgreSQL buffer will throw an error if the number of fields in the postgres data is different from the number of data members in target C++ type. This is the only sanity control for the composite types. The driver doesn't check the data type oids, it's user's responsibility to provide structures with compatible data members.
- Examples from tests
namespace pgtest {
struct FooBar {
int i{};
std::string s;
double d{};
std::vector<int> a;
std::vector<std::string> v;
bool operator==(const FooBar& rhs) const {
return i == rhs.i && s == rhs.s && d == rhs.d && a == rhs.a && v == rhs.v;
}
};
struct FooBarWithOptionalFields {
std::optional<int> i;
std::optional<std::string> s;
std::optional<double> d;
std::vector<int> a;
std::vector<std::string> v;
bool operator==(const FooBarWithOptionalFields& rhs) const {
return i == rhs.i && s == rhs.s && d == rhs.d && a == rhs.a && v == rhs.v;
}
};
using FooBarOpt = std::optional<FooBar>;
class FooClass {
int i{};
std::string s;
double d{};
std::vector<int> a;
std::vector<std::string> v;
public:
FooClass() = default;
explicit FooClass(
int x) : i(x), s(
std::
to_string(x)), d(x), a{i}, v{s} {}
auto Introspect() { return std::tie(i, s, d, a, v); }
auto GetI() const { return i; }
auto GetS() const { return s; }
auto GetD() const { return d; }
auto GetA() const { return a; }
auto GetV() const { return v; }
};
using FooTuple = std::tuple<int, std::string, double, std::vector<int>,
std::vector<std::string>>;
struct BunchOfFoo {
std::vector<FooBar> foobars;
bool operator==(const BunchOfFoo& rhs) const {
return foobars == rhs.foobars;
}
};
}
- Warning
- The type mapping specialization must be accessible at the points where parsing/formatting of the C++ type is instantiated. The header where the C++ type is declared is an appropriate place to do it.
template <>
struct CppToUserPg<pgtest::FooBar> {
static constexpr DBTypeName postgres_name = kCompositeName;
};
template <>
struct CppToUserPg<pgtest::FooClass> {
static constexpr DBTypeName postgres_name = kCompositeName;
};
template <>
struct CppToUserPg<pgtest::FooTuple> {
static constexpr DBTypeName postgres_name = kCompositeName;
};
template <>
struct CppToUserPg<pgtest::BunchOfFoo> {
static constexpr DBTypeName postgres_name = "__pgtest.foobars";
};
}