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