userver: userver/storages/postgres/field.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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}, row_index_{row_index}, field_index_{field_index} {}
47
48 template <typename T>
49 size_type To(T&& val) const {
50 using ValueType = typename std::decay<T>::type;
51 auto fb = GetBuffer();
52 return ReadNullable(fb, std::forward<T>(val), io::traits::IsNullable<ValueType>{});
53 }
54
55private:
56 io::FieldBuffer GetBuffer() const;
57 std::string_view Name() const;
58 Oid GetTypeOid() const;
59 const io::TypeBufferCategory& GetTypeBufferCategories() const;
60
61 template <typename T>
62 size_type ReadNullable(const io::FieldBuffer& fb, T&& val, std::true_type) const {
63 using ValueType = typename std::decay<T>::type;
64 using NullSetter = io::traits::GetSetNull<ValueType>;
65 if (fb.is_null) {
66 NullSetter::SetNull(val);
67 } else {
68 Read(fb, std::forward<T>(val));
69 }
70 return fb.length;
71 }
72
73 template <typename T>
74 size_type ReadNullable(const io::FieldBuffer& buffer, T&& val, std::false_type) const {
75 if (buffer.is_null) {
76 throw FieldValueIsNull{field_index_, Name(), val};
77 } else {
78 Read(buffer, std::forward<T>(val));
79 }
80 return buffer.length;
81 }
82
83 template <typename T>
84 void Read(const io::FieldBuffer& buffer, T&& val) const {
85 using ValueType = typename std::decay<T>::type;
86 io::traits::CheckParser<ValueType>();
87 try {
88 io::ReadBuffer(buffer, std::forward<T>(val), GetTypeBufferCategories());
89 } catch (InvalidInputBufferSize& ex) {
90 // InvalidInputBufferSize is not descriptive. Enriching with OID information and C++ types info
91 ex.AddMsgPrefix(fmt::format(
92 "Error while reading field #{0} '{1}' which database type {2} as a C++ type '{3}'. Refer to "
93 "the 'Supported data types' in the documentation to make sure that the database type is actually "
94 "representable as a C++ type '{3}'. Error details: ",
95 field_index_,
96 Name(),
97 impl::OidPrettyPrint(GetTypeOid()),
98 compiler::GetTypeName<T>()
99 ));
100 UASSERT_MSG(false, ex.what());
101 throw;
102 } catch (ResultSetError& ex) {
103 ex.AddMsgSuffix(fmt::format(" (ResultSet error while reading field #{} name `{}`)", field_index_, Name()));
104 throw;
105 }
106 }
107
108 const detail::ResultWrapper& res_;
109 const size_type row_index_;
110 const size_type field_index_;
111};
112
113/// @brief Accessor to a single field in a result set's row
114class Field {
115public:
116 using size_type = std::size_t;
117
118 size_type RowIndex() const { return row_index_; }
119 size_type FieldIndex() const { return field_index_; }
120
121 //@{
122 /** @name Field metadata */
123 /// Field name as named in query
124 std::string_view Name() const;
125 FieldDescription Description() const;
126
127 Oid GetTypeOid() const;
128 //@}
129
130 //@{
131 /** @name Data access */
132 bool IsNull() const;
133
134 size_type Length() const;
135
136 /// Read the field's buffer into user-provided variable.
137 /// @throws FieldValueIsNull If the field is null and the C++ type is
138 /// not nullable.
139 template <typename T>
140 size_type To(T&& val) const {
141 return FieldView{*res_, row_index_, field_index_}.To(std::forward<T>(val));
142 }
143
144 /// Read the field's buffer into user-provided variable.
145 /// If the field is null, set the variable to the default value.
146 template <typename T>
147 void Coalesce(T& val, const T& default_val) const {
148 if (!IsNull())
149 To(val);
150 else
151 val = default_val;
152 }
153
154 /// Convert the field's buffer into a C++ type.
155 /// @throws FieldValueIsNull If the field is null and the C++ type is
156 /// not nullable.
157 template <typename T>
158 typename std::decay<T>::type As() const {
159 T val{};
160 To(val);
161 return val;
162 }
163
164 /// Convert the field's buffer into a C++ type.
165 /// If the field is null, return default value.
166 template <typename T>
167 typename std::decay<T>::type Coalesce(const T& default_val) const {
168 if (IsNull()) return default_val;
169 return As<T>();
170 }
171 //@}
172 const io::TypeBufferCategory& GetTypeBufferCategories() const;
173
174protected:
175 friend class Row;
176
177 Field() = default;
178
179 Field(detail::ResultWrapperPtr res, size_type row, size_type col)
180 : res_{std::move(res)}, row_index_{row}, field_index_{col} {}
181
182 //@{
183 /** @name Iteration support */
184 bool IsValid() const;
185 int Compare(const Field& rhs) const;
186 std::ptrdiff_t Distance(const Field& rhs) const;
187 Field& Advance(std::ptrdiff_t);
188 //@}
189
190private:
191 detail::ResultWrapperPtr res_;
192 size_type row_index_{0};
193 size_type field_index_{0};
194};
195
196/// @brief Iterator over fields in a result set's row
198 : public detail::ConstDataIterator<ConstFieldIterator, Field, detail::IteratorDirection::kForward> {
199public:
200 ConstFieldIterator() = default;
201
202private:
203 friend class Row;
204
205 ConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
206 : ConstDataIterator(std::move(res), row, col) {}
207};
208
209/// @brief Reverse iterator over fields in a result set's row
211 : public detail::ConstDataIterator<ReverseConstFieldIterator, Field, detail::IteratorDirection::kReverse> {
212public:
213 ReverseConstFieldIterator() = default;
214
215private:
216 friend class Row;
217
218 ReverseConstFieldIterator(detail::ResultWrapperPtr res, size_type row, size_type col)
219 : ConstDataIterator(std::move(res), row, col) {}
220};
221
222} // namespace storages::postgres
223
224USERVER_NAMESPACE_END