|
| 1 | +# Proposal: Entity Positioning in ALTER ENTITY |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +Entity positions in the domain model canvas can only be set during `CREATE ENTITY` via the `@position` annotation. There's no way to reposition existing entities without dropping and recreating them — which destroys associations, access rules, and references. |
| 6 | + |
| 7 | +This matters for: |
| 8 | +- **Scripts that create multiple related entities** — auto-positioning stacks them linearly, making the domain model hard to read in Studio Pro |
| 9 | +- **Domain model cleanup** — reorganizing entity layout after schema evolution |
| 10 | +- **Auto-layout** — enabling tools to compute optimal positions and apply them in a single pass |
| 11 | + |
| 12 | +## Proposed Syntax |
| 13 | + |
| 14 | +### Single entity |
| 15 | + |
| 16 | +```sql |
| 17 | +ALTER ENTITY Module.Customer SET POSITION (100, 200); |
| 18 | +``` |
| 19 | + |
| 20 | +Consistent with existing `ALTER ENTITY ... SET DOCUMENTATION '...'` and `SET COMMENT '...'` patterns. |
| 21 | + |
| 22 | +### Multiple entities (batch repositioning) |
| 23 | + |
| 24 | +```sql |
| 25 | +ALTER ENTITY Module.Customer SET POSITION (100, 100); |
| 26 | +ALTER ENTITY Module.Order SET POSITION (400, 100); |
| 27 | +ALTER ENTITY Module.Product SET POSITION (400, 300); |
| 28 | +``` |
| 29 | + |
| 30 | +### Auto-layout command (future extension) |
| 31 | + |
| 32 | +A separate top-level command for automatic layout of all entities in a module: |
| 33 | + |
| 34 | +```sql |
| 35 | +ARRANGE DOMAIN MODEL IN Module; |
| 36 | +``` |
| 37 | + |
| 38 | +This is out of scope for the initial implementation but the grammar should not conflict with it. |
| 39 | + |
| 40 | +## Grammar Change |
| 41 | + |
| 42 | +Add one alternative to the `alterEntityAction` rule: |
| 43 | + |
| 44 | +```ebnf |
| 45 | +alterEntityAction |
| 46 | + : ...existing alternatives... |
| 47 | + | SET POSITION LPAREN NUMBER_LITERAL COMMA NUMBER_LITERAL RPAREN |
| 48 | + ; |
| 49 | +``` |
| 50 | + |
| 51 | +`POSITION` is already a keyword in the lexer (used by `@position` annotations and notebook actions). |
| 52 | + |
| 53 | +## AST Change |
| 54 | + |
| 55 | +Add a new operation constant and position field to `AlterEntityStmt`: |
| 56 | + |
| 57 | +```go |
| 58 | +const ( |
| 59 | + ... |
| 60 | + AlterEntitySetPosition // SET POSITION (x, y) |
| 61 | +) |
| 62 | + |
| 63 | +type AlterEntityStmt struct { |
| 64 | + ... |
| 65 | + Position *Position // For SET POSITION |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +## Executor Change |
| 70 | + |
| 71 | +In `cmd_entities.go`, handle `AlterEntitySetPosition`: |
| 72 | + |
| 73 | +1. Find the entity in the domain model |
| 74 | +2. Update `entity.Location = model.Point{X: s.Position.X, Y: s.Position.Y}` |
| 75 | +3. Write the updated entity back via `writer.UpdateEntityLocation(entityID, location)` |
| 76 | + |
| 77 | +This is a lightweight operation — it only updates the `Location` field in the BSON, no structural changes. |
| 78 | + |
| 79 | +## DESCRIBE Output |
| 80 | + |
| 81 | +`DESCRIBE ENTITY` should include a `@position` annotation when the entity has a non-default position, so the output is round-trippable: |
| 82 | + |
| 83 | +```sql |
| 84 | +@position(100, 200) |
| 85 | +CREATE OR REPLACE PERSISTENT ENTITY Module.Customer ( |
| 86 | + Name: String(100), |
| 87 | + ... |
| 88 | +); |
| 89 | +``` |
| 90 | + |
| 91 | +Currently DESCRIBE omits the `@position` annotation. This should be added regardless of whether `ALTER ENTITY SET POSITION` is implemented, since it improves roundtrip fidelity. |
| 92 | + |
| 93 | +## Implementation Scope |
| 94 | + |
| 95 | +| Component | Change | |
| 96 | +|-----------|--------| |
| 97 | +| `MDLParser.g4` | Add `SET POSITION (x, y)` to `alterEntityAction` | |
| 98 | +| `MDLLexer.g4` | No change (`POSITION` already exists) | |
| 99 | +| `mdl/ast/ast_entity.go` | Add `AlterEntitySetPosition` op, `Position` field | |
| 100 | +| `mdl/visitor/visitor_entity.go` | Parse the new alternative | |
| 101 | +| `mdl/executor/cmd_entities.go` | Handle `AlterEntitySetPosition` | |
| 102 | +| `sdk/mpr/writer.go` | Add `UpdateEntityLocation()` (if not already present) | |
| 103 | +| DESCRIBE output | Add `@position` annotation | |
| 104 | + |
| 105 | +## Alternatives Considered |
| 106 | + |
| 107 | +**`@position` annotation on ALTER ENTITY** — e.g., `@position(100,200) ALTER ENTITY ...`. Rejected because annotations are a CREATE-time concept; SET is the established ALTER pattern. |
| 108 | + |
| 109 | +**Dedicated MOVE POSITION command** — e.g., `MOVE ENTITY Module.E TO POSITION (x, y)`. Rejected because MOVE already means "move to different module" in MDL. |
| 110 | + |
| 111 | +**ARRANGE command only** — Skip per-entity positioning, just auto-layout. Insufficient for scripts that need precise control (e.g., aligning entities in a specific pattern). |
0 commit comments