userver: userver/storages/postgres/io/traits.hpp Source File
Loading...
Searching...
No Matches
traits.hpp
1#pragma once
2
3#include <limits>
4#include <string>
5#include <type_traits>
6#include <unordered_map>
7
8#include <userver/storages/postgres/detail/is_decl_complete.hpp>
9#include <userver/storages/postgres/io/pg_types.hpp>
10#include <userver/utils/void_t.hpp>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace storages::postgres {
15
16class UserTypes;
17
18namespace io {
19
20/// Category of buffer contents.
21///
22/// Applied to binary parsers and deduced from field's data type.
23enum class BufferCategory {
24 kKeepCategory = -1, //!< kKeepCategory keep current buffer category
25 kNoParser = 0, //!< kNoParser the data type doesn't have a parser defined
26 kVoid, //!< kVoid there won't be a buffer for this field, but the category is
27 //!< required for correctly handling void results
28 kPlainBuffer, //!< kPlainBuffer the buffer is a single plain value
29 kArrayBuffer, //!< kArrayBuffer the buffer contains an array of values
30 kCompositeBuffer, //!< kCompositeBuffer the buffer contains a user-defined
31 //!< composite type
32 kRangeBuffer //!< kRangeBuffer the buffer contains a range type
33};
34
35std::string_view ToString(BufferCategory);
36
37template <BufferCategory Category>
38using BufferCategoryConstant = std::integral_constant<BufferCategory, Category>;
39
40using TypeBufferCategory = std::unordered_map<Oid, BufferCategory>;
41
42BufferCategory GetTypeBufferCategory(const TypeBufferCategory&, Oid);
43
45 using IntegerType = std::underlying_type_t<BufferCategory>;
46 std::size_t operator()(BufferCategory val) const { return std::hash<IntegerType>{}(static_cast<IntegerType>(val)); }
47};
48
49inline constexpr int kPgBinaryDataFormat = 1;
50
51/// Fields that are null are denoted by specifying their length == -1
52inline constexpr Integer kPgNullBufferSize = -1;
53
55 static constexpr std::size_t npos = std::numeric_limits<std::size_t>::max();
56
57 bool is_null = false;
59 std::size_t length = 0;
60 const std::uint8_t* buffer = nullptr;
61
62 std::string ToString() const { return {reinterpret_cast<const char*>(buffer), length}; }
63 constexpr FieldBuffer GetSubBuffer(
64 std::size_t offset,
65 std::size_t size = npos,
67 ) const;
68
69 template <typename T>
70 std::size_t Read(T&& value, BufferCategory cat = BufferCategory::kKeepCategory, std::size_t length = sizeof(T));
71 template <typename T>
72 std::size_t Read(
73 T&& value,
74 const TypeBufferCategory& categories,
75 std::size_t length = sizeof(T),
77 );
78
79 // Read 'raw' postgres buffer - first read the size, then read the value
80 template <typename T>
81 std::size_t ReadRaw(
82 T&& value,
83 const TypeBufferCategory& categories,
85 );
86};
87
88/// @brief Primary template for Postgre buffer parser.
89/// Specialisations should provide call operators that parse FieldBuffer.
90template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
91struct BufferParser;
92
93/// @brief Primary template for Postgre buffer formatter
94/// Specialisations should provide call operators that write to a buffer.
95template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
96struct BufferFormatter;
97
98namespace traits {
99
100/// Customisation point for parsers
101template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
102struct Input {
103 using type = BufferParser<T>;
104};
105
106/// Customisation point for formatters
107template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
108struct Output {
109 using type = BufferFormatter<T>;
110};
111
112/// @brief A default deducer of parsers/formatters for a type/data format.
113/// Can be specialised for a type/format pair providing custom
114/// parsers/formatters.
115template <typename T>
116struct IO {
117 using ParserType = typename Input<T>::type;
118 using FormatterType = typename Output<T>::type;
119};
120
121/// @brief Metafunction to detect if a type has a parser.
122template <typename T>
123struct HasParser : utils::IsDeclComplete<typename IO<T>::ParserType> {};
124
125/// @brief Metafunction to detect if a type has a formatter.
126template <typename T>
127struct HasFormatter : utils::IsDeclComplete<typename IO<T>::FormatterType> {};
128
129//@{
130/** @name Shortcut metafunction result values */
131template <typename T>
132inline constexpr bool kHasParser = HasParser<T>::value;
133template <typename T>
134inline constexpr bool kHasFormatter = HasFormatter<T>::value;
135//@}
136
137template <typename T>
138constexpr bool CheckParser() {
139 static_assert(
140 kHasParser<T> || std::is_enum_v<T>,
141 "Type doesn't have a parser. Probably you forgot to include "
142 "file with parser or to define your own. Please see page "
143 "`uPg: Supported data types` for more information"
144 );
145
146 static_assert(
147 kHasParser<T> || !std::is_enum_v<T>,
148 "Type doesn't have a parser. Probably you forgot to include "
149 "file with parser, to define your own or to specialize "
150 "`storages::postgres::io::traits::CanUseEnumAsStrongTypedef`. "
151 "See page `uPg: Supported data types` for more information"
152 );
153
154 return true;
155}
156
157template <typename T>
158constexpr void CheckFormatter() {
159 static_assert(
160 kHasFormatter<T> || std::is_enum_v<T>,
161 "Type doesn't have a formatter. Probably you forgot to include "
162 "file with formatter or to define your own. Please see page "
163 "`uPg: Supported data types` for more information"
164 );
165
166 static_assert(
167 kHasFormatter<T> || !std::is_enum_v<T>,
168 "Type doesn't have a formatter. Probably you forgot to include "
169 "file with formatter, to define your own or to specialize "
170 "`storages::postgres::io::traits::CanUseEnumAsStrongTypedef`. "
171 "See page `uPg: Supported data types` for more information"
172 );
173}
174
175/// Buffer category for parser
176template <typename T>
177struct ParserBufferCategory : BufferCategoryConstant<BufferCategory::kPlainBuffer> {};
178template <typename T>
179using ParserBufferCategoryType = typename ParserBufferCategory<T>::type;
180template <typename T>
181inline constexpr BufferCategory kParserBufferCategory = ParserBufferCategory<T>::value;
182
183//@{
184/** @name Buffer category for a type */
185namespace detail {
186template <typename T>
187constexpr auto DetectBufferCategory() {
188 if constexpr (kHasParser<T>) {
189 return ParserBufferCategoryType<typename IO<T>::ParserType>{};
190 } else {
191 return BufferCategoryConstant<BufferCategory::kNoParser>{};
192 }
193}
194} // namespace detail
195template <typename T>
196struct TypeBufferCategory : decltype(detail::DetectBufferCategory<T>()) {};
197template <typename T>
198inline constexpr BufferCategory kTypeBufferCategory = TypeBufferCategory<T>::value;
199//@}
200
201namespace detail {
202
203template <typename T>
204struct CustomParserDefined : utils::IsDeclComplete<BufferParser<T>> {};
205template <typename T>
206inline constexpr bool kCustomParserDefined = CustomParserDefined<T>::value;
207
208template <typename T>
209struct CustomFormatterDefined : utils::IsDeclComplete<BufferFormatter<T>> {};
210template <typename T>
211inline constexpr bool kCustomFormatterDefined = CustomFormatterDefined<T>::value;
212
213} // namespace detail
214
215} // namespace traits
216
217} // namespace io
218} // namespace storages::postgres
219
220USERVER_NAMESPACE_END