fix: preserve MPR v1 contents hash and UnitID across DROP+CREATE#258
fix: preserve MPR v1 contents hash and UnitID across DROP+CREATE#258ako merged 3 commits intomendixlabs:mainfrom
Conversation
MPR v1 stores unit bodies inline in the SQLite database with a ContentsHash column that Studio Pro uses as an integrity check. The v1 INSERT and UPDATE paths in Writer.insertUnit / Writer.updateUnit were passing an empty string (or nothing at all) for ContentsHash, which left the stored hash out of sync with the actual bytes after any mxcli write. On the next open, Studio Pro refused the file with a corruption error. v2 already computed the hash correctly — the fix extracts that logic into contentHashBase64() and uses it in both schema paths. The v1 UPDATE also falls back to the older schema that doesn't have ContentsHash so projects on pre-ContentsHash schemas still succeed. Regression test (sdk/mpr/writer_units_test.go) verifies the hash column matches sha256(Contents) after insert and update on both v1 and v2. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Studio Pro treats Unit rows with a new UnitID (for the same qualified name) as unrelated documents, producing ".mpr does not look like a Mendix Studio Pro project file" errors. The executor now caches the dropped microflow's UnitID, ContainerID, and AllowedRoles via droppedMicroflows, and a follow-up CREATE OR REPLACE/MODIFY reuses them, turning delete+insert into a semantically in-place update. Also hardens scripts/run-mdl-tests.sh with explicit input-path validation so typos don't silently copy an empty sandbox.
AI Code ReviewWhat Looks Good
RecommendationApprove - The PR correctly addresses a critical compatibility issue with appropriate fixes, comprehensive tests, and full adherence to the project's architectural guidelines. The two changes are closely related aspects of the same root cause (MPR v1 corruption after roundtrips) and are appropriately scoped together. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Code ReviewOverviewTwo independent v1 MPR correctness fixes:
Both fixes are correctly motivated, well-tested, and the regression test for the UnitID case is especially thorough. IssuesSilent error swallowing in the ContentsHash fallback (both _, err := wt.tx.Exec(`UPDATE Unit SET Contents = ?, ContentsHash = ? WHERE UnitID = ?`, ...)
if err != nil {
_, err = wt.tx.Exec(`UPDATE Unit SET Contents = ? WHERE UnitID = ?`, ...)
}
return errThe fallback fires on any error from the first The same pattern appears in Performance: double SQL on every v1 write for old-schema projects With the current fallback pattern, every Missing The CLAUDE.md checklist requires every bug fix to include an MDL bug-test script in Minor notes
Strengths
SummaryThe two fixes are correct and the regression tests are solid. The main concern is the broad-catch fallback pattern in the ContentsHash writes — it should at minimum be narrowed or commented. The missing bug-test script is a checklist item. Everything else is clean. |
- Narrow v1 ContentsHash fallback to actual schema errors (ako) Previously any UPDATE error would trigger the "older schema" retry, potentially masking real failures (disk full, invalid UnitID, rolled-back tx). Now only retries when the error message mentions ContentsHash. - Drop spurious cloneRoleIDs in drop-then-create path (ako) consumeDroppedMicroflow already removed the cache entry, so the caller owns the slice — no need to clone again. - Add mdl-examples/bug-tests/258-drop-create-microflow-unitid-preservation.mdl Bug reproducer script as required by CLAUDE.md checklist.
|
Thanks @ako — addressed in 9767368:
On the double-SQL performance note: not addressed in this fixup — the fallback only fires on truly old v1 schemas that are rare in practice, and caching the schema capability at transaction start adds state I'd rather not introduce in a bug-fix PR. Happy to do it as a follow-up if you want me to file an issue. |
AI Code ReviewWhat Looks Good
RecommendationApprove. The PR resolves critical corruption issues with appropriate fixes and tests, maintains code quality standards, and follows the project's contribution practices. The changes are narrowly scoped to the reported problems without introducing unnecessary complexity. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Updated review — fix: preserve MPR v1 contents hash and UnitID across DROP+CREATEAll three concerns from the first review have been addressed. Here's a pass over the changes. What was fixedNarrow fallback () — and now only retry on the schema-absent error, not on any error. The check correctly matches both SQLite message forms ("table Unit has no column named ContentsHash" / "no such column: ContentsHash"). Real errors (disk full, invalid UnitID, rolled-back tx) propagate. ✅ ** call removed from CREATE path** — The comment makes the ownership transfer explicit: Bug reproducer script — Minor observation —
|
Summary
Two small v1 MPR storage fixes that together prevent Studio Pro from refusing to open projects after MDL roundtrips:
persist contents hashes for v1 unit writes— v1 format stored units inline in the DB but didn't refresh theContentsHashrow when a write path bypassed the writer's hash recomputation. Studio Pro would see a hash mismatch and treat the file as corrupt.preserve microflow UnitID across DROP + CREATE OR MODIFY— runningDROP MICROFLOW X; CREATE OR MODIFY MICROFLOW X ...in the same session used to delete theUnitrow and re-insert with a freshly generated UUID. Studio Pro treats the rewrittenContainerID/UnitIDpair as an unrelated document and refuses to open the resulting.mpr("file does not look like a Mendix Studio Pro project"). Fix caches the dropped microflow's UnitID on the executor and reuses it when a subsequent CREATE OR REPLACE/MODIFY targets the same qualified name, so the delete+insert behaves like an in-place update.Part of umbrella #257.
Test plan
TestDropThenCreatePreservesMicroflowUnitIDexercises the full DROP → CREATE OR MODIFY flow throughMockBackendand asserts the UnitID is preserved and the cache entry consumed.go build ./... && go test ./...pass on top of currentmain.