Cppgres
Build Postgres extensions in C++
Loading...
Searching...
No Matches
type.hpp
Go to the documentation of this file.
1
4#pragma once
5
6#include <cstddef>
7#include <span>
8#include <string>
9
10#include "datum.hpp"
11#include "guard.hpp"
12#include "imports.h"
13#include "utils/utils.hpp"
14
15namespace cppgres {
16
20struct type {
21 ::Oid oid;
22
30 std::string_view name(bool qualified = false) {
31 if (!OidIsValid(oid)) {
32 throw std::runtime_error("invalid type");
33 }
34 return (qualified ? ffi_guard{::format_type_be_qualified}
35 : ffi_guard{::format_type_be})(oid);
36 }
37
38 bool operator==(const type &other) const { return oid == other.oid; }
39};
40
41template <typename T, typename = void> struct type_traits {
42 type_traits() {}
43 type_traits(const T &) {}
44 bool is(const type &t) { return false; }
45 type type_for() = delete;
46};
47
48template <typename T> requires std::is_reference_v<T>
49struct type_traits<T> {
50 type_traits() {}
51 type_traits(const T &) {}
52 constexpr type type_for() { return type_traits<std::remove_reference_t<T>>().type_for(); }
53};
54
55template <typename T> struct type_traits<std::optional<T>> {
56 type_traits() {}
57 type_traits(const std::optional<T> &) {}
58 bool is(const type &t) { return type_traits<T>().is(t); }
59 constexpr type type_for() { return type_traits<T>().type_for(); }
60};
61
62struct non_by_value_type : public type {
63 friend struct datum;
64
65 non_by_value_type(std::pair<const struct datum &, std::optional<memory_context>> init)
66 : non_by_value_type(init.first, init.second) {}
67 non_by_value_type(const struct datum &datum, std::optional<memory_context> ctx)
68 : value_datum(datum),
69 ctx(tracking_memory_context(ctx.has_value() ? *ctx : top_memory_context())) {}
70
72 : value_datum(other.value_datum), ctx(other.ctx) {}
73 non_by_value_type(non_by_value_type &&other) noexcept
74 : value_datum(std::move(other.value_datum)), ctx(std::move(other.ctx)) {}
75 non_by_value_type &operator=(non_by_value_type &&other) noexcept {
76 value_datum = std::move(other.value_datum);
77 ctx = std::move(other.ctx);
78 return *this;
79 }
80
81 memory_context &get_memory_context() { return ctx.get_memory_context(); }
82
83 datum get_datum() const { return value_datum; }
84
85protected:
86 datum value_datum;
88 void *ptr(bool tracked = true) const {
89 if (tracked && ctx.resets() > 0) {
91 }
92 return reinterpret_cast<void *>(value_datum.operator const ::Datum &());
93 }
94};
95
96static_assert(std::copy_constructible<non_by_value_type>);
97
98struct varlena : public non_by_value_type {
99 using non_by_value_type::non_by_value_type;
100
101 operator void *() { return VARDATA_ANY(detoasted_ptr()); }
102
103 datum get_datum() const { return value_datum; }
104
105 bool is_detoasted() const { return detoasted != nullptr; }
106
107protected:
108 void *detoasted = nullptr;
109 void *detoasted_ptr() {
110 if (detoasted != nullptr) {
111 return detoasted;
112 }
113 detoasted = ffi_guard{::pg_detoast_datum}(reinterpret_cast<::varlena *>(ptr()));
114 return detoasted;
115 }
116};
117
118struct text : public varlena {
119 using varlena::varlena;
120
121 operator std::string_view() {
122 return {static_cast<char *>(this->operator void *()), VARSIZE_ANY_EXHDR(this->detoasted_ptr())};
123 }
124};
125
126using byte_array = std::span<const std::byte>;
127
128struct bytea : public varlena {
129 using varlena::varlena;
130
131 bytea(const byte_array &ba, memory_context ctx)
132 : varlena(([&]() {
133 auto alloc = ctx.alloc<std::byte>(VARHDRSZ + ba.size_bytes());
134 SET_VARSIZE(alloc, VARHDRSZ + ba.size_bytes());
135 auto ptr = VARDATA_ANY(alloc);
136 std::copy(ba.begin(), ba.end(), reinterpret_cast<std::byte *>(ptr));
137 return datum(PointerGetDatum(alloc));
138 })(),
139 ctx) {}
140
141 operator byte_array() {
142 return {reinterpret_cast<std::byte *>(this->operator void *()),
143 VARSIZE_ANY_EXHDR(this->detoasted_ptr())};
144 }
145};
146
147template <typename T>
148concept flattenable = requires(T t, std::span<std::byte> span) {
149 { T::type() } -> std::same_as<type>;
150 { t.flat_size() } -> std::same_as<std::size_t>;
151 { t.flatten_into(span) };
152 { T::restore_from(span) } -> std::same_as<T>;
153};
154
155template <flattenable T> struct expanded_varlena : public varlena {
156 using flattenable_type = T;
157 using varlena::varlena;
158
160 : varlena(([]() {
161 auto ctx = memory_context(std::move(alloc_set_memory_context()));
162 auto *e = new (ctx.alloc<expanded>()) expanded(T());
163 ctx.register_reset_callback(
164 [](void *arg) {
165 auto v = reinterpret_cast<expanded *>(arg);
166 v->inner.~T();
167 },
168 e);
169 init(&e->hdr, ctx);
170 return std::make_pair(datum(PointerGetDatum(e)), ctx);
171 })()),
172 detoasted_value(reinterpret_cast<expanded *>(DatumGetPointer(value_datum))) {}
173
174 operator T &() {
175 if (detoasted_value.has_value()) {
176 return detoasted_value.value()->inner;
177 } else {
178 auto *ptr1 = reinterpret_cast<std::byte *>(varlena::operator void *());
179 auto ctx = memory_context(std::move(alloc_set_memory_context()));
180 auto *value = new (ctx.alloc<expanded>())
181 expanded(T::restore_from(std::span(ptr1, VARSIZE_ANY_EXHDR(detoasted_ptr()))));
182 ctx.register_reset_callback(
183 [](void *arg) {
184 auto v = reinterpret_cast<expanded *>(arg);
185 v->inner.~T();
186 },
187 value);
188 init(&value->hdr, ctx);
189 detoasted_value = value;
190 return value->inner;
191 }
192 }
193
194 datum get_expanded_datum() const {
195 if (!detoasted_value.has_value()) {
196 throw std::runtime_error("hasn't been expanded yet");
197 }
198 return datum(EOHPGetRWDatum(&detoasted_value.value()->hdr));
199 }
200
201private:
202 struct expanded {
203 expanded(T &&t) : inner(std::move(t)) {}
204 ::ExpandedObjectHeader hdr;
205 T inner;
206 };
207 std::optional<expanded *> detoasted_value = std::nullopt;
208
209 static void init(ExpandedObjectHeader *hdr, memory_context &ctx) {
210 using header = int32_t;
211
212 static const ::ExpandedObjectMethods eom = {
213 .get_flat_size =
214 [](ExpandedObjectHeader *eohptr) {
215 auto *e = reinterpret_cast<expanded *>(eohptr);
216 T *inner = &e->inner;
217 return inner->flat_size() + sizeof(header);
218 },
219 .flatten_into =
220 [](ExpandedObjectHeader *eohptr, void *result, size_t allocated_size) {
221 auto *e = reinterpret_cast<expanded *>(eohptr);
222 T *inner = &e->inner;
223 SET_VARSIZE(reinterpret_cast<header *>(result), allocated_size);
224 auto bytes = reinterpret_cast<std::byte *>(result) + sizeof(header);
225 std::span buffer(bytes, allocated_size - sizeof(header));
226 inner->flatten_into(buffer);
227 }};
228
229 ffi_guard{::EOH_init_header}(hdr, &eom, ctx);
230 }
231};
232
233template <typename T>
234concept expanded_varlena_type = requires { typename T::flattenable_type; };
235
236template <typename T>
237concept has_a_type = requires(type_traits<T> t) {
238 { t.type_for() } -> std::same_as<type>;
239};
240
241} // namespace cppgres
Definition: type.hpp:234
Definition: type.hpp:148
Definition: type.hpp:237
Definition: memory.hpp:94
Definition: type.hpp:128
Definition: datum.hpp:35
Definition: type.hpp:155
Definition: guard.hpp:19
Definition: memory.hpp:61
Definition: type.hpp:62
Definition: datum.hpp:17
Definition: memory.hpp:243
Definition: type.hpp:118
Definition: memory.hpp:111
Definition: type.hpp:41
Postgres type.
Definition: type.hpp:20
std::string_view name(bool qualified=false)
Type name as defined in Postgres.
Definition: type.hpp:30
Definition: value.hpp:8
Definition: type.hpp:98