5#include "../DataBinder/Core.hpp"
6#include "../DataBinder/SqlNullValue.hpp"
7#include "../SqlColumnTypeDefinitions.hpp"
27auto inline Unwrap = [](
auto v) {
59template <auto TheReferencedField, auto ColumnNameOverr
ideString = std::nullopt, SqlNullable Nullable = SqlNullable::NotNull>
68 if constexpr (!std::same_as<
decltype(ColumnNameOverrideString), std::nullopt_t>)
69 return std::string_view { ColumnNameOverrideString };
71 return std::string_view {};
74#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
82 "The referenced field must be a primary key.");
88 "The referenced field must be a primary key.");
96 using ValueType = std::conditional_t<Nullable == SqlNullable::Null, std::optional<BaseType>,
BaseType>;
99 static constexpr auto IsOptional = Nullable == SqlNullable::Null;
108 template <
typename... S>
109 requires std::constructible_from<
ValueType, S...>
111 _referencedFieldValue(std::forward<S>(value)...)
117#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
123 _record { std::make_unique<ReferencedRecord>(other) }
129 _referencedFieldValue(other._referencedFieldValue),
130 _loader(std::move(other._loader)),
131 _loaded(other._loaded),
132 _modified(other._modified),
133 _record(other._record ? std::make_unique<ReferencedRecord>(*other._record) : nullptr)
139 _referencedFieldValue(std::move(other._referencedFieldValue)),
140 _loader(std::move(other._loader)),
141 _loaded(other._loaded),
142 _modified(other._modified),
143 _record(std::move(other._record))
150 if (!_referencedFieldValue)
154 _referencedFieldValue = {};
170 template <
typename S>
171 requires(std::constructible_from<ValueType, S> && !std::same_as<std::remove_cvref_t<S>,
BelongsTo>
173 && !std::same_as<std::remove_cvref_t<S>,
SqlNullType>)
176 _referencedFieldValue =
ValueType { std::forward<S>(value) };
186#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
193 _record = std::make_unique<ReferencedRecord>(other);
194#if defined(LIGHTWEIGHT_CXX26_REFLECTION)
209 _referencedFieldValue = other._referencedFieldValue;
210 _loader = std::move(other._loader);
211 _loaded = other._loaded;
212 _modified = other._modified;
213 _record = other._record ? std::make_unique<ReferencedRecord>(*other._record) :
nullptr;
223 _referencedFieldValue = std::move(other._referencedFieldValue);
224 _loader = std::move(other._loader);
225 _loaded = other._loaded;
226 _modified = other._modified;
227 _record = std::move(other._record);
228 other._loaded =
false;
235 LIGHTWEIGHT_FORCE_INLINE constexpr
void SetModified(
bool value) noexcept
241 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr bool IsModified() const noexcept
247 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr ValueType const&
Value() const noexcept
249 return _referencedFieldValue;
255 return _referencedFieldValue;
261 template <
typename Self>
265 self.RequireLoaded();
266 return *self._record;
271 template <
typename Self>
272 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr decltype(
auto)
Record(
this Self&& self)
275 self.RequireLoaded();
276 return [&]() -> std::optional<std::reference_wrapper<ReferencedRecord>> {
278 return *self._record;
287 template <
typename Self>
291 self.RequireLoaded();
292 return *self._record;
297 template <
typename Self>
301 self.RequireLoaded();
302 return self._record.get();
308 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr bool operator!() const noexcept
310 return !_referencedFieldValue;
314 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
constexpr explicit operator bool() const noexcept
316 return static_cast<bool>(_referencedFieldValue);
328 return _record.get();
334 return _record.get();
341 _record = std::make_unique<ReferencedRecord>();
360 if (!record.has_value())
363 _record = std::make_unique<ReferencedRecord>(std::move(record).value());
368 template <
typename Stmt>
371 stmt.BindOutputColumn(outputIndex, &_referencedFieldValue);
377 return _referencedFieldValue <=> other.Value();
381 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
384 return _referencedFieldValue <=> other.Value();
390 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
396 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
400 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
403 return (_referencedFieldValue <=> other.Value()) == std::weak_ordering::equivalent;
407 template <detail::FieldElementType T, PrimaryKey IsPrimaryKeyValue = PrimaryKey::No>
410 return (_referencedFieldValue <=> other.Value()) != std::weak_ordering::equivalent;
415 std::function<std::optional<ReferencedRecord>()> loadReference {};
421 _loader = std::move(loader);
425 void RequireLoaded()
const
430 if (_loader.loadReference)
432 auto value = _loader.loadReference();
435 _record = std::make_unique<ReferencedRecord>(std::move(value.value()));
442 throw SqlRequireLoadedError(Reflection::TypeNameOf<std::remove_cvref_t<
decltype(*
this)>>);
447 mutable bool _loaded =
false;
448 bool _modified =
false;
449 mutable std::unique_ptr<ReferencedRecord> _record {};
452template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
453std::ostream& operator<<(std::ostream& os, BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>
const& belongsTo)
455 return os << belongsTo.Value();
460 template <
typename T>
461 struct IsBelongsToType: std::false_type
465 template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
466 struct IsBelongsToType<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>: std::true_type
473constexpr bool IsBelongsTo = detail::IsBelongsToType<std::remove_cvref_t<T>>::value;
476concept is_belongs_to = IsBelongsTo<T>;
479constexpr bool IsOptionalBelongsTo =
false;
481template <is_be
longs_to T>
482constexpr bool IsOptionalBelongsTo<T> = T::IsOptional;
484template <auto ReferencedField, auto ColumnNameOverr
ideString, SqlNullable Nullable>
485struct SqlDataBinder<BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>>
487 using SelfType = BelongsTo<ReferencedField, ColumnNameOverrideString, Nullable>;
488 using InnerType = SelfType::ValueType;
490 static constexpr auto ColumnType = SqlDataBinder<InnerType>::ColumnType;
492 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN InputParameter(SQLHSTMT stmt,
494 SelfType
const& value,
495 SqlDataBinderCallback& cb)
497 return SqlDataBinder<InnerType>::InputParameter(stmt, column, value.Value(), cb);
500 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN
501 OutputColumn(SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback& cb)
503 auto const sqlReturn = SqlDataBinder<InnerType>::OutputColumn(stmt, column, &result->MutableValue(), indicator, cb);
504 cb.PlanPostProcessOutputColumn([result]() { result->SetModified(
true); });
510 static LIGHTWEIGHT_FORCE_INLINE SQLRETURN
511 GetColumn(SQLHSTMT stmt, SQLUSMALLINT column, SelfType* result, SQLLEN* indicator, SqlDataBinderCallback
const& cb)
513 auto const sqlReturn = SqlDataBinder<InnerType>::GetColumn(stmt, column, &result->MutableValue(), indicator, cb);
514 if (SQL_SUCCEEDED(sqlReturn))
515 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.
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.
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)
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
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.
constexpr BelongsTo(ReferencedRecord const &other) noexcept
Constructs a new BelongsTo from the given referenced record, copying its primary key.
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 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.
Represents a single column in a table.