11#include "Description.hpp"
12#include "SqlError.hpp"
13#include "SqlOdbcPrelude.hpp"
15#include <reflection-cpp/reflection.hpp>
25#include <source_location>
29#include <system_error>
31#include <unordered_map>
36#if !(defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L)
38 #if defined(__APPLE__)
47#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
48 #include <experimental/meta>
57 template <
typename T,
typename... Comps>
58 concept OneOf = (std::same_as<T, Comps> || ...);
63 inline void AppendUpperHex(std::string& text, std::span<std::byte const> bytes)
65 constexpr auto Digits = std::string_view {
"0123456789ABCDEF" };
66 text.reserve(text.size() + (2 * bytes.size()));
67 for (
auto const byte: bytes)
69 auto const value = std::to_integer<unsigned>(
byte);
70 text += Digits[value >> 4U];
71 text += Digits[value & 0x0FU];
76 constexpr auto AlwaysFalse = std::false_type::value;
78 constexpr auto Finally(
auto&& cleanupRoutine)
noexcept
83 std::remove_cvref_t<
decltype(cleanupRoutine)> cleanup;
89 return Finally { std::forward<decltype(cleanupRoutine)>(cleanupRoutine) };
103 template <
typename T>
104 requires std::is_floating_point_v<T>
105 [[nodiscard]]
inline std::optional<T> ParseFloat(
char const* first,
char const* last)
noexcept
109#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L
113 auto const [ptr, ec] = std::from_chars(first, last, value);
114 if (ec != std::errc {} || ptr != last)
122 static ::locale_t
const cLocale = ::newlocale(LC_NUMERIC_MASK,
"C",
static_cast<::locale_t
>(
nullptr));
123 auto const length =
static_cast<std::size_t
>(last - first);
124 std::array<char, 64> stackBuffer {};
125 std::string heapBuffer;
126 char const* text =
nullptr;
127 if (length < stackBuffer.size())
129 std::ranges::copy(first, last, stackBuffer.begin());
130 stackBuffer[length] =
'\0';
131 text = stackBuffer.data();
135 heapBuffer.assign(first, last);
136 text = heapBuffer.c_str();
139 char* parseEnd =
nullptr;
142 if constexpr (std::is_same_v<T, float>)
143 value = ::strtof_l(text, &parseEnd, cLocale);
144 else if constexpr (std::is_same_v<T, long double>)
145 value = ::strtold_l(text, &parseEnd, cLocale);
147 value =
static_cast<T
>(::strtod_l(text, &parseEnd, cLocale));
149 if (errno == ERANGE || parseEnd != text + length)
158 template <
template <
typename...>
class T,
typename U>
159 struct is_specialization_of: std::false_type
163 template <
template <
typename...>
class T,
typename... Us>
164 struct is_specialization_of<T, T<Us...>>: std::true_type
168 template <
typename T>
169 struct MemberClassTypeHelper;
171 template <
typename M,
typename T>
172 struct MemberClassTypeHelper<M T::*>
174 using type = std::remove_cvref_t<T>;
177 template <
typename Record>
178 struct RecordTableNameImpl
180 static constexpr std::string_view Value = []() {
181 if constexpr (
requires { Record::TableName; })
182 return Record::TableName;
185#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
186 return std::meta::identifier_of(^^Record);
188 auto const typeName = Reflection::TypeNameOf<Record>;
189 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
190 return typeName.substr(i + 1);
200 template <
typename First,
typename Second>
201 struct RecordTableNameImpl<std::tuple<First, Second>>
203 static constexpr std::string_view Value = []() {
204 if constexpr (
requires { First::TableName; })
205 return First::TableName;
208#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
209 return std::meta::identifier_of(^^First);
211 auto const typeName = Reflection::TypeNameOf<First>;
212 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
213 return typeName.substr(i + 1);
220 template <
typename FieldType>
221 constexpr auto ColumnNameOverride = []()
consteval {
222 if constexpr (
requires { FieldType::ColumnNameOverride; })
223 return FieldType::ColumnNameOverride;
225 return std::string_view {};
227#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
228 template <auto reflection>
229 struct FieldNameOfImpl
231 using R =
typename[:std::meta::type_of(reflection):];
232 static constexpr std::string_view value = []()
constexpr -> std::string_view {
233 if constexpr (
requires { R::ColumnNameOverride; })
235 if constexpr (!R::ColumnNameOverride.empty())
236 return R::ColumnNameOverride;
238 return std::meta::identifier_of(reflection);
242 template <
typename ReferencedFieldType, auto F>
243 struct FieldNameOfImpl;
245 template <
typename T, auto F,
typename R>
246 struct FieldNameOfImpl<R T::*, F>
248 static constexpr std::string_view value = []()
constexpr -> std::string_view {
249 if constexpr (
requires { R::ColumnNameOverride; })
251 if constexpr (!R::ColumnNameOverride.empty())
252 return R::ColumnNameOverride;
254 return Reflection::NameOf<F>;
259 template <std::
size_t I,
typename Record>
264 if constexpr (HasDescription<Record>)
266 return Description<Record>::FieldNames[I];
270 using FieldType = Reflection::MemberTypeOf<I, Record>;
272 if constexpr (!std::string_view(ColumnNameOverride<FieldType>).empty())
274 return FieldType::ColumnNameOverride;
276 return Reflection::MemberNameOf<I, Record>;
284template <std::
size_t I,
typename Record>
285constexpr inline std::string_view
FieldNameAt = detail::FieldNameAt<I, Record>();
290template <
typename Record>
291constexpr std::string_view
RecordTableName = detail::RecordTableNameImpl<Record>::Value;
293template <
template <
typename...>
class S,
class T>
294concept IsSpecializationOf = detail::is_specialization_of<S, T>::value;
296#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
301template <std::meta::info ReflectionOfField>
302constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<ReflectionOfField>::value;
304template <auto Member>
305using MemberClassType =
typename[:std::meta::parent_of(Member):];
307template <auto Member>
308constexpr size_t MemberIndexOf = []()
consteval ->
size_t {
310 auto members = nonstatic_data_members_of(std::meta::parent_of(Member), std::meta::access_context::current());
311 if (
auto it = std::ranges::find(members, Member); it != members.end())
313 index = std::distance(members.begin(), it);
314 return static_cast<size_t>(index);
323template <auto ReferencedField>
324constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<
decltype(ReferencedField), ReferencedField>::value;
326template <auto Member>
327constexpr size_t MemberIndexOf = Reflection::MemberIndexOf<Member>;
330using MemberClassType = detail::MemberClassTypeHelper<T>::type;
366template <auto ReferencedField>
368#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
371 .tableName =
RecordTableName<MemberClassType<
decltype(ReferencedField)>>,
373 .columnName = FieldNameOf<ReferencedField>,
378 template <auto ReferencedField>
379 struct FullyQualifiedQuotedNameOfImpl
381#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
382 static constexpr auto ClassName =
RecordTableName<
typename[:std::meta::parent_of(ReferencedField):]>;
384 static constexpr auto ClassName =
RecordTableName<MemberClassType<
decltype(ReferencedField)>>;
386 static constexpr auto FieldName = FieldNameOf<ReferencedField>;
387 static constexpr auto StorageSize = ClassName.size() + FieldName.size() + 6;
390 static constexpr auto Storage = []()
constexpr -> std::array<char, StorageSize> {
392 auto storage = std::array<char, StorageSize> {};
393 std::ranges::copy(
"\"", storage.begin());
394 std::ranges::copy(ClassName, storage.begin() + 1);
395 std::ranges::copy(
"\".\"", storage.begin() + 1 + ClassName.size());
396 std::ranges::copy(FieldName, storage.begin() + 1 + ClassName.size() + 3);
397 std::ranges::copy(
"\"", storage.begin() + 1 + ClassName.size() + 3 + FieldName.size());
398 storage.back() =
'\0';
402 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
405 template <auto ReferencedField>
406 constexpr inline auto FullyQualifiedQuotedNameOf = FullyQualifiedQuotedNameOfImpl<ReferencedField>::value;
408 template <
auto... ReferencedFields>
409 struct FullyQualifiedNamesOfImpl
411 static constexpr auto StorageSize =
412 1 + (2 * (
sizeof...(ReferencedFields) - 1)) + (0 + ... + FullyQualifiedQuotedNameOf<ReferencedFields>.size());
414 static constexpr std::array<char, StorageSize> Storage = []()
consteval {
415 auto result = std::array<char, StorageSize> {};
421 constexpr auto Delimiter = std::string_view(
", ");
422 std::ranges::copy(Delimiter, result.begin() + offset);
423 offset += Delimiter.size();
425 std::ranges::copy(FullyQualifiedQuotedNameOf<ReferencedFields>, result.begin() + offset);
426 offset += FullyQualifiedQuotedNameOf<ReferencedFields>.size();
429 result.back() =
'\0';
433 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
438 template <
auto... ReferencedFields>
439 constexpr inline auto FullyQualifiedNamesOf = FullyQualifiedNamesOfImpl<ReferencedFields...>::value;
450enum class SqlFailureAction : uint8_t
456 ThrowInvalidArgument,
476[[nodiscard]] LIGHTWEIGHT_API SqlFailureAction ClassifyOdbcResult(SQLRETURN result, SqlErrorInfo
const& errorInfo)
noexcept;
478LIGHTWEIGHT_API
void LogIfFailed(SQLHSTMT hStmt, SQLRETURN error, std::source_location sourceLocation);
519 [[nodiscard]]
virtual std::optional<SqlErrorInfo>
NextFailure(SQLHSTMT hStmt,
520 std::source_location
const& sourceLocation) = 0;
542 [[maybe_unused]] SQLHDBC hDbc, [[maybe_unused]] std::source_location
const& sourceLocation)
554LIGHTWEIGHT_API
void SetFaultSource(SqlFaultSource* source)
noexcept;
557[[nodiscard]] LIGHTWEIGHT_API SqlFaultSource* GetFaultSource() noexcept;
569LIGHTWEIGHT_API
void RequireSuccess(SQLHSTMT hStmt,
571 std::source_location sourceLocation = std::source_location::current());
586 struct OdbcCallOutcome
595 std::optional<SqlErrorInfo> injectedError {};
598 [[nodiscard]]
explicit constexpr operator bool() const noexcept
611 template <
typename ReadFromHandle>
612 requires std::is_invocable_r_v<SqlErrorInfo, ReadFromHandle const&>
613 [[nodiscard]] SqlErrorInfo EffectiveError(ReadFromHandle
const& readFromHandle)
const
615 return injectedError.has_value() ? *injectedError : readFromHandle();
640 [[nodiscard]] LIGHTWEIGHT_API OdbcCallOutcome
641 CheckOdbcCall(SQLRETURN result, SQLHSTMT hStmt, std::source_location sourceLocation = std::source_location::current());
657 [[nodiscard]] LIGHTWEIGHT_API OdbcCallOutcome CheckOdbcConnectionCall(
658 SQLRETURN result, SQLHDBC hDbc, std::source_location sourceLocation = std::source_location::current());
662enum class FormatType : uint8_t
675LIGHTWEIGHT_API std::string FormatName(std::string
const& name, FormatType formatType);
678LIGHTWEIGHT_API std::string FormatName(std::string_view name, FormatType formatType);
685 [[nodiscard]] LIGHTWEIGHT_API
bool IsColliding(std::string
const& name)
const noexcept;
688 [[nodiscard]] LIGHTWEIGHT_API std::optional<std::string>
TryDeclareName(std::string name);
691 [[nodiscard]] LIGHTWEIGHT_API std::string
DeclareName(std::string name);
694 std::unordered_map<std::string, size_t> _collisionMap;
Substitutes a scripted failure for an ODBC call that actually succeeded.
virtual std::optional< SqlErrorInfo > NextConnectionFailure(SQLHDBC hDbc, std::source_location const &sourceLocation)
Decides whether the next connection-handle check should fail.
virtual std::optional< SqlErrorInfo > NextFailure(SQLHSTMT hStmt, std::source_location const &sourceLocation)=0
Decides whether the next statement-handle check should fail.
Maintains collisions to create unique names.
LIGHTWEIGHT_API std::string DeclareName(std::string name)
Creates a name that is definitely not colliding.
LIGHTWEIGHT_API bool IsColliding(std::string const &name) const noexcept
Tests if the given name is already registered.
LIGHTWEIGHT_API std::optional< std::string > TryDeclareName(std::string name)
Tries to declare a name and returns it, otherwise returns std::nullopt.
constexpr std::string_view RecordTableName
Holds the SQL tabl ename for the given record type.
constexpr std::string_view FieldNameAt
Returns the SQL field name of the given field index in the record.
constexpr auto FullyQualifiedNameOf
Holds the fully qualified column reference (table + column) for the given field.
@ None
The field is not maintained by the data mapper.
SqlQualifiedTableColumnName represents a column name qualified with a table name.
std::string_view tableName
The table name.
constexpr std::weak_ordering operator<=>(SqlQualifiedTableColumnName const &) const noexcept=default
Three-way comparison operator.
std::string_view columnName
The column name.