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