32 auto res =
ffi_guard{::CreateTemplateTupleDesc}(nattrs);
35 blessed(
false), owned(
true) {
36 for (
int i = 0; i < nattrs; i++) {
48 : tupdesc(tupdesc), blessed(blessed), owned(false) {}
56 if ((*syscache).typtype != TYPTYPE_COMPOSITE) {
57 throw std::invalid_argument(
"not a composite type");
59 return ffi_guard{::lookup_rowtype_tupdesc_copy}(t.oid, (*syscache).typtypmod);
68 : tupdesc(
ffi_guard{::CreateTupleDescCopyConstr}(other.populate_compact_attribute())),
69 blessed(other.blessed), owned(other.owned) {}
75 : tupdesc(other.tupdesc), blessed(other.blessed), owned(other.owned) {}
83 tupdesc =
ffi_guard{::CreateTupleDescCopyConstr}(other.populate_compact_attribute());
84 blessed = other.blessed;
93 tupdesc = other.tupdesc;
94 blessed = other.blessed;
112 return *TupleDescAttr(tupdesc, n);
115 type get_type(
int n)
const {
117 return {att.atttypid};
132 att.atttypid = (*typ).oid;
133 att.attcollation = (*typ).typcollation;
134 att.attlen = (*typ).typlen;
135 att.attstorage = (*typ).typstorage;
136 att.attalign = (*typ).typalign;
137 att.atttypmod = (*typ).typtypmod;
138 att.attbyval = (*typ).typbyval;
141 std::string_view get_name(
int n) {
143 return NameStr(att.attname);
165 operator TupleDesc() {
166 populate_compact_attribute();
168 tupdesc =
ffi_guard{::BlessTupleDesc}(tupdesc);
174#if PG_MAJORVERSION_NUM > 16
182 return ffi_guard{::equalRowTypes}(tupdesc, other.tupdesc);
193 if (tupdesc->natts != other.tupdesc->natts)
195 if (tupdesc->tdtypeid != other.tupdesc->tdtypeid)
198 for (
int i = 0; i < other.
attributes(); i++) {
200 FormData_pg_attribute &att2 = other[i];
202 if (att1.atttypid != att2.atttypid || att1.atttypmod != att2.atttypmod ||
203 att1.attcollation != att2.attcollation || att1.attisdropped != att2.attisdropped ||
204 att1.attlen != att2.attlen || att1.attalign != att2.attalign) {
218 return ffi_guard{::equalTupleDescs}(tupdesc, other.tupdesc);
226 operator TupleDesc()
const {
return tupdesc; }
229 inline void check_bounds(
int n)
const {
230 if (n + 1 > tupdesc->natts || n < 0) {
231 throw std::out_of_range(cppgres::fmt::format(
232 "attribute index {} is out of bounds for the tuple descriptor with the size of {}", n,
236 inline void check_not_blessed()
const {
238 throw std::runtime_error(
"tuple_descriptor already blessed");
242 TupleDesc populate_compact_attribute()
const {
243#if PG_MAJORVERSION_NUM >= 18
244 for (
int i = 0; i < tupdesc->natts; i++) {
245 ffi_guard{::populate_compact_attribute}(tupdesc, i);
256static_assert(std::copy_constructible<tuple_descriptor>);
257static_assert(std::move_constructible<tuple_descriptor>);
258static_assert(std::is_copy_assignable_v<tuple_descriptor>);
272 ::lookup_rowtype_tupdesc_copy}(HeapTupleHeaderGetTypeId(
heap_tuple),
274 tuple(ctx.template alloc<HeapTupleData>()) {
275#if PG_MAJORVERSION_NUM < 18
276 tuple->t_len = HeapTupleHeaderGetDatumLength(tupdesc.operator TupleDesc());
278 tuple->t_len = HeapTupleHeaderGetDatumLength(
heap_tuple);
284 template <std::input_iterator Iter>
287 : tupdesc(tupdesc), tuple([&]() {
288 std::vector<::Datum> values;
289 std::vector<bool> null_flags;
290 for (
auto it = begin; it != end; ++it) {
291 auto nd = into_nullable_datum(*it);
292 values.push_back(nd.is_null() ? ::Datum(0) : nd);
293 null_flags.push_back(nd.is_null());
295 auto nulls = std::make_unique<bool[]>(null_flags.size());
296 std::ranges::copy(null_flags, nulls.get());
297 return ffi_guard{::heap_form_tuple}(this->tupdesc, values.data(), nulls.get());
302 : tupdesc(tupdesc), tuple([&]() {
304 cppgres::into_nullable_datum(std::move(args))...};
305 std::array<bool,
sizeof...(D)> nulls;
306 std::ranges::copy(datums | std::views::transform([](
auto &v) {
return v.is_null(); }),
308 std::array<::Datum,
sizeof...(D)> values;
310 datums | std::views::transform([](
auto &v) {
return v.is_null() ? ::Datum(0) : v; }),
312 return ffi_guard{::heap_form_tuple}(this->tupdesc, values.data(), nulls.data());
318 int attributes()
const {
return tupdesc.operator TupleDesc()->natts; }
327 return {.oid = TupleDescAttr(tupdesc.operator TupleDesc(), n)->atttypid};
337 return {NameStr(TupleDescAttr(tupdesc.operator TupleDesc(), n)->attname)};
349#if PG_MAJORVERSION_NUM < 15
351 [](::HeapTuple tup,
int attnum, ::TupleDesc tupleDesc,
bool *isnull) {
352 return heap_getattr(tup, attnum, tupleDesc, isnull);
358 datum d(_heap_getattr(tuple, n + 1, tupdesc.operator TupleDesc(), &isnull));
373 throw std::out_of_range(cppgres::fmt::format(
"no attribute by the name of {}",
name));
383 operator HeapTuple() const noexcept {
return tuple; }
384 operator TupleDesc() const noexcept {
return tupdesc; }
391 record(
const record &other) : tupdesc(other.tupdesc), tuple(other.tuple) {}
392 record(record &&other) noexcept : tupdesc(std::move(other.tupdesc)), tuple(other.tuple) {}
393 record &operator=(
const record &other) {
394 tupdesc = other.tupdesc;
399 record &operator=(record &&other)
noexcept {
400 if (
this != &other) {
401 tupdesc = std::move(other.tupdesc);
408 inline void check_bounds(
int n)
const {
410 throw std::out_of_range(cppgres::fmt::format(
411 "attribute index {} is out of bounds for record with the size of {}", n,
attributes()));
415 tuple_descriptor tupdesc;
418static_assert(std::copy_constructible<record>);
419static_assert(std::move_constructible<record>);
420static_assert(std::is_copy_assignable_v<record>);
424 return {
reinterpret_cast<HeapTupleHeader
>(
ffi_guard{::pg_detoast_datum}(
425 reinterpret_cast<struct ::
varlena *
>(d.operator const ::Datum &()))),
433 bool is(
const type &t) {
434 if (t.oid == RECORDOID)
438 return (*cache).typtype ==
'c';
440 constexpr type type_for() {
return {.oid = RECORDOID}; }
445 { T::composite_type() } -> std::same_as<type>;
450 if (oid_ != T::composite_type().
oid) {
451 throw std::runtime_error(fmt::format(
"invalid type: expected composite type {} got {}",
452 T::composite_type().
name(),
type{.oid = oid_}.name()));
455 record rec{
reinterpret_cast<HeapTupleHeader
>(
ffi_guard{::pg_detoast_datum}(
456 reinterpret_cast<struct ::
varlena *
>(d.operator const ::Datum &()))),
458 return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
460 return from_nullable_datum<utils::tuple_element_t<Is, T>>(rec.get_attribute(Is),
461 rec.attribute_type(Is).oid, mctx);
463 }(std::make_index_sequence<utils::tuple_size_v<T>>{});
467 auto res = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
469 record rec{td, utils::get<Is>(t)...};
471 }(std::make_index_sequence<utils::tuple_size_v<T>>{});
476 bool is(
const type &t) {
477 if (t == T::composite_type())
481 return (*cache).typtype ==
'c';
483 constexpr type type_for() {
return T::composite_type(); }
Definition: record.hpp:444
Definition: datum.hpp:213
Definition: memory.hpp:14
A trait to convert from and into a cppgres::datum.
Definition: datum.hpp:114
static T from_datum(const datum &, const oid, std::optional< memory_context > context=std::nullopt)=delete
Convert from a datum.
static datum into_datum(const T &d)=delete
Convert datum into a type.
Definition: datum.hpp:146
Heap tuple convenience wrapper.
Definition: heap_tuple.hpp:11
Definition: memory.hpp:240
Definition: memory.hpp:103
Runtime-typed value of record type.
Definition: record.hpp:266
nullable_datum operator[](std::string_view name)
Get attribute by name.
Definition: record.hpp:367
tuple_descriptor get_tuple_descriptor() const noexcept
Returns tuple descriptor.
Definition: record.hpp:389
std::string_view attribute_name(int n) const
Name of attribute using a 0-based index.
Definition: record.hpp:335
nullable_datum get_attribute(int n)
Get attribute value (datum) using a 0-based index.
Definition: record.hpp:345
nullable_datum operator[](int n)
Get attribute by 0-based index.
Definition: record.hpp:381
int attributes() const
Number of attributes in the record.
Definition: record.hpp:318
type attribute_type(int n) const
Type of attribute using a 0-based index.
Definition: record.hpp:325
Definition: syscache.hpp:27
Tuple descriptor operator.
Definition: record.hpp:21
tuple_descriptor(tuple_descriptor &&other) noexcept
Move constructor.
Definition: record.hpp:74
bool equal_types(const tuple_descriptor &other)
Determines whether two tuple descriptors have equal row types.
Definition: record.hpp:192
void set_name(int n, const name &name)
Set attribute name.
Definition: record.hpp:154
tuple_descriptor(tuple_descriptor &other)
Copy constructor.
Definition: record.hpp:67
::FormData_pg_attribute & operator[](int n) const
Get a reference to Form_pg_attribute
Definition: record.hpp:110
tuple_descriptor & operator=(tuple_descriptor &&other) noexcept
Move assignment.
Definition: record.hpp:91
tuple_descriptor(TupleDesc tupdesc, bool blessed=true)
Create a tuple descriptor for a given TupleDesc
Definition: record.hpp:47
tuple_descriptor(int nattrs, memory_context ctx=memory_context())
Create a tuple descriptor for a given number of attributes.
Definition: record.hpp:29
bool operator==(const tuple_descriptor &other)
Compare two TupleDesc structures for logical equality.
Definition: record.hpp:217
int attributes() const noexcept
Number of attributes.
Definition: record.hpp:103
tuple_descriptor(type t)
Create a tuple description for a given composite type.
Definition: record.hpp:53
bool is_blessed() const noexcept
Returns true if the tuple descriptor is blessed.
Definition: record.hpp:224
void set_type(int n, const type &type)
Set attribute type.
Definition: record.hpp:128
tuple_descriptor & operator=(const tuple_descriptor &other)
Copy assignment.
Definition: record.hpp:82
Postgres type.
Definition: type.hpp:21