11#include "Description.hpp"
12#include "SqlError.hpp"
13#include "SqlOdbcPrelude.hpp"
15#include <reflection-cpp/reflection.hpp>
24#include <source_location>
27#include <system_error>
29#include <unordered_map>
34#if !(defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L)
36 #if defined(__APPLE__)
45#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
46 #include <experimental/meta>
55 template <
typename T,
typename... Comps>
56 concept OneOf = (std::same_as<T, Comps> || ...);
59 constexpr auto AlwaysFalse = std::false_type::value;
61 constexpr auto Finally(
auto&& cleanupRoutine)
noexcept
66 std::remove_cvref_t<
decltype(cleanupRoutine)> cleanup;
72 return Finally { std::forward<decltype(cleanupRoutine)>(cleanupRoutine) };
87 requires std::is_floating_point_v<T>
88 [[nodiscard]]
inline std::optional<T> ParseFloat(
char const* first,
char const* last)
noexcept
92#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L
96 auto const [ptr, ec] = std::from_chars(first, last, value);
97 if (ec != std::errc {} || ptr != last)
105 static ::locale_t
const cLocale = ::newlocale(LC_NUMERIC_MASK,
"C",
static_cast<::locale_t
>(
nullptr));
106 auto const length =
static_cast<std::size_t
>(last - first);
107 std::array<char, 64> stackBuffer {};
108 std::string heapBuffer;
109 char const* text =
nullptr;
110 if (length < stackBuffer.size())
112 std::ranges::copy(first, last, stackBuffer.begin());
113 stackBuffer[length] =
'\0';
114 text = stackBuffer.data();
118 heapBuffer.assign(first, last);
119 text = heapBuffer.c_str();
122 char* parseEnd =
nullptr;
125 if constexpr (std::is_same_v<T, float>)
126 value = ::strtof_l(text, &parseEnd, cLocale);
127 else if constexpr (std::is_same_v<T, long double>)
128 value = ::strtold_l(text, &parseEnd, cLocale);
130 value =
static_cast<T
>(::strtod_l(text, &parseEnd, cLocale));
132 if (errno == ERANGE || parseEnd != text + length)
141 template <
template <
typename...>
class T,
typename U>
142 struct is_specialization_of: std::false_type
146 template <
template <
typename...>
class T,
typename... Us>
147 struct is_specialization_of<T, T<Us...>>: std::true_type
151 template <
typename T>
152 struct MemberClassTypeHelper;
154 template <
typename M,
typename T>
155 struct MemberClassTypeHelper<M T::*>
157 using type = std::remove_cvref_t<T>;
160 template <
typename Record>
161 struct RecordTableNameImpl
163 static constexpr std::string_view Value = []() {
164 if constexpr (
requires { Record::TableName; })
165 return Record::TableName;
168#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
169 return std::meta::identifier_of(^^Record);
171 auto const typeName = Reflection::TypeNameOf<Record>;
172 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
173 return typeName.substr(i + 1);
183 template <
typename First,
typename Second>
184 struct RecordTableNameImpl<std::tuple<First, Second>>
186 static constexpr std::string_view Value = []() {
187 if constexpr (
requires { First::TableName; })
188 return First::TableName;
191#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
192 return std::meta::identifier_of(^^First);
194 auto const typeName = Reflection::TypeNameOf<First>;
195 if (
auto const i = typeName.rfind(
':'); i != std::string_view::npos)
196 return typeName.substr(i + 1);
203 template <
typename FieldType>
204 constexpr auto ColumnNameOverride = []()
consteval {
205 if constexpr (
requires { FieldType::ColumnNameOverride; })
206 return FieldType::ColumnNameOverride;
208 return std::string_view {};
210#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
211 template <auto reflection>
212 struct FieldNameOfImpl
214 using R =
typename[:std::meta::type_of(reflection):];
215 static constexpr std::string_view value = []()
constexpr -> std::string_view {
216 if constexpr (
requires { R::ColumnNameOverride; })
218 if constexpr (!R::ColumnNameOverride.empty())
219 return R::ColumnNameOverride;
221 return std::meta::identifier_of(reflection);
225 template <
typename ReferencedFieldType, auto F>
226 struct FieldNameOfImpl;
228 template <
typename T, auto F,
typename R>
229 struct FieldNameOfImpl<R T::*, F>
231 static constexpr std::string_view value = []()
constexpr -> std::string_view {
232 if constexpr (
requires { R::ColumnNameOverride; })
234 if constexpr (!R::ColumnNameOverride.empty())
235 return R::ColumnNameOverride;
237 return Reflection::NameOf<F>;
242 template <std::
size_t I,
typename Record>
247 if constexpr (HasDescription<Record>)
249 return Description<Record>::FieldNames[I];
253 using FieldType = Reflection::MemberTypeOf<I, Record>;
255 if constexpr (!std::string_view(ColumnNameOverride<FieldType>).empty())
257 return FieldType::ColumnNameOverride;
259 return Reflection::MemberNameOf<I, Record>;
267template <std::
size_t I,
typename Record>
268constexpr inline std::string_view
FieldNameAt = detail::FieldNameAt<I, Record>();
273template <
typename Record>
274constexpr std::string_view
RecordTableName = detail::RecordTableNameImpl<Record>::Value;
276template <
template <
typename...>
class S,
class T>
277concept IsSpecializationOf = detail::is_specialization_of<S, T>::value;
279#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
284template <std::meta::info ReflectionOfField>
285constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<ReflectionOfField>::value;
287template <auto Member>
288using MemberClassType =
typename[:std::meta::parent_of(Member):];
290template <auto Member>
291constexpr size_t MemberIndexOf = []()
consteval ->
size_t {
293 auto members = nonstatic_data_members_of(std::meta::parent_of(Member), std::meta::access_context::current());
294 if (
auto it = std::ranges::find(members, Member); it != members.end())
296 index = std::distance(members.begin(), it);
297 return static_cast<size_t>(index);
306template <auto ReferencedField>
307constexpr inline std::string_view FieldNameOf = detail::FieldNameOfImpl<
decltype(ReferencedField), ReferencedField>::value;
309template <auto Member>
310constexpr size_t MemberIndexOf = Reflection::MemberIndexOf<Member>;
313using MemberClassType = detail::MemberClassTypeHelper<T>::type;
349template <auto ReferencedField>
351#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
354 .tableName =
RecordTableName<MemberClassType<
decltype(ReferencedField)>>,
356 .columnName = FieldNameOf<ReferencedField>,
361 template <auto ReferencedField>
362 struct FullyQualifiedQuotedNameOfImpl
364#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
365 static constexpr auto ClassName =
RecordTableName<
typename[:std::meta::parent_of(ReferencedField):]>;
367 static constexpr auto ClassName =
RecordTableName<MemberClassType<
decltype(ReferencedField)>>;
369 static constexpr auto FieldName = FieldNameOf<ReferencedField>;
370 static constexpr auto StorageSize = ClassName.size() + FieldName.size() + 6;
373 static constexpr auto Storage = []()
constexpr -> std::array<char, StorageSize> {
375 auto storage = std::array<char, StorageSize> {};
376 std::ranges::copy(
"\"", storage.begin());
377 std::ranges::copy(ClassName, storage.begin() + 1);
378 std::ranges::copy(
"\".\"", storage.begin() + 1 + ClassName.size());
379 std::ranges::copy(FieldName, storage.begin() + 1 + ClassName.size() + 3);
380 std::ranges::copy(
"\"", storage.begin() + 1 + ClassName.size() + 3 + FieldName.size());
381 storage.back() =
'\0';
385 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
388 template <auto ReferencedField>
389 constexpr inline auto FullyQualifiedQuotedNameOf = FullyQualifiedQuotedNameOfImpl<ReferencedField>::value;
391 template <
auto... ReferencedFields>
392 struct FullyQualifiedNamesOfImpl
394 static constexpr auto StorageSize =
395 1 + (2 * (
sizeof...(ReferencedFields) - 1)) + (0 + ... + FullyQualifiedQuotedNameOf<ReferencedFields>.size());
397 static constexpr std::array<char, StorageSize> Storage = []()
consteval {
398 auto result = std::array<char, StorageSize> {};
404 constexpr auto Delimiter = std::string_view(
", ");
405 std::ranges::copy(Delimiter, result.begin() + offset);
406 offset += Delimiter.size();
408 std::ranges::copy(FullyQualifiedQuotedNameOf<ReferencedFields>, result.begin() + offset);
409 offset += FullyQualifiedQuotedNameOf<ReferencedFields>.size();
412 result.back() =
'\0';
416 static constexpr auto value = std::string_view(Storage.data(), Storage.size() - 1);
421 template <
auto... ReferencedFields>
422 constexpr inline auto FullyQualifiedNamesOf = FullyQualifiedNamesOfImpl<ReferencedFields...>::value;
433enum class SqlFailureAction : uint8_t
439 ThrowInvalidArgument,
459[[nodiscard]] LIGHTWEIGHT_API SqlFailureAction ClassifyOdbcResult(SQLRETURN result, SqlErrorInfo
const& errorInfo)
noexcept;
461LIGHTWEIGHT_API
void LogIfFailed(SQLHSTMT hStmt, SQLRETURN error, std::source_location sourceLocation);
502 [[nodiscard]]
virtual std::optional<SqlErrorInfo>
NextFailure(SQLHSTMT hStmt,
503 std::source_location
const& sourceLocation) = 0;
525 [[maybe_unused]] SQLHDBC hDbc, [[maybe_unused]] std::source_location
const& sourceLocation)
537LIGHTWEIGHT_API
void SetFaultSource(SqlFaultSource* source)
noexcept;
540[[nodiscard]] LIGHTWEIGHT_API SqlFaultSource* GetFaultSource() noexcept;
552LIGHTWEIGHT_API
void RequireSuccess(SQLHSTMT hStmt,
554 std::source_location sourceLocation = std::source_location::current());
569 struct OdbcCallOutcome
578 std::optional<SqlErrorInfo> injectedError {};
581 [[nodiscard]]
explicit constexpr operator bool() const noexcept
594 template <
typename ReadFromHandle>
595 requires std::is_invocable_r_v<SqlErrorInfo, ReadFromHandle const&>
596 [[nodiscard]] SqlErrorInfo EffectiveError(ReadFromHandle
const& readFromHandle)
const
598 return injectedError.has_value() ? *injectedError : readFromHandle();
623 [[nodiscard]] LIGHTWEIGHT_API OdbcCallOutcome
624 CheckOdbcCall(SQLRETURN result, SQLHSTMT hStmt, std::source_location sourceLocation = std::source_location::current());
640 [[nodiscard]] LIGHTWEIGHT_API OdbcCallOutcome CheckOdbcConnectionCall(
641 SQLRETURN result, SQLHDBC hDbc, std::source_location sourceLocation = std::source_location::current());
645enum class FormatType : uint8_t
658LIGHTWEIGHT_API std::string FormatName(std::string
const& name, FormatType formatType);
661LIGHTWEIGHT_API std::string FormatName(std::string_view name, FormatType formatType);
668 [[nodiscard]] LIGHTWEIGHT_API
bool IsColliding(std::string
const& name)
const noexcept;
671 [[nodiscard]] LIGHTWEIGHT_API std::optional<std::string>
TryDeclareName(std::string name);
674 [[nodiscard]] LIGHTWEIGHT_API std::string
DeclareName(std::string name);
677 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
Not giving up — set when the action is SqlRetryAction::Retry.
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.