diff --git a/docs/11-proposals/PROPOSAL_authorable_message_definitions.md b/docs/11-proposals/PROPOSAL_authorable_message_definitions.md index d950809ab..af63e0b86 100644 --- a/docs/11-proposals/PROPOSAL_authorable_message_definitions.md +++ b/docs/11-proposals/PROPOSAL_authorable_message_definitions.md @@ -60,8 +60,28 @@ MessageDefinitions$MessageDefinitionCollection 36 Name, Documentation, Exc MessageDefinitions$ExposedAssociation 933 (same, plus Association + Entity) ``` -Collections are small: 28 of 36 hold a single definition, 4 hold two, 3 hold -four, 1 holds eight. +Collections are small in one dimension and not at all in the other. 28 of 36 +hold a single definition, 4 hold two, 3 hold four, 1 holds eight — but the +definitions themselves are **deep**: + +| nesting depth | elements | | +|---|---|---| +| 0 (the definition root) | 56 | | +| 1 | 356 | | +| 2 | 265 | | +| 3 | 142 | | +| 4 | 208 | | +| 5 | 477 | | +| 6 | 974 | | +| **7** | **2,208** | the maximum observed | + +Object elements carry a mean of 4.7 members, and the tail is long — 93 have 15 +members, one has 22. + +That shape decides more of this proposal than anything else. A definition is not +a flat field list you would happily restate; a whole-document `CREATE OR MODIFY` +is a poor tool for "expose one more attribute", and any addressing scheme for +targeted edits has to reach seven levels down. ### Almost every property is a constant or is derived @@ -152,6 +172,8 @@ Following [ADR-0003](../13-decisions/0003-mdl-is-sql-shaped.md) and ### Statement set +**Whole document:** + | statement | notes | |---|---| | `CREATE [OR MODIFY] MESSAGE DEFINITION COLLECTION M.Name ( ... )` | `OR MODIFY` preserves the UUID — mappings reference the collection by qualified name, and a fresh document would break every `WITH MESSAGE DEFINITION` | @@ -161,6 +183,69 @@ Following [ADR-0003](../13-decisions/0003-mdl-is-sql-shaped.md) and `FOLDER 'path'` on create, as every other document type takes. +**Targeted edits.** `CREATE OR MODIFY` alone is not enough, for the reason the +depth table shows: adding one attribute to a definition seven levels deep would +mean restating the whole document, which is precisely the diff-unfriendliness +[ADR-0003](../13-decisions/0003-mdl-is-sql-shaped.md) argues against, and is why +`ALTER ENTITY ADD ATTRIBUTE` exists rather than only `CREATE OR MODIFY ENTITY`. + +Definitions within a collection — this is the 8 collections that hold more than +one: + +```sql +alter message definition collection Sales.MD_Order + add definition Line for Sales.Line as 'Lines' ( Sku, Quantity ); + +alter message definition collection Sales.MD_Order drop definition Line; +alter message definition collection Sales.MD_Order rename definition Line to OrderLine; +``` + +Members within a definition — this applies to all 36: + +```sql +alter message definition Sales.MD_Order.Order add member Total; +alter message definition Sales.MD_Order.Order + add member Sales.Order_Line/Sales.Line as 'Lines' ( Sku, Quantity ); + +alter message definition Sales.MD_Order.Order drop member Total; +alter message definition Sales.MD_Order.Order set member Total as 'GrandTotal'; +``` + +Three deliberate choices here: + +- **The definition is addressed as `Module.Collection.Definition`** — the same + three-part reference `WITH MESSAGE DEFINITION` already takes. Nothing new to + learn, and the two cannot drift apart. +- **`SET member … AS`, not `RENAME member … TO`.** `ALTER ENTITY RENAME + ATTRIBUTE` renames the attribute *in the model* and rewrites every reference to + it. This changes only the element's `ExposedName` and touches nothing else, so + borrowing `RENAME` would promise something far larger than it does. `SET` is + the established verb for changing a property, and `as` for a name-to-name + mapping. +- **`IF NOT EXISTS` / `IF EXISTS`** on add and drop, so a definition script + re-runs cleanly — the same treatment `ALTER ENTITY` gives attributes. + +**Reaching a nested member.** Members live up to seven levels down, so the +address needs a path. It is written in **exposed names**, the names the document +itself carries: + +```sql +alter message definition Sales.MD_Order.Order + add member Price in Lines/Prices; + +alter message definition Sales.MD_Order.Order + drop member Sku in Lines; +``` + +`in ` rather than a `/`-joined member name, because `/` already means +"association to entity" inside a member (`Sales.Order_Line/Sales.Line`) and +overloading it in the same clause would be ambiguous to a reader even where the +grammar could tell them apart. + +New members append. `describe` emits stored order, so the round trip is stable +either way; a positional form (`before` / `after`) is deliberately not proposed +until something needs it. + ## The name of a repeating element, and why not to guess it Studio Pro **pluralises** `ExposedName` for a repeating element while keeping @@ -196,10 +281,11 @@ makes the cardinality visible at the point the author writes it. ## Scope -**In:** the four statements above; attributes, associations in both directions, -nested definitions to arbitrary depth; `OR MODIFY`; folder placement; the -`describe` round trip; a check-time rule for members that do not exist on the -entity (the shape `MDL-JSON01` established). +**In:** the statements above — whole-document and targeted; attributes, +associations in both directions, nesting to the depth the corpus shows; +`OR MODIFY`; folder placement; the `describe` round trip; a check-time rule for +members that do not exist on the entity, and for an `in` path that reaches +nothing (the shape `MDL-JSON01` / `MDL-JSON02` established). **Out:** @@ -210,15 +296,32 @@ entity (the shape `MDL-JSON01` established). 4,686 of 4,686. Add them when a document needs them, not before. - **Published message definitions** (the Business Events surface). A different document. +- **A wildcard member** (`definition Order for Sales.Order ( * )` to expose every + attribute). Studio Pro's UI is a tree of checkboxes, so "tick everything" is a + natural thing to want, and with a mean of 4.7 members and a tail out to 22 it + would save real typing. But `*` appears nowhere else in MDL, and no measurement + here says anyone wants it — proposing it now would be designing for a shape + with no document behind it. Raised as an open question instead. + +## Open questions for review + +1. **Require `as` on a repeating element?** See above — it removes the silent + divergence from Studio Pro at the cost of friction. +2. **A wildcard member.** Worth it, or speculative? +3. **Inherited attributes** — refuse, or block the proposal until measured? +4. **Legacy engine** — author there too, or modelsdk-only like rules, menus and + layouts? ## Implementation outline Full-stack, per the CLAUDE.md checklist: 1. **Grammar** — `createMessageDefinitionCollectionStatement`, plus `drop` / - `describe` / `show`. New keyword: none required; `MESSAGE` and `DEFINITION` - already exist (`WITH MESSAGE DEFINITION`), and `COLLECTION` is used by image - and icon collections. + `describe` / `show`, and `alterMessageDefinitionCollectionStatement` / + `alterMessageDefinitionStatement`. New keyword: none required; `MESSAGE` and + `DEFINITION` already exist (`WITH MESSAGE DEFINITION`), `COLLECTION` is used + by image and icon collections, and `IN` / `AS` / `SET` / `ADD` / `DROP` / + `RENAME` are all in the lexer. 2. **AST** — `CreateMessageDefinitionCollectionStmt`, with a member tree that distinguishes attribute from association by the presence of a qualified name. 3. **Visitor** — bridge, as usual. @@ -226,8 +329,11 @@ Full-stack, per the CLAUDE.md checklist: `UpdateMessageDefinitionCollection` / `DeleteMessageDefinitionCollection` on `MappingBackend`, beside the existing `ListMessageDefinitionCollections`; implemented in both engines and stubbed in the mock. -5. **Executor** — thin handler; the element tree is built from the domain model, - which is where `MaxOccurs` and `PrimitiveType` are resolved. +5. **Executor** — thin handlers. The element tree is built from the domain model, + which is where `MaxOccurs` and `PrimitiveType` are resolved. The ALTER path + edits the stored document rather than rebuilding it, so an untouched + definition is never round-tripped through the describer — the argument that + made `ALTER LAYOUT` a capability rather than a convenience. 6. **`describe`** — re-executable, deterministic order. The document is written through the codec on the modelsdk engine. Whether the @@ -252,6 +358,9 @@ uniform. 4. **The constants.** One test asserting `MinOccurs`/`Nillable`/`MaxLength`/… against the measured values, so a future change that starts varying one of them fails loudly rather than drifting. +5. **ALTER leaves the rest alone.** Add one member to a pinned collection and + assert every *other* element is byte-identical — the control that separates a + targeted edit from a rebuild that happened to produce the same thing. ## A note on measuring this document type