userver: /data/code/userver/odbc/include/userver/storages/odbc/field.hpp Source File
Loading...
Searching...
No Matches
field.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/odbc/field.hpp
4/// @brief @copybrief storages::odbc::Field
5
6#include <concepts>
7#include <cstdint>
8#include <limits>
9#include <optional>
10#include <stdexcept>
11#include <string>
12#include <type_traits>
13#include <utility>
14
15#include <userver/storages/odbc/exception.hpp>
16#include <userver/storages/odbc/io/type_mapping.hpp>
17#include <userver/storages/odbc/odbc_fwd.hpp>
18#include <userver/storages/odbc/types.hpp>
19
20USERVER_NAMESPACE_BEGIN
21
22namespace storages::odbc {
23
24/// @cond
25namespace impl {
26
27template <typename T>
28struct IsOptional : std::false_type {};
29
30template <typename T>
31struct IsOptional<std::optional<T>> : std::true_type {
32 using ValueType = T;
33};
34
35template <typename T>
36inline constexpr bool kIsOptional = IsOptional<std::remove_cv_t<T>>::value;
37
38template <typename T>
39inline constexpr bool kIsFieldScalar =
40 std::same_as<std::remove_cv_t<T>, bool> ||
41 (std::integral<std::remove_cv_t<T>> && !std::same_as<std::remove_cv_t<T>, bool> &&
42 sizeof(std::remove_cv_t<T>) <= sizeof(std::uint64_t)) ||
43 std::same_as<std::remove_cv_t<T>, float> || std::same_as<std::remove_cv_t<T>, double> ||
44 std::same_as<std::remove_cv_t<T>, std::string> || std::same_as<std::remove_cv_t<T>, Bytes> ||
45 std::same_as<std::remove_cv_t<T>, Date> || std::same_as<std::remove_cv_t<T>, Time> ||
46 std::same_as<std::remove_cv_t<T>, Timestamp> || kIsDecimal<std::remove_cv_t<T>>;
47
48template <typename T>
49struct IsFieldAsType
50 : std::bool_constant<
51 kIsFieldScalar<T> || (!kIsFieldScalar<T> && io::traits::kHasFromOdbc<std::remove_cvref_t<T>>)> {};
52
53template <typename T>
54struct IsFieldAsType<std::optional<T>>
55 : std::bool_constant<
56 !io::traits::kHasMappingDeclaration<std::optional<T>> &&
57 (kIsFieldScalar<T> || (!kIsFieldScalar<T> && io::traits::kHasFromOdbc<T>))> {};
58
59template <typename T>
60inline constexpr bool kIsFieldAsType = IsFieldAsType<std::remove_cv_t<T>>::value;
61
62} // namespace impl
63/// @endcond
64
65/// @brief Single cell in an ODBC result set row
66class Field {
67public:
68 using size_type = std::size_t;
69
70 size_type RowIndex() const { return row_index_; }
71 size_type FieldIndex() const { return field_index_; }
72
73 bool IsNull() const;
74
75 std::string GetString() const;
76 int64_t GetInt64() const;
77 int32_t GetInt32() const;
78 double GetDouble() const;
79 bool GetBool() const;
80
81 /// Converts the field with strict SQL category, NULL and range checks.
82 /// Use `As<std::optional<T>>()` to accept SQL NULL.
83 template <typename T>
84 T As() const;
85
86protected:
87 friend class Row;
88
89 Field() = default;
90
91 Field(detail::ResultWrapperPtr res, size_type row, size_type col)
92 : res_{std::move(res)},
93 row_index_{row},
94 field_index_{col}
95 {}
96
97private:
98 std::int64_t GetSignedIntegerForAs() const;
99 std::uint64_t GetUnsignedIntegerForAs() const;
100 double GetFloatingPointForAs() const;
101 std::string GetStringForAs() const;
102 bool GetBoolForAs() const;
103 Bytes GetBytesForAs() const;
104 Date GetDateForAs() const;
105 Time GetTimeForAs() const;
106 Timestamp GetTimestampForAs() const;
107 std::string GetDecimalForAs(std::size_t precision, std::size_t scale) const;
108
109 detail::ResultWrapperPtr res_;
110 size_type row_index_{0};
111 size_type field_index_{0};
112};
113
114template <typename T>
115T Field::As() const {
116 using Value = std::remove_cv_t<T>;
117 static_assert(
118 !impl::kIsOptional<Value> || !io::traits::kHasMappingDeclaration<Value>,
119 "CppToOdbc<std::optional<T>> is not supported; map T and use std::optional<T> for SQL NULL"
120 );
121 static_assert(impl::kIsFieldAsType<Value>, "Unsupported ODBC Field::As<T>() type");
122
123 if constexpr (impl::kIsOptional<Value>) {
124 using Inner = typename impl::IsOptional<Value>::ValueType;
125 if (IsNull()) {
126 return std::nullopt;
127 }
128 return Value{As<Inner>()};
129 } else if constexpr (!impl::kIsFieldScalar<Value> && io::traits::kHasFromOdbc<Value>) {
130 using Mapping = io::CppToOdbc<Value>;
131 using BoundType = typename Mapping::BoundType;
132 return Mapping::FromOdbc(As<BoundType>());
133 } else if constexpr (std::same_as<Value, bool>) {
134 return GetBoolForAs();
135 } else if constexpr (std::signed_integral<Value>) {
136 const auto value = GetSignedIntegerForAs();
137 if (value < static_cast<std::int64_t>(std::numeric_limits<Value>::lowest()) ||
138 value > static_cast<std::int64_t>(std::numeric_limits<Value>::max()))
139 {
140 throw ResultSetError("ODBC integer field does not fit into the requested signed type");
141 }
142 return static_cast<Value>(value);
143 } else if constexpr (std::unsigned_integral<Value>) {
144 const auto value = GetUnsignedIntegerForAs();
145 if (value > static_cast<std::uint64_t>(std::numeric_limits<Value>::max())) {
146 throw ResultSetError("ODBC integer field does not fit into the requested unsigned type");
147 }
148 return static_cast<Value>(value);
149 } else if constexpr (std::same_as<Value, float>) {
150 const auto value = GetFloatingPointForAs();
151 if (value < static_cast<double>(std::numeric_limits<float>::lowest()) ||
152 value > static_cast<double>(std::numeric_limits<float>::max()))
153 {
154 throw ResultSetError("ODBC floating-point field does not fit into float");
155 }
156 return static_cast<float>(value);
157 } else if constexpr (std::same_as<Value, double>) {
158 return GetFloatingPointForAs();
159 } else if constexpr (std::same_as<Value, std::string>) {
160 return GetStringForAs();
161 } else if constexpr (std::same_as<Value, Bytes>) {
162 return GetBytesForAs();
163 } else if constexpr (std::same_as<Value, Date>) {
164 return GetDateForAs();
165 } else if constexpr (std::same_as<Value, Time>) {
166 return GetTimeForAs();
167 } else if constexpr (std::same_as<Value, Timestamp>) {
168 return GetTimestampForAs();
169 } else if constexpr (impl::kIsDecimal<Value>) {
170 return Value{GetDecimalForAs(Value::kPrecision, Value::kScale)};
171 }
172}
173
174} // namespace storages::odbc
175
176USERVER_NAMESPACE_END