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
35const std::string& 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
44struct BufferCategoryHash {
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
54struct FieldBuffer {
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
64 GetSubBuffer(std::size_t offset, std::size_t size = npos, BufferCategory cat = BufferCategory::kKeepCategory) const;
65
66 template <typename T>
67 std::size_t Read(T&& value, BufferCategory cat = BufferCategory::kKeepCategory, std::size_t length = sizeof(T));
68 template <typename T>
69 std::size_t Read(
70 T&& value,
71 const TypeBufferCategory& categories,
72 std::size_t length = sizeof(T),
74 );
75
76 // Read 'raw' postgres buffer - first read the size, then read the value
77 template <typename T>
78 std::size_t
79 ReadRaw(T&& value, const TypeBufferCategory& categories, BufferCategory cat = BufferCategory::kKeepCategory);
80};
81
82/// @brief Primary template for Postgre buffer parser.
83/// Specialisations should provide call operators that parse FieldBuffer.
84template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
85struct BufferParser;
86
87/// @brief Primary template for Postgre buffer formatter
88/// Specialisations should provide call operators that write to a buffer.
89template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
90struct BufferFormatter;
91
92namespace traits {
93
94/// Customisation point for parsers
95template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
96struct Input {
97 using type = BufferParser<T>;
98};
99
100/// Customisation point for formatters
101template <typename T, typename Enable = USERVER_NAMESPACE::utils::void_t<>>
102struct Output {
103 using type = BufferFormatter<T>;
104};
105
106/// @brief A default deducer of parsers/formatters for a type/data format.
107/// Can be specialised for a type/format pair providing custom
108/// parsers/formatters.
109template <typename T>
110struct IO {
111 using ParserType = typename Input<T>::type;
112 using FormatterType = typename Output<T>::type;
113};
114
115/// @brief Metafunction to detect if a type has a parser.
116template <typename T>
117struct HasParser : utils::IsDeclComplete<typename IO<T>::ParserType> {};
118
119/// @brief Metafunction to detect if a type has a formatter.
120template <typename T>
121struct HasFormatter : utils::IsDeclComplete<typename IO<T>::FormatterType> {};
122
123//@{
124/** @name Shortcut metafunction result values */
125template <typename T>
126inline constexpr bool kHasParser = HasParser<T>::value;
127template <typename T>
128inline constexpr bool kHasFormatter = HasFormatter<T>::value;
129//@}
130
131template <typename T>
132constexpr bool CheckParser() {
133 static_assert(
134 kHasParser<T> || std::is_enum_v<T>,
135 "Type doesn't have a parser. Probably you forgot to include "
136 "file with parser or to define your own. Please see page "
137 "`uPg: Supported data types` for more information"
138 );
139
140 static_assert(
141 kHasParser<T> || !std::is_enum_v<T>,
142 "Type doesn't have a parser. Probably you forgot to include "
143 "file with parser, to define your own or to specialize "
144 "`storages::postgres::io::traits::CanUseEnumAsStrongTypedef`. "
145 "See page `uPg: Supported data types` for more information"
146 );
147
148 return true;
149}
150
151template <typename T>
152constexpr void CheckFormatter() {
153 static_assert(
154 kHasFormatter<T> || std::is_enum_v<T>,
155 "Type doesn't have a formatter. Probably you forgot to include "
156 "file with formatter or to define your own. Please see page "
157 "`uPg: Supported data types` for more information"
158 );
159
160 static_assert(
161 kHasFormatter<T> || !std::is_enum_v<T>,
162 "Type doesn't have a formatter. Probably you forgot to include "
163 "file with formatter, to define your own or to specialize "
164 "`storages::postgres::io::traits::CanUseEnumAsStrongTypedef`. "
165 "See page `uPg: Supported data types` for more information"
166 );
167}
168
169/// Buffer category for parser
170template <typename T>
172template <typename T>
173using ParserBufferCategoryType = typename ParserBufferCategory<T>::type;
174template <typename T>
175inline constexpr BufferCategory kParserBufferCategory = ParserBufferCategory<T>::value;
176
177//@{
178/** @name Buffer category for a type */
179namespace detail {
180template <typename T>
181constexpr auto DetectBufferCategory() {
182 if constexpr (kHasParser<T>) {
183 return ParserBufferCategoryType<typename IO<T>::ParserType>{};
184 } else {
185 return BufferCategoryConstant<BufferCategory::kNoParser>{};
186 }
187}
188} // namespace detail
189template <typename T>
190struct TypeBufferCategory : decltype(detail::DetectBufferCategory<T>()) {};
191template <typename T>
192inline constexpr BufferCategory kTypeBufferCategory = TypeBufferCategory<T>::value;
193//@}
194
195namespace detail {
196
197template <typename T>
198struct CustomParserDefined : utils::IsDeclComplete<BufferParser<T>> {};
199template <typename T>
200inline constexpr bool kCustomParserDefined = CustomParserDefined<T>::value;
201
202template <typename T>
203struct CustomFormatterDefined : utils::IsDeclComplete<BufferFormatter<T>> {};
204template <typename T>
205inline constexpr bool kCustomFormatterDefined = CustomFormatterDefined<T>::value;
206
207} // namespace detail
208
209} // namespace traits
210
211} // namespace io
212} // namespace storages::postgres
213
214USERVER_NAMESPACE_END