Skip to content

Commit b28922c

Browse files
authored
Merge pull request #449 from LASTRADA-Software/improvement/hasmany_tests_and_fixes
[DataMapper] HasMany fixes and tests
2 parents 1967881 + cb2eafe commit b28922c

File tree

9 files changed

+426
-102
lines changed

9 files changed

+426
-102
lines changed

src/Lightweight/DataMapper/DataMapper.hpp

Lines changed: 197 additions & 62 deletions
Large diffs are not rendered by default.

src/Lightweight/DataMapper/HasMany.hpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class HasMany
117117
struct Loader
118118
{
119119
std::function<size_t()> count {};
120-
std::function<void()> all {};
120+
std::function<ReferencedRecordList()> all {};
121121
std::function<void(std::function<void(ReferencedRecord const&)>)> each {};
122122

123123
std::weak_ordering operator<=>(Loader const& /*other*/) const noexcept
@@ -150,7 +150,7 @@ template <typename OtherRecord>
150150
inline LIGHTWEIGHT_FORCE_INLINE void HasMany<OtherRecord>::RequireLoaded()
151151
{
152152
if (!_records)
153-
_loader.all();
153+
_records = _loader.all();
154154
}
155155

156156
template <typename OtherRecord>
@@ -239,28 +239,40 @@ template <typename OtherRecord>
239239
inline LIGHTWEIGHT_FORCE_INLINE HasMany<OtherRecord>::iterator HasMany<OtherRecord>::begin() noexcept
240240
{
241241
RequireLoaded();
242-
return _records->begin();
242+
if (_records)
243+
return _records->begin();
244+
else
245+
return iterator {};
243246
}
244247

245248
template <typename OtherRecord>
246249
inline LIGHTWEIGHT_FORCE_INLINE HasMany<OtherRecord>::iterator HasMany<OtherRecord>::end() noexcept
247250
{
248251
RequireLoaded();
249-
return _records->end();
252+
if (_records)
253+
return _records->end();
254+
else
255+
return iterator {};
250256
}
251257

252258
template <typename OtherRecord>
253259
inline LIGHTWEIGHT_FORCE_INLINE HasMany<OtherRecord>::const_iterator HasMany<OtherRecord>::begin() const noexcept
254260
{
255261
RequireLoaded();
256-
return _records->begin();
262+
if (_records)
263+
return _records->begin();
264+
else
265+
return const_iterator {};
257266
}
258267

259268
template <typename OtherRecord>
260269
inline LIGHTWEIGHT_FORCE_INLINE HasMany<OtherRecord>::const_iterator HasMany<OtherRecord>::end() const noexcept
261270
{
262271
RequireLoaded();
263-
return _records->end();
272+
if (_records)
273+
return _records->end();
274+
else
275+
return const_iterator {};
264276
}
265277

266278
} // namespace Lightweight

src/Lightweight/DataMapper/HasManyThrough.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class HasManyThrough
9797
struct Loader
9898
{
9999
std::function<size_t()> count;
100-
std::function<void()> all;
100+
std::function<ReferencedRecordList()> all;
101101
std::function<void(std::function<void(ReferencedRecord const&)>)> each;
102102
};
103103

@@ -140,7 +140,7 @@ class HasManyThrough
140140
return;
141141

142142
if (_loader.all)
143-
_loader.all();
143+
_records = _loader.all();
144144

145145
if (!_records)
146146
throw SqlRequireLoadedError(Reflection::TypeNameOf<std::remove_cvref_t<decltype(*this)>>);

src/Lightweight/DataMapper/HasOneThrough.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class HasOneThrough
6868

6969
struct Loader
7070
{
71-
std::function<void()> loadReference {};
71+
std::function<std::shared_ptr<ReferencedRecord>()> loadReference {};
7272
};
7373

7474
/// Used internally to configure on-demand loading of the record.
@@ -84,7 +84,7 @@ class HasOneThrough
8484
return;
8585

8686
if (_loader.loadReference)
87-
_loader.loadReference();
87+
_record = _loader.loadReference();
8888

8989
if (!IsLoaded())
9090
throw SqlRequireLoadedError { Reflection::TypeNameOf<std::remove_cvref_t<decltype(*this)>> };
@@ -93,7 +93,7 @@ class HasOneThrough
9393
Loader _loader {};
9494

9595
// We use shared_ptr to not require ReferencedRecord to be declared before HasOneThrough.
96-
std::shared_ptr<ReferencedRecord> _record {};
96+
mutable std::shared_ptr<ReferencedRecord> _record {};
9797
};
9898

9999
namespace detail

src/Lightweight/DataMapper/Record.hpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ namespace details
8888
template <typename Record>
8989
struct RecordPrimaryKeyTypeHelper
9090
{
91-
using type = void;
91+
using type = std::monostate;
9292
};
9393

9494
template <typename Record>
@@ -182,10 +182,12 @@ inline LIGHTWEIGHT_FORCE_INLINE RecordPrimaryKeyType<Record> GetPrimaryKeyField(
182182

183183
auto result = RecordPrimaryKeyType<Record> {};
184184
Reflection::EnumerateMembers(record, [&]<size_t I, typename FieldType>(FieldType const& field) {
185-
if constexpr (IsPrimaryKey<FieldType> && std::same_as<FieldType, RecordPrimaryKeyType<Record>>)
186-
{
187-
result = field;
188-
}
185+
// std::same_as<typename FieldType::ValueType, RecordPrimaryKeyType<Record>>condition is for the case where there are
186+
// multiple primary keys, we want to return the first one
187+
if constexpr (IsField<FieldType>)
188+
if constexpr (IsPrimaryKey<FieldType>)
189+
if constexpr (std::same_as<typename FieldType::ValueType, RecordPrimaryKeyType<Record>>)
190+
result = field.Value();
189191
});
190192
return result;
191193
}

src/tests/DataMapper/MiscTests.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,18 @@ static_assert(RecordPrimaryKeyIndex<PKTestMulti> == 2,
163163
"Primary key index should be the first primary key field in the record");
164164
static_assert(std::same_as<RecordPrimaryKeyType<PKTestMulti>, SqlTrimmedFixedString<10>>);
165165

166+
struct NoPKRecord
167+
{
168+
Field<int> fieldA;
169+
Field<char> fieldB;
170+
};
171+
172+
static_assert(HasPrimaryKey<PKTest0>);
173+
static_assert(HasPrimaryKey<PKTest1>);
174+
static_assert(HasPrimaryKey<PKTestMulti>);
175+
static_assert(HasPrimaryKey<Person>);
176+
static_assert(!HasPrimaryKey<NoPKRecord>);
177+
166178
TEST_CASE_METHOD(SqlTestFixture, "Primary key access", "[DataMapper]")
167179
{
168180
PKTest0 pk0;
@@ -178,6 +190,39 @@ TEST_CASE_METHOD(SqlTestFixture, "Primary key access", "[DataMapper]")
178190
CHECK(pkMulti.pk1.Value() == "World");
179191
}
180192

193+
TEST_CASE("GetPrimaryKeyField", "[DataMapper][Record]")
194+
{
195+
SECTION("int PK at field index 0")
196+
{
197+
PKTest0 record;
198+
record.pk = 42;
199+
CHECK(GetPrimaryKeyField(record) == 42);
200+
}
201+
202+
SECTION("string PK at field index 1")
203+
{
204+
PKTest1 record;
205+
record.pk = "Hello";
206+
CHECK(GetPrimaryKeyField(record) == SqlTrimmedFixedString<10> { "Hello" });
207+
}
208+
209+
SECTION("first PK returned when multiple PKs present")
210+
{
211+
PKTestMulti record;
212+
record.pk1 = "World";
213+
record.pk2 = 3.14;
214+
CHECK(GetPrimaryKeyField(record) == SqlTrimmedFixedString<10> { "World" });
215+
}
216+
217+
SECTION("SqlGuid PK")
218+
{
219+
auto const guid = SqlGuid::Create();
220+
Person record;
221+
record.id = guid;
222+
CHECK(GetPrimaryKeyField(record) == guid);
223+
}
224+
}
225+
181226
TEST_CASE_METHOD(SqlTestFixture, "MapFromRecordFields", "[DataMapper]")
182227
{
183228
auto const person = Person {

src/tests/DataMapper/ReadTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ struct SimpleStruct2
461461
};
462462

463463
static_assert(std::cmp_equal(RecordPrimaryKeyIndex<SimpleStruct2>, static_cast<size_t>(-1)));
464-
static_assert(std::same_as<RecordPrimaryKeyType<SimpleStruct2>, void>);
464+
static_assert(std::same_as<RecordPrimaryKeyType<SimpleStruct2>, std::monostate>);
465465

466466
std::ostream& operator<<(std::ostream& os, SimpleStruct2 const& record)
467467
{

0 commit comments

Comments
 (0)