Skip to content

fix(db): make draft_admin_integrity safely re-enable-able, keep flag OFF (#2993) - #2999

Merged
jung-thomas merged 8 commits into
DEVfrom
worktree-draft-integrity-constraints-2993
Nov 2, 2026
Merged

jung-thomas merged 8 commits into
DEVfrom
worktree-draft-integrity-constraints-2993

Conversation

@jung-thomas

Copy link
Copy Markdown
Contributor

Closes #2993.

What & why

#2909 enabled cds.fiori.draft_admin_integrity: true without the required ON DELETE CASCADE constraints on the ADMINSERVICE_*_DRAFTS tables → orphaned child draft rows → PROD outage (2026-11-02). #2992 disabled the flag; #2994 swept orphans.

This PR does not re-enable the flag. Per decision (another outage is unacceptable), it proves re-enabling is safe and keeps the flag OFF — delivering the artifacts and the gate so a future flip is a documented, verified step rather than a leap.

The flag stays false in package.json (both cds.fiori and [development] blocks).

Changes

  1. Build-time cascade-FK patch (scripts/patch-draft-cascade-constraints.cjs + unit test, wired into .deploy/mta.yaml before-all). cds build already emits the 46 AdminService.*_drafts.hdbtable with the DraftAdministrativeData_DraftUUID column but no cascade clause — this appends FOREIGN KEY ("DRAFTADMINISTRATIVEDATA_DRAFTUUID") REFERENCES "DRAFT_DRAFTADMINISTRATIVEDATA"("DRAFTUUID") ON DELETE CASCADE to each, prod gen/db tree only (QA emits no draft tables). Idempotent; fails loud if zero draft tables found; skips tables lacking the FK column.
  2. Flag-agnostic orphan gate test (test/admin-drafts-cascade-gate.test.js). Mission draftEdit→discard leaves zero child draft rows, asserted via the service-layer .drafts accessor on SQLite — correct whether deletion comes from today's app-level reconcile or a future DB cascade. This is the re-enable gate.
  3. Re-enable runbook (docs/developers/operations/draft-integrity-reenable.md + CLAUDE.md pointer): sweep orphans → deploy constraints → preflight verified (N/N) → gate green → flip flag → redeploy.

⚠️ Deploy-timing note for the reviewer

The constraint patch runs on every MTA build. So the next deploy of this branch starts shipping the enforced cascade FK to HANA even with the flag off. This is intended (the FK is inert at the runtime level until the flag flips) — but the FK is ENFORCED (an inline FK inside COLUMN TABLE (...) cannot be NOT VALIDATED), so orphaned draft rows must be swept BEFORE the constraint-bearing build deploys or HANA will reject the deploy. The runbook sequences the sweep first. It's a real DB-schema change on next deploy, not a no-op — coordinate deploy timing accordingly.

Testing

npx vitest run test/unit/patch-draft-cascade-constraints.test.js test/admin-drafts-cascade-gate.test.js test/admin-drafts-discard-reconcile.test.js → 10/10 green. Real-build spot check confirms the uppercase FK lands in AdminService.Missions_drafts.hdbtable.

🤖 Generated with Claude Code

…t enabled (#2993)

Adds scripts/patch-draft-cascade-constraints.cjs which enumerates all
*_drafts.hdbtable files emitted by `npx cds build --production` and
injects a FOREIGN KEY ... ON DELETE CASCADE clause for the
DraftAdministrativeData_DraftUUID column so HANA can enforce cascade-
delete of orphaned draft rows.

Wired into the MTA before-all builder (prod gen tree only; QA tree
emits zero draft tables). The draft_admin_integrity flag remains false
in package.json — this task only makes the constraints deployable.

Verified: patched 46 / skipped 0 against a real production build.
… cascade patch (#2993)

Two review findings:
- Comma now trails PRIMARY KEY(ID) line (trailing-comma style) rather
  than appearing as a bare leading comma on the separator line. Fixed by
  trimEnd()ing the body slice so the comma attaches to the last element.
- Idempotency regex tightened from /ON DELETE CASCADE/i to a pattern
  that keys on DraftAdministrativeData_DraftUUID + ON DELETE CASCADE
  (cross-paren match via [\s\S]*?) so a different column's cascade
  clause cannot be mistaken for our injection.

New test: 'comma trails the previous line — no leading-comma style'
asserts PRIMARY KEY(ID), immediately precedes FOREIGN KEY.
5/5 tests pass; real build: patched 46 / skipped 0.
Critical fix: local column in FK clause changed from quoted mixed-case
("DraftAdministrativeData_DraftUUID") to quoted UPPERCASE
("DRAFTADMINISTRATIVEDATA_DRAFTUUID"). The column is declared bare
(unquoted) in the generated .hdbtable, so HANA folds it to uppercase;
a quoted mixed-case reference is case-sensitive and would not resolve.
Confirmed by srv/lib/draft-integrity-preflight.js:93,100 and
scripts/cleanup-draft-orphans.cjs:55 both using the uppercase name.

Idempotency regex updated to match the uppercase token.

Test updated: FK-match assertion now requires "DRAFTADMINISTRATIVEDATA_DRAFTUUID"
(exact uppercase, not /i on the column name) and adds a regression guard
that fails on the mixed-case quoted form.

Docs corrected across runbook, spec, and plan: inline FK is ENFORCED
(NOT VALIDATED is an ALTER TABLE affordance only), so orphan sweep must
come BEFORE deploying the constraint-bearing build. Runbook reordered:
Step 1 = sweep, Step 2 = deploy (with enforced-FK precondition noted).
All "NOT VALIDATED / no pre-clean needed" claims removed.

Real build: patched 46 / skipped 0; PRIMARY KEY(ID), FOREIGN KEY
("DRAFTADMINISTRATIVEDATA_DRAFTUUID") ... ON DELETE CASCADE confirmed.
5/5 tests pass.
@jung-thomas
jung-thomas marked this pull request as ready for review November 2, 2026 22:25
@jung-thomas
jung-thomas merged commit f522aa9 into DEV Nov 2, 2026
4 of 5 checks passed
@jung-thomas
jung-thomas deleted the worktree-draft-integrity-constraints-2993 branch November 2, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant