userver: userver/storages/postgres/field.hpp Source File
Loading...
Searching...
No Matches
field.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @brief Result field accessors
5
6#include <cstddef>
7#include <type_traits>
8#include <utility>
9
10#include <userver/storages/postgres/exceptions.hpp>
11#include <userver/storages/postgres/io/supported_types.hpp>
12
13#include <userver/storages/postgres/detail/const_data_iterator.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace storages::postgres {
18
20 /// Index of the field in the result set
21 std::size_t index;
22 /// @brief The object ID of the field's data type.
24 /// @brief The field name.
25 // TODO string_view
26 std::string name;
27 /// @brief If the field can be identified as a column of a specific table,
28 /// the object ID of the table; otherwise zero.
30 /// @brief If the field can be identified as a column of a specific table,
31 /// the attribute number of the column; otherwise zero.
32 Integer table_column;
33 /// @brief The data type size (see pg_type.typlen). Note that negative
34 /// values denote variable-width types.
35 Integer type_size;
36 /// @brief The type modifier (see pg_attribute.atttypmod). The meaning of
37 /// the modifier is type-specific.
39};
40
41class FieldView final {
42public:
43 using size_type = std::size_t;
44
45 FieldView(const detail::ResultWrapper& res, size_type row_index, size_type field_index)
46 : res_{res},
47 row_index_{row_index},
48 field_index_{field_index}
49 {}
50
51 template <typename T>
52 size_type To(T&& val) const {
53 using ValueType = typename std::decay<T>::type;
54 auto fb = GetBuffer();
55 return ReadNullable(fb, std::forward<T>(val), io::traits::IsNullable<ValueType>{});
56 }
57
58private:
59 io::FieldBuffer GetBuffer() const;
60 std::string_view Name() const;
61 Oid GetTypeOid() const;
62 const io::TypeBufferCategory& GetTypeBufferCategories() const;
63
64 template <typename T>
65 size_type ReadNullable(const io::FieldBuffer& fb, T&& val, std::true_type) const {
66 using ValueType = typename std::decay<T>::type;
67 using NullSetter = io::traits::GetSetNull<ValueType>;
68 if (fb.is_null) {
69 NullSetter::SetNull(val);
70 } else {
71 Read(fb, std::forward<T>(val));
72 }
73 return fb.length;
74 }
75
76 template <typename T>
77 size_type ReadNullable(const io::FieldBuffer& buffer, T&& val, std::false_type) const {
78 if (buffer.is_null) {
79 throw FieldValueIsNull{field_index_, Name(), val};
80 } else {
81 Read(buffer, std::forward<T>(val));
82 }
83 return buffer.length;
84 }
85
86 template <typename T>
87 void Read(const io::FieldBuffer& buffer, T&& val) const {
88 using ValueType = typename std::decay<T>::type;
89 io::traits::CheckParser<ValueType>();
90 try {
91 io::ReadBuffer(buffer, std::forward<T>(val), GetTypeBufferCategories());
92 } catch (InvalidInputBufferSize& ex) {
93 // InvalidInputBufferSize is not descriptive. Enriching with OID information and C++ types info
94 ex.AddMsgPrefix(fmt::format(
95 "Error while reading field #{0} '{1}' which database type {2} as a C++ type '{3}'. Refer to "
96 "the 'Supported data types' in the documentation to make sure that the database type is actually "
97 "representable as a C++ type '{3}'. Error details: ",
98 field_index_,
99 Name(),
100 impl::OidPrettyPrint(GetTypeOid()),
101 compiler::GetTypeName<T>()
102 ));
103 UASSERT_MSG(false, ex.what());
104 throw;
105 } catch (ResultSetError& ex) {
106 ex.AddMsgSuffix(fmt::format(" (ResultSet error while reading field #{} name `{}`)", field_index_, Name()));
107 throw;
108 }
109 }
110
111 const detail::ResultWrapper& res_;
112 const size_type row_index_;
113 const size_type field_index_;
114};
115
116/// @brief Accessor to a single field in a result set's row
117class Field {
118public:
119 using size_type = std::size_t;
120
121 size_type RowIndex() const { return row_index_; }
122 size_type FieldIndex() const { return field_index_; }
123
124 //@{
125 /** @name Field metadata */
126 /// Field name as named in query
127 std::string_view Name() const;
128 FieldDescription Description() const;
129
130 Oid GetTypeOid() const;
131 //@}
132
133 //@{
134 /** @name Data access */
135 bool IsNull() const;
136
137 size_type Length() const;
138
139 /// Read the field's buffer into user-provided variable.
140 /// @throws FieldValueIsNull If the field is null and the C++ type is
141 /// not nullable.
142 template <typename T>
143 size_type To(T&& val) const {
144 return FieldView{*res_, row_index_, field_index_}.To(std::forward<T>(val));
145 }
146
147 /// Read the field's buffer into user-provided variable.
148 /// If the field is null, set the variable to the default value.
149 template <typename T>
150 void Coalesce(T& val, const T& default_val) const {
151 if (!IsNull()) {
152 To(val);
153 } else {
154 val = default_val;
155 }
156 }
157
158 /// Convert the field's buffer into a C++ type.
159 /// @throws FieldValueIsNull If the field is null and the C++ type is
160 /// not nullable.
161 template <typename T>
162 typename std::decay<T>::type As() const {
163 T val{};
164 To(val);
165 return val;
166 }
167
168 /// Convert the field's buffer into a C++ type.
169 /// If the field is null, return default value.
170 template <typename T>
171 typename std::decay<T>::type Coalesce(const T& default_val) const {
172 if (IsNull()) {
173 return default_val;
174 }
175 return As<T>();
176 }
177 //@}
178 const io::TypeBufferCategory& GetTypeBufferCategories() const;
179
180protected:
181 friend class Row;
182
183 Field() = default;
184
185 Field(detail::ResultWrapperPtr res, size_type row, size_type col)
186 : res_{std::move(res)},
187 row_index_{row},
188 field_index_{col}
189 {}
190
191 //@{
192 /** @name Iteration support */
193 bool IsValid() const;
194 int Compare(const Field& rhs) const;
195 std::ptrdiff_t Distance(const Field& rhs) const;
196 Field& Advance(std::ptrdiff_t);
197 //@}
198
199private:
200 detail::ResultWrapperPtr res_;
201 size_type row_index_{0};
202 size_type field_index_{0};
203};
204
205/// @brief Iterator over fields in a result set's row
207 : public detail::ConstDataIterator<ConstFieldIterator, Field, detail::IteratorDirection::kForward> {
208public:
209 ConstFieldIterator() = default;
210
211private:
212 friend class Row;
213
214 ConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
215 : ConstDataIterator(std::move(res), row, col)
216 {}
217};
218
219/// @brief Reverse iterator over fields in a result set's row
221 : public detail::ConstDataIterator<ReverseConstFieldIterator, Field, detail::IteratorDirection::kReverse> {
222public:
223 ReverseConstFieldIterator() = default;
224
225private:
226 friend class Row;
227
228 ReverseConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
229 : ConstDataIterator(std::move(res), row, col)
230 {}
231};
232
233} // namespace storages::postgres
234
235USERVER_NAMESPACE_END