5#include "../DataBinder/Core.hpp"
6#include "../DataBinder/SqlNullValue.hpp"
7#include "../SqlColumnTypeDefinitions.hpp"
29auto inline Unwrap = [](
auto v) {
61template <auto TheReferencedField, auto ColumnNameOverr
ideString = std::nullopt, SqlNullable Nullable = SqlNullable::NotNull>
70 if constexpr (!std::same_as<
decltype(ColumnNameOverrideString), std::nullopt_t>)
71 return std::string_view { ColumnNameOverrideString };
73 return std::string_view {};
76#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
84 "The referenced field must be a primary key.");
90 "The referenced field must be a primary key.");
98 using ValueType = std::conditional_t<Nullable == SqlNullable::Null, std::optional<BaseType>,
BaseType>;
101 static constexpr auto IsOptional = Nullable == SqlNullable::Null;
110 template <
typename... S>
111 requires std::constructible_from<
ValueType, S...>
113 _referencedFieldValue(std::forward<S>(value)...)
119#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
125 _record { std::make_unique<ReferencedRecord>(other) }
131 _referencedFieldValue(other._referencedFieldValue),
132 _loader(std::move(other._loader)),
133 _loaded(other._loaded),
134 _modified(other._modified),
135 _record(other._record ? std::make_unique<ReferencedRecord>(*other._record) : nullptr),
136 _loadError(other._loadError)
142 _referencedFieldValue(std::move(other._referencedFieldValue)),
143 _loader(std::move(other._loader)),
144 _loaded(other._loaded),
145 _modified(other._modified),
146 _record(std::move(other._record)),
147 _loadError(other._loadError)
154 if (!_referencedFieldValue)
159 _referencedFieldValue = {};
175 template <
typename S>
176 requires(std::constructible_from<ValueType, S> && !std::same_as<std::remove_cvref_t<S>,
BelongsTo>
178 && !std::same_as<std::remove_cvref_t<S>,
SqlNullType>)
181 _referencedFieldValue =
ValueType { std::forward<S>(value) };
192#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
199 _record = std::make_unique<ReferencedRecord>(other);
201#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
216 _referencedFieldValue = other._referencedFieldValue;
217 _loader = std::move(other._loader);
218 _loaded = other._loaded;
219 _modified = other._modified;
220 _record = other._record ? std::make_unique<ReferencedRecord>(*other._record) :
nullptr;
221 _loadError = other._loadError;
231 _referencedFieldValue = std::move(other._referencedFieldValue);
232 _loader = std::move(other._loader);
233 _loaded = other._loaded;
234 _modified = other._modified;
235 _record = std::move(other._record);
236 _loadError = other._loadError;
237 other._loaded =
false;
244 LIGHTWEIGHT_FORCE_INLINE constexpr
void SetModified(
bool value) noexcept
250 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr bool IsModified() const noexcept
256 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr ValueType const&
Value() const noexcept
258 return _referencedFieldValue;
264 return _referencedFieldValue;
278 template <
typename Self>
279 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr auto Record(
this Self&& self) ->
RelationResult<std::reference_wrapper<
284 return self.Load().transform([&self] {
return std::reference_wrapper<Target> { *self._record }; });
290 template <
typename Self>
295 return *self._record;
301 template <
typename Self>
306 return self._record.get();
312 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr bool operator!() const noexcept
314 return !_referencedFieldValue;
318 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr explicit operator bool() const noexcept
320 return static_cast<bool>(_referencedFieldValue);
332 return _record.get();
338 return _record.get();
346 _record = std::make_unique<ReferencedRecord>();
365 if (!record.has_value())
368 _record = std::make_unique<ReferencedRecord>(std::move(record).value());
374 template <
typename Stmt>
377 stmt.BindOutputColumn(outputIndex, &_referencedFieldValue);
383 return _referencedFieldValue <=> other.Value();
387 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
390 return _referencedFieldValue <=> other.Value();
396 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
402 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
406 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
409 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
413 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
416 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
429 _loader = std::move(loader);
442 return std::unexpected { *_loadError };
444 if (!_referencedFieldValue)
453 _loadError = loaded.error();
454 return std::unexpected { loaded.error() };
456 _record = std::make_unique<ReferencedRecord>(std::move(*loaded));
461 void LoadOrThrow()
const
463 if (
auto const loaded = Load(); !loaded)
464 throw SqlRequireLoadedError(Reflection::TypeNameOf<std::remove_cvref_t<
decltype(*
this)>>, loaded.error());
469 mutable bool _loaded =
false;
470 bool _modified =
false;
471 mutable std::unique_ptr<ReferencedRecord> _record {};
472 mutable std::optional<RelationError> _loadError {};
475template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
476std::ostream& operator<<(std::ostream& os, BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>
const& belongsTo)
478 return os << belongsTo.Value();
483 template <
typename T>
484 struct IsBelongsToType: std::false_type
488 template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
489 struct IsBelongsToType<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>: std::true_type
496constexpr bool IsBelongsTo = detail::IsBelongsToType<std::remove_cvref_t<T>>::value;
499concept is_belongs_to = IsBelongsTo<T>;
502constexpr bool IsOptionalBelongsTo =
false;
504template <is_be
longs_to T>
505constexpr bool IsOptionalBelongsTo<T> = T::IsOptional;
507template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
508struct SqlDataBinder<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>
510 using SelfType = BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>;
511 using InnerType = SelfType::ValueType;
513 static constexpr auto ColumnType = SqlDataBinder<InnerType>::ColumnType;
515 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
517 SelfType
const& value,
518 SqlDataBinderCallback& cb)
520 return SqlDataBinder<InnerType>::InputParameter(stmt, column, value.Value(), cb);
523 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN
524 OutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
526 auto const sqlReturn = SqlDataBinder<InnerType>::OutputColumn(stmt, column, &result->MutableValue(), indicator, cb);
527 cb.PlanPostProcessOutputColumn([result]() { result->SetModified(
true); });
533 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN
534 GetColumn(SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback
const& cb)
536 auto const sqlReturn = SqlDataBinder<InnerType>::GetColumn(stmt, column, &result->MutableValue(), indicator, cb);
537 if (SQL_SUCCEEDED(sqlReturn))
538 result->SetModified(
true);
Represents the many-to-one side of a foreign-key relationship.
BelongsTo & operator=(BelongsTo &&other) noexcept
Move assignment operator.
LIGHTWEIGHT_FORCE_INLINE void AdoptFetchedRecord(std::optional< ReferencedRecord > record)
static constexpr auto IsAutoIncrementPrimaryKey
Indicates that a BelongsTo field is never an auto-increment primary key.
bool operator==(BelongsTo const &other) const noexcept
Equality comparison operator.
std::weak_ordering operator<=>(Field< T, IsPrimaryKeyValue > const &other) const noexcept
Three-way comparison operator with a Field.
bool operator==(Field< T, IsPrimaryKeyValue > const &other) const noexcept
Equality comparison operator with a Field.
std::weak_ordering operator<=>(BelongsTo const &other) const noexcept
Three-way comparison operator.
bool operator!=(BelongsTo const &other) const noexcept
Inequality comparison operator.
BelongsTo & operator=(SqlNullType) noexcept
Assigns NULL to the relationship, clearing the loaded record.
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.
constexpr BelongsTo(S &&... value) noexcept
Constructs a new BelongsTo with the given value(s) forwarded to the underlying value type.
static constexpr auto ReferencedField
The field in the other record that references the current record.
static constexpr auto IsMandatory
Indicates whether this relationship is mandatory (non-nullable).
LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!() const noexcept
Checks if the field value is NULL.
static constexpr auto IsPrimaryKey
Indicates that a BelongsTo field is never a primary key.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord * LoadedRecord() noexcept
Returns the already-loaded referenced record, or nullptr when none is loaded.
BelongsTo & operator=(BelongsTo const &other)
Copy assignment operator.
LIGHTWEIGHT_FORCE_INLINE void BindOutputColumn(SQLSMALLINT outputIndex, Stmt &stmt)
Binds the foreign key value to the given output column index on the statement.
LIGHTWEIGHT_FORCE_INLINE constexpr bool IsModified() const noexcept
Checks if the field is modified.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord * operator->(this Self &&self)
Retrieves the referenced record. Only available when the relationship is mandatory.
constexpr BelongsTo(BelongsTo &&other) noexcept
Move constructor.
LIGHTWEIGHT_FORCE_INLINE constexpr ValueType & MutableValue() noexcept
Retrieves the mutable reference to the value of the field.
BelongsTo & operator=(ReferencedRecord &other)
Assigns a referenced record, updating the foreign key and loaded state.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord const * LoadedRecord() const noexcept
Returns the already-loaded referenced record, or nullptr when none is loaded.
LIGHTWEIGHT_FORCE_INLINE constexpr ValueType const & Value() const noexcept
Retrieves the reference to the value of the field.
constexpr BelongsTo(BelongsTo const &other) noexcept
Copy constructor.
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
void SetAutoLoader(Loader loader) noexcept
Used internally to configure on-demand loading of the record.
LIGHTWEIGHT_FORCE_INLINE constexpr ReferencedRecord & operator*(this Self &&self)
Retrieves the referenced record. Only available when the relationship is mandatory.
constexpr BelongsTo(ReferencedRecord const &other) noexcept
Constructs a new BelongsTo from the given referenced record, copying its primary key.
LIGHTWEIGHT_FORCE_INLINE constexpr auto Record(this Self &&self) -> RelationResult< std::reference_wrapper< std::conditional_t< std::is_const_v< std::remove_reference_t< Self > >, ReferencedRecord const, ReferencedRecord > > >
Retrieves the referenced record, loading it on first access.
MemberClassType< decltype(TheReferencedField)> ReferencedRecord
Represents the record type of the other field.
static constexpr auto IsOptional
Indicates whether this relationship is optional (nullable).
static constexpr std::string_view ColumnNameOverride
If not an empty string, this value will be used as the column name in the database.
bool operator!=(Field< T, IsPrimaryKeyValue > const &other) const noexcept
Inequality comparison operator with a Field.
std::expected< T, RelationError > RelationResult
The outcome of a relation access: the requested value, or why it is unavailable.
@ NotFound
There is nothing to load: the foreign key is NULL, or the referenced row does not exist.
Carries the deferred load, installed by the DataMapper.
std::function< RelationResult< ReferencedRecord >()> loadReference
Loads the referenced record: RelationError::NotFound when there is none.
Represents a single column in a table.