Skip to content

Commit 511e98d

Browse files
committed
Set modified flag to true by default and make datamapper to set it to false during loading
1 parent 6447e50 commit 511e98d

File tree

4 files changed

+101
-39
lines changed

4 files changed

+101
-39
lines changed

src/Lightweight/DataMapper/DataMapper.hpp

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -350,9 +350,17 @@ class DataMapper
350350
template <typename Record>
351351
bool IsModified(Record const& record) const noexcept;
352352

353-
/// Clears the modified state of the record.
354-
template <typename Record>
355-
void ClearModifiedState(Record& record) noexcept;
353+
/// Enum to set the modified state of a record.
354+
enum class ModifiedState : uint8_t
355+
{
356+
Modified,
357+
NotModified
358+
};
359+
360+
/// Sets the modified state of the record after receiving from the database.
361+
/// This marks all fields as not modified.
362+
template <ModifiedState state, typename Record>
363+
void SetModifiedState(Record& record) noexcept;
356364

357365
/// Loads all direct relations to this record.
358366
template <typename Record>
@@ -1323,15 +1331,15 @@ RecordPrimaryKeyType<Record> DataMapper::Create(Record& record)
13231331
if constexpr (FieldType::IsPrimaryKey)
13241332
if constexpr (FieldType::IsAutoAssignPrimaryKey)
13251333
{
1326-
if (!record.[:el:].IsModified())
1334+
using ValueType = typename FieldType::ValueType;
1335+
if constexpr (std::same_as<ValueType, SqlGuid>)
13271336
{
1328-
using ValueType = typename FieldType::ValueType;
1329-
if constexpr (std::same_as<ValueType, SqlGuid>)
1330-
{
1331-
if (!record.[:el:].Value())
1332-
record.[:el:] = SqlGuid::Create();
1333-
}
1334-
else if constexpr (requires { ValueType {} + 1; })
1337+
if (!record.[:el:].Value())
1338+
record.[:el:] = SqlGuid::Create();
1339+
}
1340+
else if constexpr (requires { ValueType {} + 1; })
1341+
{
1342+
if (record.[:el:].Value() == ValueType {})
13351343
{
13361344
auto maxId = SqlStatement { _connection }.ExecuteDirectScalar<ValueType>(std::format(
13371345
R"sql(SELECT MAX("{}") FROM "{}")sql", FieldNameOf<el>, RecordTableName<Record>));
@@ -1344,15 +1352,15 @@ RecordPrimaryKeyType<Record> DataMapper::Create(Record& record)
13441352
CallOnPrimaryKey(record, [&]<size_t PrimaryKeyIndex, typename PrimaryKeyType>(PrimaryKeyType& primaryKeyField) {
13451353
if constexpr (PrimaryKeyType::IsAutoAssignPrimaryKey)
13461354
{
1347-
if (!primaryKeyField.IsModified())
1355+
using ValueType = PrimaryKeyType::ValueType;
1356+
if constexpr (std::same_as<ValueType, SqlGuid>)
13481357
{
1349-
using ValueType = PrimaryKeyType::ValueType;
1350-
if constexpr (std::same_as<ValueType, SqlGuid>)
1351-
{
1352-
if (!primaryKeyField.Value())
1353-
primaryKeyField = SqlGuid::Create();
1354-
}
1355-
else if constexpr (requires { ValueType {} + 1; })
1358+
if (!primaryKeyField.Value())
1359+
primaryKeyField = SqlGuid::Create();
1360+
}
1361+
else if constexpr (requires { ValueType {} + 1; })
1362+
{
1363+
if (primaryKeyField.Value() == ValueType {})
13561364
{
13571365
auto maxId = SqlStatement { _connection }.ExecuteDirectScalar<ValueType>(
13581366
std::format(R"sql(SELECT MAX("{}") FROM "{}")sql",
@@ -1370,7 +1378,7 @@ RecordPrimaryKeyType<Record> DataMapper::Create(Record& record)
13701378
if constexpr (HasAutoIncrementPrimaryKey<Record>)
13711379
SetId(record, _stmt.LastInsertId(RecordTableName<Record>));
13721380

1373-
ClearModifiedState(record);
1381+
SetModifiedState<ModifiedState::NotModified>(record);
13741382

13751383
if constexpr (QueryOptions.loadRelations)
13761384
ConfigureRelationAutoLoading(record);
@@ -1474,7 +1482,7 @@ void DataMapper::Update(Record& record)
14741482

14751483
_stmt.Execute();
14761484

1477-
ClearModifiedState(record);
1485+
SetModifiedState<ModifiedState::NotModified>(record);
14781486
}
14791487

14801488
template <typename Record>
@@ -1550,6 +1558,9 @@ std::optional<Record> DataMapper::QuerySingleWithoutRelationAutoLoading(PrimaryK
15501558
if (!detail::ReadSingleResult(_stmt.Connection().ServerType(), reader, *resultRecord))
15511559
return std::nullopt;
15521560

1561+
if (resultRecord)
1562+
SetModifiedState<ModifiedState::NotModified>(resultRecord.value());
1563+
15531564
return resultRecord;
15541565
}
15551566

@@ -1558,7 +1569,9 @@ std::optional<Record> DataMapper::QuerySingle(PrimaryKeyTypes&&... primaryKeys)
15581569
{
15591570
auto record = QuerySingleWithoutRelationAutoLoading<Record>(std::forward<PrimaryKeyTypes>(primaryKeys)...);
15601571
if (record)
1572+
{
15611573
ConfigureRelationAutoLoading(*record);
1574+
}
15621575
return record;
15631576
}
15641577

@@ -1578,6 +1591,10 @@ std::optional<Record> DataMapper::QuerySingle(SqlSelectQueryBuilder selectQuery,
15781591
auto reader = _stmt.GetResultCursor();
15791592
if (!detail::ReadSingleResult(_stmt.Connection().ServerType(), reader, *resultRecord))
15801593
return std::nullopt;
1594+
1595+
if (resultRecord)
1596+
SetModifiedState<ModifiedState::NotModified>(resultRecord.value());
1597+
15811598
return resultRecord;
15821599
}
15831600

@@ -1638,7 +1655,10 @@ std::vector<Record> DataMapper::Query(std::string_view sqlQueryString, InputPara
16381655
result.pop_back();
16391656

16401657
for (auto& record: result)
1658+
{
1659+
SetModifiedState<ModifiedState::NotModified>(record);
16411660
ConfigureRelationAutoLoading(record);
1661+
}
16421662
}
16431663

16441664
return result;
@@ -1710,16 +1730,16 @@ std::vector<std::tuple<First, Second, Rest...>> DataMapper::Query(SqlSelectQuery
17101730
// Drop the last record, which we failed to fetch (End of result set).
17111731
result.pop_back();
17121732

1713-
if constexpr (QueryOptions.loadRelations)
1733+
for (auto& record: result)
17141734
{
1715-
1716-
for (auto& record: result)
1717-
{
1718-
Reflection::template_for<0, std::tuple_size_v<value_type>>([&]<auto I>() {
1719-
auto& element = std::get<I>(record);
1735+
Reflection::template_for<0, std::tuple_size_v<value_type>>([&]<auto I>() {
1736+
auto& element = std::get<I>(record);
1737+
SetModifiedState<ModifiedState::NotModified>(element);
1738+
if constexpr (QueryOptions.loadRelations)
1739+
{
17201740
ConfigureRelationAutoLoading(element);
1721-
});
1722-
}
1741+
}
1742+
});
17231743
}
17241744

17251745
return result;
@@ -1759,21 +1779,27 @@ std::vector<Record> DataMapper::Query(SqlSelectQueryBuilder::ComposedQuery const
17591779
records.pop_back();
17601780

17611781
for (auto& record: records)
1782+
{
1783+
SetModifiedState<ModifiedState::NotModified>(record);
17621784
ConfigureRelationAutoLoading(record);
1785+
}
17631786

17641787
return records;
17651788
}
17661789

1767-
template <typename Record>
1768-
void DataMapper::ClearModifiedState(Record& record) noexcept
1790+
template <DataMapper::ModifiedState state, typename Record>
1791+
void DataMapper::SetModifiedState(Record& record) noexcept
17691792
{
17701793
static_assert(!std::is_const_v<Record>);
17711794
static_assert(DataMapperRecord<Record>, "Record must satisfy DataMapperRecord");
17721795

17731796
Reflection::EnumerateMembers(record, []<size_t I, typename FieldType>(FieldType& field) {
17741797
if constexpr (requires { field.SetModified(false); })
17751798
{
1776-
field.SetModified(false);
1799+
if constexpr (state == ModifiedState::Modified)
1800+
field.SetModified(true);
1801+
else
1802+
field.SetModified(false);
17771803
}
17781804
});
17791805
}

src/Lightweight/DataMapper/Field.hpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,20 @@ struct Field
126126
constexpr std::weak_ordering operator<=>(Field const& other) const noexcept;
127127

128128
/// Compares the field value with the given value for equality.
129-
bool operator==(Field const& value) const noexcept = default;
129+
constexpr bool operator==(Field const& value) const noexcept;
130130

131131
/// Compares the field value with the given value for inequality.
132-
bool operator!=(Field const& value) const noexcept = default;
132+
constexpr bool operator!=(Field const& value) const noexcept;
133133

134134
/// Compares the field value with the given value for equality.
135135
template <typename S>
136136
requires std::convertible_to<S, T>
137-
bool operator==(S const& value) const noexcept;
137+
constexpr bool operator==(S const& value) const noexcept;
138138

139139
/// Compares the field value with the given value for inequality.
140140
template <typename S>
141141
requires std::convertible_to<S, T>
142-
bool operator!=(S const& value) const noexcept;
142+
constexpr bool operator!=(S const& value) const noexcept;
143143

144144
/// Returns a string representation of the value, suitable for use in debugging and logging.
145145
[[nodiscard]] std::string InspectValue() const;
@@ -167,7 +167,7 @@ struct Field
167167

168168
private:
169169
ValueType _value {};
170-
bool _modified { false };
170+
bool _modified { true };
171171
};
172172

173173
// clang-format off
@@ -237,18 +237,30 @@ constexpr std::weak_ordering LIGHTWEIGHT_FORCE_INLINE Field<T, P1, P2>::operator
237237
return _value <=> other._value;
238238
}
239239

240+
template <detail::FieldElementType T, auto P1, auto P2>
241+
constexpr bool LIGHTWEIGHT_FORCE_INLINE Field<T, P1, P2>::operator==(Field const& other) const noexcept
242+
{
243+
return _value == other._value;
244+
}
245+
246+
template <detail::FieldElementType T, auto P1, auto P2>
247+
constexpr bool LIGHTWEIGHT_FORCE_INLINE Field<T, P1, P2>::operator!=(Field const& other) const noexcept
248+
{
249+
return _value != other._value;
250+
}
251+
240252
template <detail::FieldElementType T, auto P1, auto P2>
241253
template <typename S>
242254
requires std::convertible_to<S, T>
243-
inline LIGHTWEIGHT_FORCE_INLINE bool Field<T, P1, P2>::operator==(S const& value) const noexcept
255+
constexpr bool LIGHTWEIGHT_FORCE_INLINE Field<T, P1, P2>::operator==(S const& value) const noexcept
244256
{
245257
return _value == value;
246258
}
247259

248260
template <detail::FieldElementType T, auto P1, auto P2>
249261
template <typename S>
250262
requires std::convertible_to<S, T>
251-
inline LIGHTWEIGHT_FORCE_INLINE bool Field<T, P1, P2>::operator!=(S const& value) const noexcept
263+
constexpr bool LIGHTWEIGHT_FORCE_INLINE Field<T, P1, P2>::operator!=(S const& value) const noexcept
252264
{
253265
return _value != value;
254266
}

src/tests/DataMapper/CreateTests.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ TEST_CASE_METHOD(SqlTestFixture, "Table with multiple primary keys", "[DataMappe
136136
CHECK(queriedRecords.size() == 1);
137137
auto const& queriedRecord = queriedRecords.at(0);
138138
INFO("Queried record: " << DataMapper::Inspect(queriedRecord));
139+
INFO("record: " << DataMapper::Inspect(record));
139140
CHECK(queriedRecord == record);
140141
}
141142

@@ -159,4 +160,21 @@ TEST_CASE_METHOD(SqlTestFixture, "Loading of the dependent records after create"
159160
REQUIRE(!nullableFKUserNotSet.user.Record().has_value());
160161
}
161162

163+
TEST_CASE_METHOD(SqlTestFixture, "Create with defined primary key", "[DataMapper]")
164+
{
165+
auto dm = DataMapper();
166+
167+
dm.CreateTables<EntryWithIntPrimaryKey>();
168+
169+
auto entry = EntryWithIntPrimaryKey { .id = 42, .comment = "The Answer" };
170+
dm.Create(entry);
171+
172+
REQUIRE(entry.id.Value() == 42);
173+
174+
entry.comment = "Updated Comment";
175+
dm.Update(entry);
176+
REQUIRE(entry.id.Value() == 42);
177+
REQUIRE(dm.QuerySingle<EntryWithIntPrimaryKey>(42).value().comment.Value() == "Updated Comment");
178+
}
179+
162180
// NOLINTEND(bugprone-unchecked-optional-access)

src/tests/DataMapper/Entities.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,9 @@ struct Appointment
133133
return std::weak_ordering::equivalent;
134134
}
135135
};
136+
137+
struct EntryWithIntPrimaryKey
138+
{
139+
Light::Field<int, Light::PrimaryKey::AutoAssign> id;
140+
Light::Field<Light::SqlAnsiString<30>> comment;
141+
};

0 commit comments

Comments
 (0)