Lightweight 0.20260303.0
Loading...
Searching...
No Matches
SqlVariant.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../DataBinder/UnicodeConverter.hpp"
6#include "../SqlLogger.hpp"
7#include "Core.hpp"
8#include "Primitives.hpp"
9#include "SqlDate.hpp"
10#include "SqlDateTime.hpp"
11#include "SqlFixedString.hpp"
12#include "SqlGuid.hpp"
13#include "SqlNullValue.hpp"
14#include "SqlText.hpp"
15#include "SqlTime.hpp"
16#include "StdString.hpp"
17#include "StdStringView.hpp"
18
19#include <format>
20#include <print>
21#include <variant>
22
23namespace Lightweight
24{
25
26namespace detail
27{
28 template <class... Ts>
29 struct overloaded: Ts... // NOLINT(readability-identifier-naming)
30 {
31 using Ts::operator()...;
32 };
33
34 template <class... Ts>
35 overloaded(Ts...) -> overloaded<Ts...>;
36
37} // namespace detail
38
39/// @brief Represents a value that can be any of the supported SQL data types.
40///
41/// Use this class with care. Always prefer native types when possible, in order to avoid any unnecessary overhead.
42///
43/// @ingroup DataTypes
45{
46 /// @brief The inner type of the variant.
47 ///
48 /// This type is a variant of all the supported SQL data types.
49 using InnerType = std::variant<SqlNullType,
50 SqlGuid,
51 bool,
52 int8_t,
53 short,
54 unsigned short,
55 int,
56 unsigned int,
57 long long,
58 unsigned long long,
59 float,
60 double,
61 std::string,
62 std::string_view,
63 std::u16string,
64 std::u16string_view,
65 SqlText,
66 SqlDate,
67 SqlTime,
69
70 /// The variant value.
72
73 /// @brief Default construct a new SqlVariant.
74 SqlVariant() = default;
75 /// @brief Copy construct a new SqlVariant from another.
76 SqlVariant(SqlVariant const&) = default;
77 /// @brief Move construct a new SqlVariant from another.
78 SqlVariant(SqlVariant&&) noexcept = default;
79 /// @brief Copy assign a new SqlVariant from another.
80 SqlVariant& operator=(SqlVariant const&) = default;
81 /// @brief Move assign a new SqlVariant from another.
82 SqlVariant& operator=(SqlVariant&&) noexcept = default;
83 /// @brief Destructor for SqlVariant.
84 ~SqlVariant() = default;
85
86 /// @brief Copy constructor of a SqlVariant from one of the supported types.
87 LIGHTWEIGHT_FORCE_INLINE SqlVariant(InnerType const& other):
88 value(other)
89 {
90 }
91
92 /// @brief Move constructor of a SqlVariant from one of the supported types.
93 LIGHTWEIGHT_FORCE_INLINE SqlVariant(InnerType&& other) noexcept:
94 value(std::move(other))
95 {
96 }
97
98 /// @brief Construct a new SqlVariant from a SqlFixedString.
99 template <std::size_t N, typename T = char, SqlFixedStringMode Mode>
100 constexpr LIGHTWEIGHT_FORCE_INLINE SqlVariant(SqlFixedString<N, T, Mode> const& other):
101 value { std::string { other.data(), other.size() } }
102 {
103 }
104
105 /// @brief Copy constructor of a SqlVariant from a char array.
106 template <std::size_t TextSize>
107 constexpr LIGHTWEIGHT_FORCE_INLINE SqlVariant(char const (&text)[TextSize]):
108 value { std::string_view { text, TextSize - 1 } }
109 {
110 }
111
112 /// @brief Copy constructor of a SqlVariant from a char16_t array.
113 template <std::size_t TextSize>
114 constexpr LIGHTWEIGHT_FORCE_INLINE SqlVariant(char16_t const (&text)[TextSize]):
115 value { std::u16string_view { text, TextSize - 1 } }
116 {
117 }
118
119 /// @brief Copy constructor of a SqlVariant from an optional of one of the supported types.
120 template <typename T>
121 LIGHTWEIGHT_FORCE_INLINE SqlVariant(std::optional<T> const& other):
122 value { other ? InnerType { *other } : InnerType { SqlNullValue } }
123 {
124 }
125
126 /// @brief Assignment operator of a SqlVariant from one of the supported types.
127 LIGHTWEIGHT_FORCE_INLINE SqlVariant& operator=(InnerType const& other)
128 {
129 value = other;
130 return *this;
131 }
132
133 /// @brief Assignment operator of a SqlVariant from one of the supported types.
134 LIGHTWEIGHT_FORCE_INLINE SqlVariant& operator=(InnerType&& other) noexcept
135 {
136 value = std::move(other);
137 return *this;
138 }
139
140 /// @brief Construct from an string-like object that implements an SqlViewHelper<>.
141 template <detail::HasSqlViewHelper StringViewLike>
142 LIGHTWEIGHT_FORCE_INLINE explicit SqlVariant(StringViewLike const* newValue):
143 value { detail::SqlViewHelper<std::remove_cv_t<decltype(*newValue)>>::View(*newValue) }
144 {
145 }
146
147 /// @brief Assign from an string-like object that implements an SqlViewHelper<>.
148 template <detail::HasSqlViewHelper StringViewLike>
149 LIGHTWEIGHT_FORCE_INLINE SqlVariant& operator=(StringViewLike const* newValue) noexcept
150 {
151 value = std::string_view(newValue->GetString(), newValue->GetLength());
152 return *this;
153 }
154
155 /// @brief Check if the value is NULL.
156 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE bool IsNull() const noexcept
157 {
158 return std::holds_alternative<SqlNullType>(value);
159 }
160
161 /// @brief Check if the value is of the specified type.
162 template <typename T>
163 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE bool Is() const noexcept
164 {
165 return std::holds_alternative<T>(value);
166 }
167
168 /// @brief Retrieve the value as the specified type.
169 template <typename T>
170 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE decltype(auto) Get() noexcept
171 {
172 if constexpr (IsSpecializationOf<std::optional, T>)
173 {
174 if (IsNull())
175 return T { std::nullopt };
176 else
177 return T { std::get<typename T::value_type>(value) };
178 }
179 else
180 return std::get<T>(value);
181 }
182
183 /// @brief Retrieve the value as the specified type, or return the default value if the value is NULL.
184 template <typename T>
185 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE T ValueOr(T&& defaultValue) const noexcept
186 {
187 if constexpr (std::is_integral_v<T>)
188 return TryGetIntegral<T>().value_or(std::forward<T>(defaultValue));
189
190 if (IsNull())
191 return std::forward<T>(defaultValue);
192
193 return std::get<T>(value);
194 }
195
196 // clang-format off
197 /// @brief Retrieve the bool from the variant or std::nullopt
198 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<bool> TryGetBool() const noexcept { return TryGetIntegral<bool>(); }
199 /// @brief Retrieve the int8_t from the variant or std::nullopt
200 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<int8_t> TryGetInt8() const noexcept { return TryGetIntegral<int8_t>(); }
201 /// @brief Retrieve the unsigned short from the variant or std::nullopt
202 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<short> TryGetShort() const noexcept { return TryGetIntegral<short>(); }
203 /// @brief Retrieve the unsigned short from the variant or std::nullopt
204 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<unsigned short> TryGetUShort() const noexcept { return TryGetIntegral<unsigned short>(); }
205 /// @brief Retrieve the int from the variant or std::nullopt
206 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<int> TryGetInt() const noexcept { return TryGetIntegral<int>(); }
207 /// @brief Retrieve the unsigned int from the variant or std::nullopt
208 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<unsigned int> TryGetUInt() const noexcept { return TryGetIntegral<unsigned int>(); }
209 /// @brief Retrieve the long long from the variant or std::nullopt
210 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<long long> TryGetLongLong() const noexcept { return TryGetIntegral<long long>(); }
211 /// @brief Retrieve the unsigned long long from the variant or std::nullopt
212 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<unsigned long long> TryGetULongLong() const noexcept { return TryGetIntegral<unsigned long long>(); }
213 // clang-format on
214
215 private:
216 /// @brief template that is used to get integral types
217 template <typename ResultType>
218 [[nodiscard]] std::optional<ResultType> TryGetIntegral() const noexcept
219 {
220 if (IsNull())
221 return std::nullopt;
222
223 // clang-format off
224 return std::visit(detail::overloaded {
225 []<typename T>(T v) -> ResultType requires(std::is_integral_v<T>) { return static_cast<ResultType>(v); },
226 [](auto) -> ResultType { throw std::bad_variant_access(); } // NOLINT(performance-unnecessary-value-param)
227 }, value);
228 // clang-format on
229 }
230
231 public:
232 /// @brief function to get string_view from SqlVariant or std::nullopt
233 [[nodiscard]] std::optional<std::string_view> TryGetStringView() const noexcept
234 {
235 if (IsNull())
236 return std::nullopt;
237
238 // clang-format off
239 return std::visit(detail::overloaded {
240 [](std::string_view v) { return v; },
241 [](std::string const& v) { return std::string_view(v.data(), v.size()); },
242 [](SqlText const& v) { return std::string_view(v.value.data(), v.value.size()); },
243 [](auto const&) -> std::string_view { throw std::bad_variant_access(); }
244 }, value);
245 // clang-format on
246 }
247
248 /// @brief Retrieves a UTF-16 string view from the variant, or std::nullopt if not available.
249 [[nodiscard]] std::optional<std::u16string_view> TryGetUtf16StringView() const noexcept
250 {
251 if (IsNull())
252 return std::nullopt;
253
254 // clang-format off
255 return std::visit(detail::overloaded {
256 [](std::u16string_view v) { return v; },
257 [](std::u16string const& v) { return std::u16string_view(v.data(), v.size()); },
258 [](auto const&) -> std::u16string_view { throw std::bad_variant_access(); }
259 }, value);
260 // clang-format on
261 }
262
263 /// @brief function to get SqlDate from SqlVariant or std::nullopt
264 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<SqlDate> TryGetDate() const
265 {
266 if (IsNull())
267 return std::nullopt;
268
269 if (auto const* date = std::get_if<SqlDate>(&value))
270 return *date;
271
272 if (auto const* datetime = std::get_if<SqlDateTime>(&value))
273 {
274 return SqlDate { std::chrono::year(datetime->sqlValue.year),
275 std::chrono::month(datetime->sqlValue.month),
276 std::chrono::day(datetime->sqlValue.day) };
277 }
278
279 throw std::bad_variant_access();
280 }
281
282 /// @brief Equality comparison operator.
283 [[nodiscard]] bool operator==(SqlVariant const& other) const noexcept
284 {
285 return ToString() == other.ToString();
286 }
287
288 /// @brief Inequality comparison operator.
289 [[nodiscard]] bool operator!=(SqlVariant const& other) const noexcept
290 {
291 return !(*this == other);
292 }
293
294 /// @brief function to get SqlTime from SqlVariant or std::nullopt
295 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<SqlTime> TryGetTime() const
296 {
297 if (IsNull())
298 return std::nullopt;
299
300 if (auto const* time = std::get_if<SqlTime>(&value))
301 return *time;
302
303 if (auto const* datetime = std::get_if<SqlDateTime>(&value))
304 {
305 return SqlTime { std::chrono::hours(datetime->sqlValue.hour),
306 std::chrono::minutes(datetime->sqlValue.minute),
307 std::chrono::seconds(datetime->sqlValue.second),
308 std::chrono::microseconds(datetime->sqlValue.fraction) };
309 }
310
311 throw std::bad_variant_access();
312 }
313
314 /// @brief function to get SqlDateTime from SqlVariant or std::nullopt
315 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<SqlDateTime> TryGetDateTime() const
316 {
317 if (IsNull())
318 return std::nullopt;
319
320 if (auto const* dateTime = std::get_if<SqlDateTime>(&value))
321 return *dateTime;
322
323 throw std::bad_variant_access();
324 }
325
326 /// @brief Retrieve the GUID from the variant or std::nullopt if the value is NULL.
327 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<SqlGuid> TryGetGuid() const
328 {
329 if (IsNull())
330 return std::nullopt;
331
332 if (auto const* guid = std::get_if<SqlGuid>(&value))
333 return *guid;
334
335 throw std::bad_variant_access();
336 }
337
338 /// @brief Create string representation of the variant. Can be used for debug purposes
339 [[nodiscard]] LIGHTWEIGHT_API std::string ToString() const;
340};
341
342/// @brief Represents a row of data from the database using SqlVariant as the column data type.
343using SqlVariantRow = std::vector<SqlVariant>;
344
345template <>
346struct LIGHTWEIGHT_API SqlDataBinder<SqlVariant>
347{
348 static SQLRETURN InputParameter(SQLHSTMT stmt,
349 SQLUSMALLINT column,
350 SqlVariant const& variantValue,
351 SqlDataBinderCallback& cb) noexcept;
352
353 static SQLRETURN GetColumn(
354 SQLHSTMT stmt, SQLUSMALLINT column, SqlVariant* result, SQLLEN* indicator, SqlDataBinderCallback const& cb) noexcept;
355
356 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(SqlVariant const& value) noexcept
357 {
358 return value.ToString();
359 }
360};
361
362} // namespace Lightweight
363
364template <>
365struct std::formatter<Lightweight::SqlVariant>: formatter<string>
366{
367 auto format(Lightweight::SqlVariant const& value, format_context& ctx) const -> format_context::iterator
368 {
369 return std::formatter<string>::format(value.ToString(), ctx);
370 }
371};
constexpr auto SqlNullValue
Represents a value that can be any of the supported SQL data types.
LIGHTWEIGHT_FORCE_INLINE SqlVariant(InnerType &&other) noexcept
Move constructor of a SqlVariant from one of the supported types.
LIGHTWEIGHT_API std::string ToString() const
Create string representation of the variant. Can be used for debug purposes.
LIGHTWEIGHT_FORCE_INLINE std::optional< unsigned long long > TryGetULongLong() const noexcept
Retrieve the unsigned long long from the variant or std::nullopt.
LIGHTWEIGHT_FORCE_INLINE SqlVariant(std::optional< T > const &other)
Copy constructor of a SqlVariant from an optional of one of the supported types.
LIGHTWEIGHT_FORCE_INLINE std::optional< SqlDateTime > TryGetDateTime() const
function to get SqlDateTime from SqlVariant or std::nullopt
constexpr LIGHTWEIGHT_FORCE_INLINE SqlVariant(SqlFixedString< N, T, Mode > const &other)
Construct a new SqlVariant from a SqlFixedString.
LIGHTWEIGHT_FORCE_INLINE bool Is() const noexcept
Check if the value is of the specified type.
LIGHTWEIGHT_FORCE_INLINE T ValueOr(T &&defaultValue) const noexcept
Retrieve the value as the specified type, or return the default value if the value is NULL.
std::variant< SqlNullType, SqlGuid, bool, int8_t, short, unsigned short, int, unsigned int, long long, unsigned long long, float, double, std::string, std::string_view, std::u16string, std::u16string_view, SqlText, SqlDate, SqlTime, SqlDateTime > InnerType
The inner type of the variant.
SqlVariant(SqlVariant const &)=default
Copy construct a new SqlVariant from another.
constexpr LIGHTWEIGHT_FORCE_INLINE SqlVariant(char16_t const (&text)[TextSize])
Copy constructor of a SqlVariant from a char16_t array.
LIGHTWEIGHT_FORCE_INLINE std::optional< SqlGuid > TryGetGuid() const
Retrieve the GUID from the variant or std::nullopt if the value is NULL.
std::optional< std::string_view > TryGetStringView() const noexcept
function to get string_view from SqlVariant or std::nullopt
bool operator!=(SqlVariant const &other) const noexcept
Inequality comparison operator.
std::optional< std::u16string_view > TryGetUtf16StringView() const noexcept
Retrieves a UTF-16 string view from the variant, or std::nullopt if not available.
LIGHTWEIGHT_FORCE_INLINE SqlVariant & operator=(InnerType &&other) noexcept
Assignment operator of a SqlVariant from one of the supported types.
InnerType value
The variant value.
SqlVariant()=default
Default construct a new SqlVariant.
LIGHTWEIGHT_FORCE_INLINE std::optional< int > TryGetInt() const noexcept
Retrieve the int from the variant or std::nullopt.
LIGHTWEIGHT_FORCE_INLINE SqlVariant(StringViewLike const *newValue)
Construct from an string-like object that implements an SqlViewHelper<>.
LIGHTWEIGHT_FORCE_INLINE bool IsNull() const noexcept
Check if the value is NULL.
LIGHTWEIGHT_FORCE_INLINE std::optional< SqlTime > TryGetTime() const
function to get SqlTime from SqlVariant or std::nullopt
constexpr LIGHTWEIGHT_FORCE_INLINE SqlVariant(char const (&text)[TextSize])
Copy constructor of a SqlVariant from a char array.
LIGHTWEIGHT_FORCE_INLINE SqlVariant & operator=(StringViewLike const *newValue) noexcept
Assign from an string-like object that implements an SqlViewHelper<>.
bool operator==(SqlVariant const &other) const noexcept
Equality comparison operator.
LIGHTWEIGHT_FORCE_INLINE std::optional< SqlDate > TryGetDate() const
function to get SqlDate from SqlVariant or std::nullopt
LIGHTWEIGHT_FORCE_INLINE decltype(auto) Get() noexcept
Retrieve the value as the specified type.
LIGHTWEIGHT_FORCE_INLINE std::optional< short > TryGetShort() const noexcept
Retrieve the unsigned short from the variant or std::nullopt.
LIGHTWEIGHT_FORCE_INLINE std::optional< unsigned short > TryGetUShort() const noexcept
Retrieve the unsigned short from the variant or std::nullopt.
LIGHTWEIGHT_FORCE_INLINE std::optional< bool > TryGetBool() const noexcept
Retrieve the bool from the variant or std::nullopt.
LIGHTWEIGHT_FORCE_INLINE std::optional< int8_t > TryGetInt8() const noexcept
Retrieve the int8_t from the variant or std::nullopt.
SqlVariant(SqlVariant &&) noexcept=default
Move construct a new SqlVariant from another.
LIGHTWEIGHT_FORCE_INLINE std::optional< long long > TryGetLongLong() const noexcept
Retrieve the long long from the variant or std::nullopt.
LIGHTWEIGHT_FORCE_INLINE SqlVariant & operator=(InnerType const &other)
Assignment operator of a SqlVariant from one of the supported types.
LIGHTWEIGHT_FORCE_INLINE std::optional< unsigned int > TryGetUInt() const noexcept
Retrieve the unsigned int from the variant or std::nullopt.