5#include "../DataBinder/Core.hpp"
6#include "../DataBinder/SqlNullValue.hpp"
7#include "../SqlStatement.hpp"
22auto inline Unwrap = [](
auto v) {
50template <auto TheReferencedField, auto ColumnNameOverr
ideString = std::nullopt, SqlNullable Nullable = SqlNullable::NotNull>
59 if constexpr (!std::same_as<
decltype(ColumnNameOverrideString), std::nullopt_t>)
60 return std::string_view { ColumnNameOverrideString };
62 return std::string_view {};
65#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
72 static_assert(std::remove_cvref_t<decltype(std::declval<ReferencedRecord>().[:
ReferencedField:])>::IsPrimaryKey,
73 "The referenced field must be a primary key.");
78 static_assert(std::remove_cvref_t<decltype(std::declval<ReferencedRecord>().*
ReferencedField)>::IsPrimaryKey,
79 "The referenced field must be a primary key.");
87 using ValueType = std::conditional_t<Nullable == SqlNullable::Null, std::optional<BaseType>,
BaseType>;
89 static constexpr auto IsOptional = Nullable == SqlNullable::Null;
90 static constexpr auto IsMandatory = !IsOptional;
91 static constexpr auto IsPrimaryKey =
false;
92 static constexpr auto IsAutoIncrementPrimaryKey =
false;
94 template <
typename... S>
95 requires std::constructible_from<
ValueType, S...>
96 constexpr BelongsTo(S&&... value)
noexcept:
97 _referencedFieldValue(std::forward<S>(value)...)
102#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
108 _record { std::make_unique<ReferencedRecord>(other) }
112 constexpr BelongsTo(BelongsTo
const& other)
noexcept:
113 _referencedFieldValue(other._referencedFieldValue),
114 _loader(std::move(other._loader)),
115 _loaded(other._loaded),
116 _modified(other._modified),
117 _record(other._record ? std::make_unique<ReferencedRecord>(*other._record) : nullptr)
121 constexpr BelongsTo(BelongsTo&& other)
noexcept:
122 _referencedFieldValue(std::move(other._referencedFieldValue)),
123 _loader(std::move(other._loader)),
124 _loaded(other._loaded),
125 _modified(other._modified),
126 _record(std::move(other._record))
130 BelongsTo& operator=(SqlNullType )
noexcept
132 if (!_referencedFieldValue)
135 _record = std::nullopt;
136 _referencedFieldValue = {};
143#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
150 _record = std::make_unique<ReferencedRecord>(other);
151#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
160 BelongsTo& operator=(BelongsTo
const& other)
165 _referencedFieldValue = other._referencedFieldValue;
166 _loader = std::move(other._loader);
167 _loaded = other._loaded;
168 _modified = other._modified;
169 _record = other._record ? std::make_unique<ReferencedRecord>(*other._record) : nullptr;
174 BelongsTo& operator=(BelongsTo&& other)
noexcept
178 _referencedFieldValue = std::move(other._referencedFieldValue);
179 _loader = std::move(other._loader);
180 _loaded = other._loaded;
181 _modified = other._modified;
182 _record = std::move(other._record);
183 other._loaded =
false;
187 ~BelongsTo() noexcept = default;
190 LIGHTWEIGHT_FORCE_INLINE constexpr
void SetModified(
bool value) noexcept
196 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr bool IsModified() const noexcept
202 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr ValueType const&
Value() const noexcept
204 return _referencedFieldValue;
210 return _referencedFieldValue;
214 template <
typename Self>
216 requires(IsMandatory)
218 self.RequireLoaded();
219 return *self._record;
224 template <
typename Self>
225 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr decltype(
auto)
Record(
this Self&& self)
228 self.RequireLoaded();
229 return [&]() -> std::optional<std::reference_wrapper<ReferencedRecord>> {
231 return *self._record;
240 template <
typename Self>
242 requires(IsMandatory)
244 self.RequireLoaded();
245 return *self._record;
250 template <
typename Self>
252 requires(IsMandatory)
254 self.RequireLoaded();
255 return self._record.get();
259 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr bool operator!() const noexcept
261 return !_referencedFieldValue;
265 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr explicit operator bool() const noexcept
267 return static_cast<bool>(_referencedFieldValue);
274 _record = std::make_unique<ReferencedRecord>();
278 LIGHTWEIGHT_FORCE_INLINE
void BindOutputColumn(SQLSMALLINT outputIndex,
SqlStatement& stmt)
280 stmt.BindOutputColumn(outputIndex, &_referencedFieldValue);
283 std::weak_ordering operator<=>(BelongsTo
const& other)
const noexcept
285 return _referencedFieldValue <=> other.Value();
288 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
289 std::weak_ordering operator<=>(Field<T, IsPrimaryKeyValue>
const& other)
const noexcept
291 return _referencedFieldValue <=> other.Value();
294 bool operator==(BelongsTo
const& other)
const noexcept
296 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
299 bool operator!=(BelongsTo
const& other)
const noexcept
301 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
304 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
305 bool operator==(Field<T, IsPrimaryKeyValue>
const& other)
const noexcept
307 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
310 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
311 bool operator!=(Field<T, IsPrimaryKeyValue>
const& other)
const noexcept
313 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
318 std::function<std::optional<ReferencedRecord>()> loadReference {};
324 _loader = std::move(loader);
328 void RequireLoaded()
const
333 if (_loader.loadReference)
335 auto value = _loader.loadReference();
338 _record = std::make_unique<ReferencedRecord>(std::move(value.value()));
343 if constexpr (IsMandatory)
345 throw SqlRequireLoadedError(Reflection::TypeNameOf<std::remove_cvref_t<
decltype(*
this)>>);
350 mutable bool _loaded =
false;
351 bool _modified =
false;
352 mutable std::unique_ptr<ReferencedRecord> _record {};
355template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
356std::ostream& operator<<(std::ostream& os, BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>
const& belongsTo)
358 return os << belongsTo.Value();
363 template <
typename T>
364 struct IsBelongsToType: std::false_type
368 template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
369 struct IsBelongsToType<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>: std::true_type
376constexpr bool IsBelongsTo = detail::IsBelongsToType<std::remove_cvref_t<T>>::value;
379concept is_belongs_to = IsBelongsTo<T>;
382constexpr bool IsOptionalBelongsTo =
false;
384template <is_be
longs_to T>
385constexpr bool IsOptionalBelongsTo<T> = T::IsOptional;
387template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
388struct SqlDataBinder<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>
390 using SelfType = BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>;
391 using InnerType = SelfType::ValueType;
393 static constexpr auto ColumnType = SqlDataBinder<InnerType>::ColumnType;
395 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
397 SelfType
const& value,
398 SqlDataBinderCallback& cb)
400 return SqlDataBinder<InnerType>::InputParameter(stmt, column, value.Value(), cb);
403 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN
404 OutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
406 auto const sqlReturn = SqlDataBinder<InnerType>::OutputColumn(stmt, column, &result->MutableValue(), indicator, cb);
407 cb.PlanPostProcessOutputColumn([result]() { result->SetModified(
true); });
411 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN GetColumn(
412 SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback
const& cb)
noexcept
414 auto const sqlReturn = SqlDataBinder<InnerType>::GetColumn(stmt, column, &result->MutableValue(), indicator, cb);
415 if (SQL_SUCCEEDED(sqlReturn))
416 result->SetModified(
true);
Represents a one-to-one relationship.
std::remove_cvref_t< decltype(std::declval< ReferencedRecord >().*ReferencedField)>::ValueType BaseType
Represents the base column type of the foreign key, matching the primary key of the other record.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord & EmplaceRecord()
Emplaces a record into the relationship. This will mark the relationship as loaded.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord & operator*(this Self &&self) noexcept
static constexpr auto ReferencedField
The field in the other record that references the current record.
LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!() const noexcept
Checks if the field value is NULL.
LIGHTWEIGHT_FORCE_INLINE constexpr bool IsModified() const noexcept
Checks if the field is modified.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord * operator->(this Self &&self)
LIGHTWEIGHT_FORCE_INLINE constexpr ValueType & MutableValue() noexcept
Retrieves the mutable reference to the value of the field.
LIGHTWEIGHT_FORCE_INLINE constexpr ValueType const & Value() const noexcept
Retrieves the reference to the value of the field.
LIGHTWEIGHT_FORCE_INLINE constexpr void SetModified(bool value) noexcept
Marks the field as modified or unmodified.
std::conditional_t< Nullable==SqlNullable::Null, std::optional< BaseType >, BaseType > ValueType
LIGHTWEIGHT_FORCE_INLINE constexpr decltype(auto) Record(this Self &&self)
void SetAutoLoader(Loader loader) noexcept
Used internally to configure on-demand loading of the record.
MemberClassType< decltype(TheReferencedField)> ReferencedRecord
Represents the record type of the other field.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord const & Record(this Self &&self)
Retrieves a record from the relationship. When the record is not optional.
static constexpr std::string_view ColumnNameOverride
If not an empty string, this value will be used as the column name in the database.
High level API for (prepared) raw SQL statements.