Skip to content

Order migration 0021 so it never drops a table with children - #192

Merged
admdly merged 3 commits into
mainfrom
claude/fix-0021-d1-foreign-keys
Aug 8, 2026
Merged

Order migration 0021 so it never drops a table with children#192
admdly merged 3 commits into
mainfrom
claude/fix-0021-d1-foreign-keys

Conversation

@admdly

@admdly admdly commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Fixes the failed remote apply of 0021 from #190. No data was changed by that run; it rolled back whole, 0021 is not recorded in d1_migrations, and the remote schema is still at 0020. I confirmed that against the production database before touching anything.

Applying 0021 to the remote database failed with "FOREIGN KEY constraint
failed" while local succeeded. The migration opened with
PRAGMA foreign_keys=OFF, which is a documented no-op inside a transaction
- wrangler wraps each migration file in one, so it silently did nothing
against D1. The tests ran statements outside a transaction, where the
pragma works, which is precisely why they could not see the difference.

PRAGMA defer_foreign_keys is not a substitute, which I verified before
relying on it: every statement succeeds and PRAGMA foreign_key_check
comes back clean, but COMMIT still fails, because DROP TABLE on a parent
increments SQLite's deferred-violation counter once per child row and
renaming the replacement into place never decrements it. SQLite's own
12-step ALTER procedure sidesteps this by demanding foreign_keys=OFF,
the one thing unavailable here.

The fix is ordering, not a pragma, so the pragma is removed rather than
left in as decoration. extension_submissions is copied into a holding
table and dropped before extensions is rebuilt, leaving extensions
childless at the moment it is replaced; extension_revisions is created
afterwards. 0021 now applies with foreign keys fully enforced.

The developers rebuild is dropped for the same reason: developer_claims,
developer_transfers and extensions all reference it, so it can never be
dropped this way, and rebuilding all three children to replace a default
nothing reads is not a trade worth making. created_at/updated_at keep the
1970 placeholder, with a note on why it stays.

migrations.test.ts gains applyAllAsD1(), which runs the chain with
foreign keys enforced and 0021 inside a transaction. Against 0021 as
merged it reproduces the production failure exactly; against this one it
passes.

No data was changed by the failed run: it rolled back whole, 0021 is not
recorded in d1_migrations, and the remote schema is still at 0020.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 8, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
api f772bc4 Commit Preview URL

Branch Preview URL
Aug 08 2026, 03:12 PM

@admdly admdly self-assigned this Aug 8, 2026
Follow-up to the ordering fix. The reference check ran at the end of the
file, where it could never fire: with foreign keys enforced the rebuilt
table's own constraint rejects a dangling row during the copy, so the
failure arrived as a bare "FOREIGN KEY constraint failed" and the named
check was dead code. It only ever fired in tests, which ran with foreign
keys off.

Moved to the front with the other pre-flight checks, so it fires before
anything is copied and names what is wrong. Its second half is dropped:
extension_revisions.extension_id comes from a join against extensions and
cannot dangle by construction. The test now runs this case through
applyAllAsD1() rather than with foreign keys off, so it exercises the
path that actually runs.

Also trimmed the comments this change added - the pragma explanation, the
holding-table note, the developers-default note and applyAllAsD1's
header were all saying the same thing more than once.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread test/services/extensions/v2/migrations.test.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 3 files (changes from recent commits).

Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.

Re-trigger cubic

The test named for the drop-parent-with-children ordering did not
actually exercise it. seedSubmissionFixture creates no submissions, so
extension_submissions was empty and extensions had no children - and
SQLite only raises on DROP TABLE when a child row actually references a
deleted one. Reintroducing the old ordering left this test green.

The evidence was already in front of me: when I mutation-checked the
ordering earlier, three tests failed and this one was not among them.
They fail because the SQL references a dropped table, not because of
foreign key enforcement, so they would not have caught the production
failure either.

It now seeds a submission with extension_id set. Null is not enough -
extension_id is what makes extensions a parent, and every submission in
production happened to be null, which is why the remote apply failed on
the developers drop rather than this one. Restoring the old ordering now
fails this test.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Auto-approved: Fixes a broken migration that failed on remote by reordering table drops and adding a pre-check, with a test that replicates D1's foreign-key enforcement to prevent regression. No new behavior, rollout, or operational tradeoff.

Re-trigger cubic

@admdly
admdly merged commit a35a3f1 into main Aug 8, 2026
9 checks passed
@admdly
admdly deleted the claude/fix-0021-d1-foreign-keys branch August 8, 2026 15:16
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