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