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 Concept to detect if a type has a parser.
122template <typename T>
123concept HasParser = utils::IsDeclComplete<typename IO<T>::ParserType>::value;
124
125/// @brief Concept to detect if a type has a formatter.
126template <typename T>
127concept HasFormatter = utils::IsDeclComplete<typename IO<T>::FormatterType>::value;
128
129template <typename T>
130constexpr bool CheckParser() {
131 static_assert(
132 HasParser<T> || std::is_enum_v<T>,
133 "Type doesn't have a parser. Probably you forgot to include "
134 "file with parser or to define your own. Please see page "
135 "`uPg: Supported data types` for more information"
136 );
137
138 static_assert(
139 HasParser<T> || !std::is_enum_v<T>,
140 "Type doesn't have a parser. Probably you forgot to include "
141 "file with parser, to define your own or to specialize "
142 "`storages::postgres::io::traits::CanUseEnumAsStrongTypedef`. "
143 "See page `uPg: Supported data types` for more information"
144 );
145
146 return true;
147}
148
149template <typename T>
150constexpr void CheckFormatter() {
151 static_assert(
152 HasFormatter<T> || std::is_enum_v<T>,
153 "Type doesn't have a formatter. Probably you forgot to include "
154 "file with formatter or to define your own. Please see page "
155 "`uPg: Supported data types` for more information"
156 );
157
158 static_assert(
159 HasFormatter<T> || !std::is_enum_v<T>,
160 "Type doesn't have a formatter. Probably you forgot to include "
161 "file with formatter, to define your own or to specialize "
162 "`storages::postgres::io::traits::CanUseEnumAsStrongTypedef`. "
163 "See page `uPg: Supported data types` for more information"
164 );
165}
166
167/// Buffer category for parser
168template <typename T>
169struct ParserBufferCategory : BufferCategoryConstant<BufferCategory::kPlainBuffer> {};
170template <typename T>
171using ParserBufferCategoryType = typename ParserBufferCategory<T>::type;
172template <typename T>
173inline constexpr BufferCategory kParserBufferCategory = ParserBufferCategory<T>::value;
174
175//@{
176/** @name Buffer category for a type */
177namespace detail {
178template <typename T>
179constexpr auto DetectBufferCategory() {
180 if constexpr (HasParser<T>) {
181 return ParserBufferCategoryType<typename IO<T>::ParserType>{};
182 } else {
183 return BufferCategoryConstant<BufferCategory::kNoParser>{};
184 }
185}
186} // namespace detail
187template <typename T>
188struct TypeBufferCategory : decltype(detail::DetectBufferCategory<T>()) {};
189template <typename T>
190inline constexpr BufferCategory kTypeBufferCategory = TypeBufferCategory<T>::value;
191//@}
192
193namespace detail {
194
195template <typename T>
196concept CustomParserDefined = utils::IsDeclComplete<BufferParser<T>>::value;
197
198template <typename T>
199concept CustomFormatterDefined = utils::IsDeclComplete<BufferFormatter<T>>::value;
200
201} // namespace detail
202
203} // namespace traits
204
205} // namespace io
206} // namespace storages::postgres
207
208USERVER_NAMESPACE_END