Problem
With PrimaryKeyAssignment: ClientSide, CxxModelPrinter appends , Light::PrimaryKey::AutoAssign to every primary-key column (src/Lightweight/Tools/CxxModelPrinter.cpp:952-959, applied at :1083). For a table with a composite primary key — a junction table, typically — that emits several auto-assigned key members:
struct RlMxnFormelliste final
{
static constexpr std::string_view TableName = "RL_MXN_FORMELLISTE";
Light::Field<int32_t, Light::PrimaryKey::AutoAssign, Light::SqlRealName { "FORMEL_NR" }> formelNr;
Light::Field<int32_t, Light::PrimaryKey::AutoAssign, Light::SqlRealName { "LISTE_NR" }> listeNr;
Light::Field<std::optional<int32_t>, Light::SqlRealName { "LFD_NR" }> lfdNr;
};
DataMapper::Create() on such a record is now a hard compile error, from the static_assert added in GenerateAutoAssignPrimaryKey (DataMapper.hpp:1878). So ddl2cpp generates code that the DataMapper rejects.
The assert is right — docs/composite-keys-design.md states the rule, and SetId() does write the single generated value into every IsPrimaryKey member. The generator simply has not caught up; composite-keys-design.md lists ddl2cpp generation under "Deferred".
Impact
A downstream model of 688 generated entities has 165 with more than one AutoAssign key member. Only those actually passed to Create<>() instantiate the assert, so this surfaces as a scattered, hard-to-attribute build break on upgrade rather than all at once.
Before the assert existed this was silently dangerous rather than harmless: GenerateAutoAssignPrimaryKey only generates when a key field still holds its default, so a caller supplying both parts is fine — but a caller leaving either at 0 gets SELECT MAX(thatColumn)+1 written into every key member, overwriting the part it did supply.
What the fix needs to decide
Suppressing AutoAssign for composite keys is the easy half. The harder half is what to emit instead, because none of the three enumerators expresses "composite key, values supplied by the caller":
PrimaryKey::No — loses key-ness; QuerySingle, Update and Delete by key stop working.
PrimaryKey::AutoAssign — rejected by the new assert.
PrimaryKey::ServerSideAutoIncrement — what composite-keys-design.md shows for CkParent, but Create() then runs if constexpr (HasAutoIncrementPrimaryKey<Record>) SetId(record, pk);, and SetId writes into every key member. That reintroduces the same clobbering after the insert rather than before it.
So this may need a fourth enumerator (a plain "part of the key, never generated") before ddl2cpp has something correct to emit. Happy to take that on if the shape is agreed.
Reproducing
Any table with a composite primary key, generated with PrimaryKeyAssignment: ClientSide, then passed to DataMapper::Create().
Problem
With
PrimaryKeyAssignment: ClientSide,CxxModelPrinterappends, Light::PrimaryKey::AutoAssignto every primary-key column (src/Lightweight/Tools/CxxModelPrinter.cpp:952-959, applied at:1083). For a table with a composite primary key — a junction table, typically — that emits several auto-assigned key members:DataMapper::Create()on such a record is now a hard compile error, from thestatic_assertadded inGenerateAutoAssignPrimaryKey(DataMapper.hpp:1878). So ddl2cpp generates code that the DataMapper rejects.The assert is right —
docs/composite-keys-design.mdstates the rule, andSetId()does write the single generated value into everyIsPrimaryKeymember. The generator simply has not caught up;composite-keys-design.mdlistsddl2cppgeneration under "Deferred".Impact
A downstream model of 688 generated entities has 165 with more than one
AutoAssignkey member. Only those actually passed toCreate<>()instantiate the assert, so this surfaces as a scattered, hard-to-attribute build break on upgrade rather than all at once.Before the assert existed this was silently dangerous rather than harmless:
GenerateAutoAssignPrimaryKeyonly generates when a key field still holds its default, so a caller supplying both parts is fine — but a caller leaving either at0getsSELECT MAX(thatColumn)+1written into every key member, overwriting the part it did supply.What the fix needs to decide
Suppressing
AutoAssignfor composite keys is the easy half. The harder half is what to emit instead, because none of the three enumerators expresses "composite key, values supplied by the caller":PrimaryKey::No— loses key-ness;QuerySingle,UpdateandDeleteby key stop working.PrimaryKey::AutoAssign— rejected by the new assert.PrimaryKey::ServerSideAutoIncrement— whatcomposite-keys-design.mdshows forCkParent, butCreate()then runsif constexpr (HasAutoIncrementPrimaryKey<Record>) SetId(record, pk);, andSetIdwrites into every key member. That reintroduces the same clobbering after the insert rather than before it.So this may need a fourth enumerator (a plain "part of the key, never generated") before ddl2cpp has something correct to emit. Happy to take that on if the shape is agreed.
Reproducing
Any table with a composite primary key, generated with
PrimaryKeyAssignment: ClientSide, then passed toDataMapper::Create().