Skip to content

ddl2cpp emits PrimaryKey::AutoAssign on every member of a composite key, which GenerateAutoAssignPrimaryKey now rejects #609

Description

@christianparpart

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().

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions