5#include "../SqlColumnTypeDefinitions.hpp"
6#include "../SqlError.hpp"
7#include "Primitives.hpp"
15#include <source_location>
23#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER)
24 #define LIGHTWEIGHT_INT128_T __int128_t
25 static_assert(
sizeof(__int128_t) ==
sizeof(SQL_NUMERIC_STRUCT::val));
37template <std::
size_t ThePrecision, std::
size_t TheScale>
44 static constexpr auto Scale = TheScale;
46 static constexpr auto ColumnType = SqlColumnTypeDefinitions::Decimal { .precision =
Precision, .scale = TheScale };
48 static_assert(
Precision <= SQL_MAX_NUMERIC_LEN);
51 SQL_NUMERIC_STRUCT sqlValue {};
54 double nativeValue {};
56 constexpr SqlNumeric() noexcept = default;
57 constexpr SqlNumeric(SqlNumeric&&) noexcept = default;
58 constexpr SqlNumeric& operator=(SqlNumeric&&) noexcept = default;
59 constexpr SqlNumeric(SqlNumeric const&) noexcept = default;
60 constexpr SqlNumeric& operator=(SqlNumeric const&) noexcept = default;
61 constexpr ~SqlNumeric() noexcept = default;
64 constexpr
SqlNumeric(std::floating_point auto value) noexcept
70 constexpr explicit SqlNumeric(SQL_NUMERIC_STRUCT
const& value)
noexcept:
76 static_assert(std::endian::native == std::endian::little);
79 LIGHTWEIGHT_FORCE_INLINE
constexpr void assign(std::floating_point
auto inputValue)
noexcept
81 nativeValue =
static_cast<decltype(nativeValue)
>(inputValue);
84 sqlValue.sign = std::signbit(inputValue) ? 0 : 1;
85 sqlValue.precision =
static_cast<SQLCHAR
>(
Precision);
86 sqlValue.scale =
static_cast<SQLSCHAR
>(
Scale);
88 auto const unscaledValue = std::roundl(
static_cast<long double>(std::abs(inputValue) * std::powl(10.0L,
Scale)));
89#if defined(LIGHTWEIGHT_INT128_T)
90 auto const num =
static_cast<LIGHTWEIGHT_INT128_T
>(unscaledValue);
91 std::memcpy(sqlValue.val, &num,
sizeof(num));
93 auto const num =
static_cast<int64_t
>(unscaledValue);
94 std::memcpy(sqlValue.val, &num,
sizeof(num));
106 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
auto ToUnscaledValue() const noexcept
108 if (nativeValue != 0.0)
110 auto const unscaledValue = std::roundl(nativeValue * std::powl(10.0L,
Scale));
111#if defined(LIGHTWEIGHT_INT128_T)
112 return static_cast<LIGHTWEIGHT_INT128_T
>(unscaledValue);
114 return static_cast<int64_t
>(unscaledValue);
118 auto const sign = sqlValue.sign ? 1 : -1;
119#if defined(LIGHTWEIGHT_INT128_T)
120 return sign * *
reinterpret_cast<LIGHTWEIGHT_INT128_T const*
>(sqlValue.val);
122 return sign * *
reinterpret_cast<int64_t const*
>(sqlValue.val);
127 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
float ToFloat() const noexcept
129 return static_cast<float>(
ToUnscaledValue()) / std::powf(10, sqlValue.scale);
133 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
double ToDouble() const noexcept
135 return static_cast<double>(
ToUnscaledValue()) / std::pow(10, sqlValue.scale);
139 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
long double ToLongDouble() const noexcept
141 return static_cast<long double>(
ToUnscaledValue()) / std::pow(10, sqlValue.scale);
145 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
explicit operator float() const noexcept
151 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
explicit operator double() const noexcept
157 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
explicit operator long double() const noexcept
163 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::string
ToString()
const
168 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE std::weak_ordering operator<=>(
SqlNumeric const& other)
const noexcept
170 return ToDouble() <=> other.ToDouble();
173 template <std::
size_t OtherPrecision, std::
size_t OtherScale>
174 [[nodiscard]]
constexpr LIGHTWEIGHT_FORCE_INLINE
bool operator==(
175 SqlNumeric<OtherPrecision, OtherScale>
const& other)
const noexcept
177 return ToFloat() == other.ToFloat();
182concept SqlNumericType =
requires(T t) {
183 { T::Precision } -> std::convertible_to<std::size_t>;
184 { T::Scale } -> std::convertible_to<std::size_t>;
185} && std::same_as<T, SqlNumeric<T::Precision, T::Scale>>;
188template <std::
size_t Precision, std::
size_t Scale>
189struct SqlDataBinder<SqlNumeric<Precision, Scale>>
191 using ValueType = SqlNumeric<Precision, Scale>;
193 static constexpr auto ColumnType = SqlColumnTypeDefinitions::Decimal { .precision = Precision, .scale = Scale };
195 static void RequireSuccess(SQLHSTMT stmt, SQLRETURN error, std::source_location sourceLocation = std::source_location::current())
197 if (SQL_SUCCEEDED(error))
203 static constexpr bool NativeNumericSupportIsBroken(SqlServerType serverType)
noexcept
207 return serverType == SqlServerType::SQLITE || serverType == SqlServerType::MICROSOFT_SQL;
210 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
212 ValueType
const& value,
213 SqlDataBinderCallback& cb)
noexcept
215 if (NativeNumericSupportIsBroken(cb.ServerType()))
217 return SQLBindParameter(stmt,
224 (SQLPOINTER) &value.nativeValue,
225 sizeof(value.nativeValue),
229 return SQLBindParameter(stmt,
234 value.sqlValue.precision,
235 value.sqlValue.scale,
242 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN OutputColumn(
243 SQLHSTMT stmt, SQLUSMALLINT column, ValueType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
noexcept
245 if (NativeNumericSupportIsBroken(cb.ServerType()))
247 result->sqlValue = { .precision = Precision, .scale = Scale, .sign = 0, .val = {} };
248 return SQLBindCol(stmt, column, SQL_C_DOUBLE, &result->nativeValue,
sizeof(result->nativeValue), indicator);
252 RequireSuccess(stmt, SQLGetStmtAttr(stmt, SQL_ATTR_APP_ROW_DESC, (SQLPOINTER) &hDesc, 0,
nullptr));
253 RequireSuccess(stmt, SQLSetDescField(hDesc, (SQLSMALLINT) column, SQL_DESC_PRECISION, (SQLPOINTER) Precision, 0));
254 RequireSuccess(stmt, SQLSetDescField(hDesc, (SQLSMALLINT) column, SQL_DESC_SCALE, (SQLPOINTER) Scale, 0));
256 return SQLBindCol(stmt, column, SQL_C_NUMERIC, &result->sqlValue,
sizeof(ValueType), indicator);
259 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN GetColumn(SQLHSTMT stmt, SQLUSMALLINT column, ValueType* result, SQLLEN* indicator, SqlDataBinderCallback
const& cb)
noexcept
261 if (NativeNumericSupportIsBroken(cb.ServerType()))
263 result->sqlValue = { .precision = Precision, .scale = Scale, .sign = 0, .val = {} };
264 return SQLGetData(stmt, column, SQL_C_DOUBLE, &result->nativeValue,
sizeof(result->nativeValue), indicator);
268 RequireSuccess(stmt, SQLGetStmtAttr(stmt, SQL_ATTR_APP_ROW_DESC, (SQLPOINTER) &hDesc, 0,
nullptr));
269 RequireSuccess(stmt, SQLSetDescField(hDesc, (SQLSMALLINT) column, SQL_DESC_PRECISION, (SQLPOINTER) Precision, 0));
270 RequireSuccess(stmt, SQLSetDescField(hDesc, (SQLSMALLINT) column, SQL_DESC_SCALE, (SQLPOINTER) Scale, 0));
272 return SQLGetData(stmt, column, SQL_C_NUMERIC, &result->sqlValue,
sizeof(ValueType), indicator);
275 static LIGHTWEIGHT_FORCE_INLINE std::string Inspect(ValueType
const& value)
noexcept
277 return value.ToString();
284template <Lightweight::SqlNumericType Type>
285struct std::formatter<Type>: std::formatter<std::string>
287 template <
typename FormatContext>
288 auto format(Type
const& value, FormatContext& ctx)
290 return formatter<std::string>::format(value.ToString(), ctx);
static SqlErrorInfo FromStatementHandle(SQLHSTMT hStmt)
Constructs an ODBC error info object from the given ODBC statement handle.
constexpr LIGHTWEIGHT_FORCE_INLINE float ToFloat() const noexcept
Converts the numeric to a floating point value.
LIGHTWEIGHT_FORCE_INLINE constexpr void assign(std::floating_point auto inputValue) noexcept
Assigns a value to the numeric.
constexpr LIGHTWEIGHT_FORCE_INLINE double ToDouble() const noexcept
Converts the numeric to a double precision floating point value.
constexpr SqlNumeric(SQL_NUMERIC_STRUCT const &value) noexcept
Constructs a numeric from a SQL_NUMERIC_STRUCT.
LIGHTWEIGHT_FORCE_INLINE std::string ToString() const
Converts the numeric to a string.
static constexpr auto Precision
Number of total digits.
constexpr LIGHTWEIGHT_FORCE_INLINE auto ToUnscaledValue() const noexcept
Converts the numeric to an unscaled integer value.
static constexpr auto Scale
Number of digits after the decimal point.
constexpr LIGHTWEIGHT_FORCE_INLINE long double ToLongDouble() const noexcept
Converts the numeric to a long double precision floating point value.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlNumeric & operator=(std::floating_point auto value) noexcept
Assigns a floating point value to the numeric.