Skip to content

Commit 7827200

Browse files
akoclaude
andcommitted
feat: system attributes as pseudo-types (AutoOwner, AutoChangedBy, etc.)
Replace STORE OWNER/CHANGED BY/CREATED DATE/CHANGED DATE syntax on CREATE ENTITY with pseudo-typed attributes in the attribute list: Owner: AutoOwner, ChangedBy: AutoChangedBy, CreatedDate: AutoCreatedDate, ChangedDate: AutoChangedDate These follow the AutoNumber pattern — the type name communicates "system-generated, read-only". DESCRIBE now outputs them as regular attributes. ALTER ENTITY SET/DROP STORE remains for toggling on existing entities. Also fixes Mendix 11.9.0 BSON field rename (HasOwner → HasOwnerAttr) and version-aware writer output. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 36caacb commit 7827200

24 files changed

+11613
-11452
lines changed

.claude/skills/mendix/generate-domain-model.md

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,14 @@ CREATE PERSISTENT ENTITY Module.Photo (
198198

199199
#### System Attributes (Auditing)
200200

201-
Mendix supports four built-in auditing properties on persistent entities. These are stored at the entity level (not as regular attributes) and the system fills them automatically:
201+
Mendix supports four built-in auditing properties on persistent entities. Declare them as regular attributes using pseudo-types (like `AutoNumber`):
202202

203-
| Clause | Effect |
204-
|--------|--------|
205-
| `STORE OWNER` | Adds `System.owner` association to System.User; set on insert |
206-
| `STORE CHANGED BY` | Adds `System.changedBy` association to System.User; updated on every commit |
207-
| `STORE CREATED DATE` | Adds `CreatedDate` (DateTime); set on insert |
208-
| `STORE CHANGED DATE` | Adds `ChangedDate` (DateTime); updated on every commit |
209-
210-
**Position**: STORE clauses go AFTER the closing parenthesis of attributes, before the semicolon. They can appear in any order.
203+
| Pseudo-Type | System Attribute | Set When |
204+
|-------------|-----------------|----------|
205+
| `AutoOwner` | `System.owner` (→ System.User) | Object created |
206+
| `AutoChangedBy` | `System.changedBy` (→ System.User) | Every commit |
207+
| `AutoCreatedDate` | `CreatedDate` (DateTime) | Object created |
208+
| `AutoChangedDate` | `ChangedDate` (DateTime) | Every commit |
211209

212210
```sql
213211
/**
@@ -216,12 +214,12 @@ Mendix supports four built-in auditing properties on persistent entities. These
216214
CREATE PERSISTENT ENTITY Sales.Order (
217215
OrderNumber: AutoNumber,
218216
TotalAmount: Decimal NOT NULL,
219-
Status: Enumeration(Sales.OrderStatus) NOT NULL
220-
)
221-
STORE OWNER
222-
STORE CHANGED BY
223-
STORE CREATED DATE
224-
STORE CHANGED DATE;
217+
Status: Enumeration(Sales.OrderStatus) NOT NULL,
218+
Owner: AutoOwner,
219+
ChangedBy: AutoChangedBy,
220+
CreatedDate: AutoCreatedDate,
221+
ChangedDate: AutoChangedDate
222+
);
225223
```
226224

227225
To enable/disable on existing entities, use ALTER ENTITY:
@@ -234,8 +232,8 @@ ALTER ENTITY Sales.Order DROP STORE CHANGED BY;
234232

235233
**When to use auditing:**
236234
- Compliance/regulated domains (finance, healthcare) — use all four
237-
- User-generated content — use STORE OWNER for ownership rules
238-
- "Recently modified" lists — use STORE CHANGED DATE
235+
- User-generated content — use AutoOwner for ownership-based access rules
236+
- "Recently modified" lists — use AutoChangedDate
239237
- Avoid on high-volume system tables (every write touches the audit columns)
240238

241239
#### Non-Persistent Entity

cmd/mxcli/help.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ var attributeTypes = map[string]string{
133133
"DateTime": "Date and time combined (DateAndTime also accepted)",
134134
"Date": "Date only (no time component)",
135135
"AutoNumber": "Auto-incrementing integer",
136+
"AutoOwner": "System.owner association (auto-set on create)",
137+
"AutoChangedBy": "System.changedBy association (auto-set on commit)",
138+
"AutoCreatedDate": "CreatedDate: DateTime (auto-set on create)",
139+
"AutoChangedDate": "ChangedDate: DateTime (auto-set on commit)",
136140
"Binary": "Binary data (files, images)",
137141
"HashedString": "Securely hashed string (for passwords)",
138142
"Enumeration(Name)": "Reference to an enumeration (e.g., Enumeration(MyModule.Status))",

cmd/mxcli/help_topics/entity.txt

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,27 @@ Attribute Constraints:
4141
Note: CALCULATED attributes are only supported on persistent entities.
4242

4343
System Attributes (auditing):
44-
STORE OWNER - Adds System.owner association (System.User)
45-
STORE CHANGED BY - Adds System.changedBy association (System.User)
46-
STORE CREATED DATE - Adds CreatedDate attribute (DateTime, auto-set on insert)
47-
STORE CHANGED DATE - Adds ChangedDate attribute (DateTime, auto-set on every commit)
44+
AutoOwner - System.owner association (auto-set on create)
45+
AutoChangedBy - System.changedBy association (auto-set on every commit)
46+
AutoCreatedDate - CreatedDate: DateTime (auto-set on create)
47+
AutoChangedDate - ChangedDate: DateTime (auto-set on every commit)
4848

49-
These clauses go AFTER the closing parenthesis of attributes:
49+
These are pseudo-types like AutoNumber — list them as regular attributes:
5050

5151
CREATE PERSISTENT ENTITY MyModule.AuditedEntity (
52-
Name: String(100)
53-
)
54-
STORE OWNER
55-
STORE CHANGED BY
56-
STORE CREATED DATE
57-
STORE CHANGED DATE;
52+
Name: String(100),
53+
Owner: AutoOwner,
54+
ChangedBy: AutoChangedBy,
55+
CreatedDate: AutoCreatedDate,
56+
ChangedDate: AutoChangedDate
57+
);
5858

5959
Use ALTER ENTITY to toggle on existing entities:
6060
ALTER ENTITY MyModule.Entity SET STORE OWNER;
6161
ALTER ENTITY MyModule.Entity DROP STORE CHANGED DATE;
6262

63+
To toggle on existing entities, use ALTER ENTITY SET/DROP STORE.
64+
6365
Complete Example:
6466
CREATE PERSISTENT ENTITY MyModule.Customer (
6567
-- Customer identification

cmd/mxcli/lsp_completions_gen.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs-site/src/language/entities.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,25 +97,25 @@ CREATE OR MODIFY PERSISTENT ENTITY Sales.Customer (
9797

9898
## System Attributes (Auditing)
9999

100-
Persistent entities can track who created/modified objects and when, using built-in system attributes. Add these clauses after the closing parenthesis:
100+
Persistent entities can track who created/modified objects and when. Declare them as regular attributes using pseudo-types (like `AutoNumber`):
101101

102102
```sql
103103
CREATE PERSISTENT ENTITY Sales.Order (
104104
OrderNumber: AutoNumber,
105-
TotalAmount: Decimal
106-
)
107-
STORE OWNER
108-
STORE CHANGED BY
109-
STORE CREATED DATE
110-
STORE CHANGED DATE;
105+
TotalAmount: Decimal,
106+
Owner: AutoOwner,
107+
ChangedBy: AutoChangedBy,
108+
CreatedDate: AutoCreatedDate,
109+
ChangedDate: AutoChangedDate
110+
);
111111
```
112112

113-
| Clause | System Attribute | Type | Set When |
114-
|--------|-----------------|------|----------|
115-
| `STORE OWNER` | `System.owner` | Association → System.User | Object created |
116-
| `STORE CHANGED BY` | `System.changedBy` | Association → System.User | Every commit |
117-
| `STORE CREATED DATE` | `CreatedDate` | DateTime | Object created |
118-
| `STORE CHANGED DATE` | `ChangedDate` | DateTime | Every commit |
113+
| Pseudo-Type | System Attribute | Set When |
114+
|-------------|-----------------|----------|
115+
| `AutoOwner` | `System.owner` (→ System.User) | Object created |
116+
| `AutoChangedBy` | `System.changedBy` (→ System.User) | Every commit |
117+
| `AutoCreatedDate` | `CreatedDate` (DateTime) | Object created |
118+
| `AutoChangedDate` | `ChangedDate` (DateTime) | Every commit |
119119

120120
Toggle on existing entities with ALTER ENTITY:
121121

docs/01-project/MDL_QUICK_REFERENCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ CREATE PERSISTENT ENTITY Module.Photo (
3636
|-----------|--------|-------|
3737
| Create entity | `CREATE [OR MODIFY] PERSISTENT\|NON-PERSISTENT ENTITY Module.Name (attrs);` | Persistent is default |
3838
| Create with extends | `CREATE PERSISTENT ENTITY Module.Name EXTENDS Parent.Entity (attrs);` | EXTENDS before `(` |
39-
| Create with auditing | `CREATE PERSISTENT ENTITY Module.Name (attrs) STORE OWNER STORE CHANGED BY STORE CREATED DATE STORE CHANGED DATE;` | System attributes added automatically |
39+
| Create with auditing | `CREATE PERSISTENT ENTITY Module.Name (attrs, Owner: AutoOwner, ChangedBy: AutoChangedBy, CreatedDate: AutoCreatedDate, ChangedDate: AutoChangedDate);` | Pseudo-types like AutoNumber |
4040
| Create view entity | `CREATE VIEW ENTITY Module.Name (attrs) AS SELECT ...;` | OQL-backed read-only |
4141
| Create external entity | `CREATE EXTERNAL ENTITY Module.Name FROM ODATA CLIENT Module.Client (...) (attrs);` | From consumed OData |
4242
| Create external entities | `CREATE [OR MODIFY] EXTERNAL ENTITIES FROM Module.Client [INTO Module] [ENTITIES (...)];` | Bulk from $metadata |

mdl-examples/doctype-tests/26-system-attribute-examples.mdl

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,16 @@
22
-- System Attribute Examples (Issue #182)
33
-- ============================================================================
44
--
5-
-- Mendix persistent entities support four optional system attributes that
6-
-- provide built-in auditing:
5+
-- Mendix persistent entities support four optional system attributes for
6+
-- built-in auditing. Declare them as pseudo-types (like AutoNumber):
77
--
8-
-- STORE OWNER -> System.owner association
9-
-- STORE CHANGED BY -> System.changedBy association
10-
-- STORE CREATED DATE -> CreatedDate attribute (DateTime)
11-
-- STORE CHANGED DATE -> ChangedDate attribute (DateTime)
8+
-- AutoOwner -> System.owner association (auto-set on create)
9+
-- AutoChangedBy -> System.changedBy association (auto-set on commit)
10+
-- AutoCreatedDate -> CreatedDate: DateTime (auto-set on create)
11+
-- AutoChangedDate -> ChangedDate: DateTime (auto-set on commit)
1212
--
13-
-- This file demonstrates enabling these flags both during CREATE ENTITY
14-
-- (as entity options) and via ALTER ENTITY (SET / DROP).
15-
--
16-
-- Usage:
17-
-- 1. Connect to your project: CONNECT LOCAL 'app.mpr';
18-
-- 2. Execute: EXECUTE SCRIPT 'mdl-examples/doctype-tests/26-system-attribute-examples.mdl';
19-
-- 3. Roundtrip: DESCRIBE ENTITY SysAttrTest.<EntityName>
13+
-- This file demonstrates enabling these both during CREATE ENTITY
14+
-- (as pseudo-typed attributes) and via ALTER ENTITY (SET / DROP STORE).
2015
--
2116
-- ============================================================================
2217

@@ -31,22 +26,22 @@ CREATE MODULE SysAttrTest;
3126
*/
3227
CREATE OR MODIFY PERSISTENT ENTITY SysAttrTest.AuditedRecord (
3328
Title: String(200) NOT NULL,
34-
Amount: Decimal
35-
)
36-
STORE OWNER
37-
STORE CHANGED BY
38-
STORE CREATED DATE
39-
STORE CHANGED DATE;
29+
Amount: Decimal,
30+
Owner: AutoOwner,
31+
ChangedBy: AutoChangedBy,
32+
CreatedDate: AutoCreatedDate,
33+
ChangedDate: AutoChangedDate
34+
);
4035

4136
-- ============================================================================
4237
-- Example 2: Subset of system attributes
4338
-- ============================================================================
4439

4540
CREATE OR MODIFY PERSISTENT ENTITY SysAttrTest.LogEntry (
4641
Message: String(500),
47-
Level: String(20)
48-
)
49-
STORE CREATED DATE;
42+
Level: String(20),
43+
CreatedDate: AutoCreatedDate
44+
);
5045

5146
-- ============================================================================
5247
-- Example 3: ALTER ENTITY to enable flags after creation
@@ -67,10 +62,10 @@ ALTER ENTITY SysAttrTest.Customer SET STORE CHANGED DATE;
6762
-- ============================================================================
6863

6964
CREATE OR MODIFY PERSISTENT ENTITY SysAttrTest.TempRecord (
70-
Payload: String(100)
71-
)
72-
STORE OWNER
73-
STORE CREATED DATE;
65+
Payload: String(100),
66+
Owner: AutoOwner,
67+
CreatedDate: AutoCreatedDate
68+
);
7469

7570
-- Disable both flags
7671
ALTER ENTITY SysAttrTest.TempRecord DROP STORE OWNER;

mdl/ast/ast_datatype.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ const (
3131
TypeDateTime
3232
TypeDate
3333
TypeAutoNumber
34+
TypeAutoOwner // System.owner association (auto-set on create)
35+
TypeAutoChangedBy // System.changedBy association (auto-set on commit)
36+
TypeAutoCreatedDate // CreatedDate DateTime (auto-set on create)
37+
TypeAutoChangedDate // ChangedDate DateTime (auto-set on commit)
3438
TypeBinary
3539
TypeEnumeration
3640
TypeEntity // Entity reference (for microflow parameters)
@@ -58,6 +62,14 @@ func (k DataTypeKind) String() string {
5862
return "Date"
5963
case TypeAutoNumber:
6064
return "AutoNumber"
65+
case TypeAutoOwner:
66+
return "AutoOwner"
67+
case TypeAutoChangedBy:
68+
return "AutoChangedBy"
69+
case TypeAutoCreatedDate:
70+
return "AutoCreatedDate"
71+
case TypeAutoChangedDate:
72+
return "AutoChangedDate"
6173
case TypeBinary:
6274
return "Binary"
6375
case TypeEnumeration:

mdl/ast/ast_entity.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@ type CreateEntityStmt struct {
4242
Position *Position
4343
Documentation string
4444
Comment string
45-
CreateOrModify bool // true for CREATE OR MODIFY
46-
StoreOwner bool // STORE OWNER (system attribute)
47-
StoreChangedBy bool // STORE CHANGED BY (system attribute)
48-
StoreCreatedDate bool // STORE CREATED DATE (system attribute)
49-
StoreChangedDate bool // STORE CHANGED DATE (system attribute)
45+
CreateOrModify bool // true for CREATE OR MODIFY
5046
}
5147

5248
func (s *CreateEntityStmt) isStatement() {}

mdl/executor/cmd_entities.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,28 @@ func (e *Executor) execCreateEntity(s *ast.CreateEntityStmt) error {
9898
}
9999

100100
// Create attributes and build name-to-ID map for validation rules and indexes
101+
// Also detect pseudo-types (AutoOwner, AutoChangedBy, etc.) and set entity flags
102+
var storeOwner, storeChangedBy, storeCreatedDate, storeChangedDate bool
103+
101104
var attrs []*domainmodel.Attribute
102105
attrNameToID := make(map[string]model.ID)
103106
for _, a := range s.Attributes {
107+
// Pseudo-types: set entity flags instead of creating attributes
108+
switch a.Type.Kind {
109+
case ast.TypeAutoOwner:
110+
storeOwner = true
111+
continue
112+
case ast.TypeAutoChangedBy:
113+
storeChangedBy = true
114+
continue
115+
case ast.TypeAutoCreatedDate:
116+
storeCreatedDate = true
117+
continue
118+
case ast.TypeAutoChangedDate:
119+
storeChangedDate = true
120+
continue
121+
}
122+
104123
// CALCULATED attributes are only supported on persistent entities
105124
if a.Calculated && !persistable {
106125
return fmt.Errorf("attribute '%s': CALCULATED attributes are only supported on persistent entities", a.Name)
@@ -235,10 +254,10 @@ func (e *Executor) execCreateEntity(s *ast.CreateEntityStmt) error {
235254
ValidationRules: validationRules,
236255
Indexes: indexes,
237256
EventHandlers: eventHandlers,
238-
HasOwner: s.StoreOwner,
239-
HasChangedBy: s.StoreChangedBy,
240-
HasCreatedDate: s.StoreCreatedDate,
241-
HasChangedDate: s.StoreChangedDate,
257+
HasOwner: storeOwner,
258+
HasChangedBy: storeChangedBy,
259+
HasCreatedDate: storeCreatedDate,
260+
HasChangedDate: storeChangedDate,
242261
}
243262

244263
// Set generalization (inheritance) if specified

0 commit comments

Comments
 (0)