Skip to content

feat: add rollback/down migrations support#5476

Open
roor0 wants to merge 5 commits into
drizzle-team:betafrom
roor0:feat/down-migrations
Open

feat: add rollback/down migrations support#5476
roor0 wants to merge 5 commits into
drizzle-team:betafrom
roor0:feat/down-migrations

Conversation

@roor0
Copy link
Copy Markdown

@roor0 roor0 commented Mar 12, 2026

Summary

Adds automatic down migration generation and runtime rollback support to Drizzle.

Down SQL Generation (drizzle-kit)

  • drizzle-kit generate now produces a down.sql file alongside each migration.sql
  • Down SQL is generated by running the schema diff in reverse with rename-aware resolvers
  • Renames produce proper RENAME statements instead of destructive DROP/CREATE
  • Supported for all dialects: PostgreSQL, MySQL, SQLite, LibSQL, SingleStore, CockroachDB
  • Bundled migrations (Expo, Durable SQLite, OP-SQLite) gain a downMigrations record in migrations.js

Runtime Rollback (drizzle-orm)

  • New rollback(db, config, steps?) export on every driver migrator
  • Reads the N most-recently-applied migrations from the tracking table
  • Executes their down SQL in reverse order within a transaction
  • Removes the corresponding tracking rows
  • Supports all 30+ driver adapters

Files Changed

  • drizzle-kit: generate-common.ts, generate-down-helpers.ts (new), all generate-*.ts files
  • drizzle-orm: migrator.ts, all dialect dialect.ts files, all driver migrator.ts files
  • Tests: down-sql.test.ts, down-helpers.test.ts

Test plan

  • Unit tests for writeResult down SQL file generation
  • Unit tests for embeddedMigrations down SQL bundling
  • Unit tests for withCapture rename capture
  • Unit tests for makeInverseResolver rename inversion
  • TypeScript compilation passes (tsc --noEmit)
  • Integration tests with actual database rollback (future)

@roor0 roor0 changed the title feat: add rollback/down migrations support [All]: Add rollback/down migrations support Mar 12, 2026
@roor0 roor0 force-pushed the feat/down-migrations branch from 5755197 to c3f9e83 Compare March 25, 2026 02:03
@roor0 roor0 changed the base branch from main to beta March 25, 2026 02:03
@roor0 roor0 changed the title [All]: Add rollback/down migrations support feat: add rollback/down migrations support Mar 25, 2026
@theholla
Copy link
Copy Markdown

theholla commented Apr 7, 2026

I'm curious if this is still in the works? My team just started using drizzle and this rollback piece would def come in handy.

@roor0 roor0 force-pushed the feat/down-migrations branch 2 times, most recently from 9c397cd to e8e001c Compare April 14, 2026 08:30
@alexbitar98
Copy link
Copy Markdown

please let's push this asap its very needed

@a-husic
Copy link
Copy Markdown

a-husic commented Apr 16, 2026

do we really need to put this all over reddit? Please prioritize

@AndriiSherman
Copy link
Copy Markdown
Member

Thanks for you PR!

The question is, how would custom migrations be handled? How would it handle the cases, where someone changed generated sql file with some custom statements?

roor0 added a commit to roor0/drizzle-orm that referenced this pull request Apr 19, 2026
Addresses the custom-migration question on drizzle-team#5476:

- `drizzle-kit generate --custom` now writes a `down.sql` scaffold
  (`-- Custom SQL rollback file, put your reverse statements below! --`)
  alongside the empty `migration.sql`, giving users a clear home for
  hand-written rollback SQL.
- Auto-generated `down.sql` files are now prefixed with a header noting
  they are editable and should be kept in sync with `migration.sql` when
  the developer hand-edits it.
- New `generateDownMigrations` config option (defaults to `true`) for
  teams that prefer to author rollback SQL entirely by hand — skips both
  the reverse diff and the down.sql file emission.
@roor0 roor0 force-pushed the feat/down-migrations branch from e8e001c to 66d84f9 Compare April 19, 2026 05:59
@roor0
Copy link
Copy Markdown
Author

roor0 commented Apr 19, 2026

Good question — a couple of things worth calling out explicitly, and I've just pushed a follow-up commit addressing them:

1. drizzle-kit generate --custom — previously skipped down.sql entirely. It now emits an empty scaffold (-- Custom SQL rollback file, put your reverse statements below! --) alongside the placeholder migration.sql, so users have a clear home for the inverse statements.

2. Hand-edited generated migrations — the auto-generated down.sql is produced from the reverse schema diff and is intended as a starting point. It's now prefixed with a header explicitly stating it's editable and should be kept in sync with migration.sql when the developer adds custom SQL. This matches the Rails / Flyway / TypeORM convention: the tool seeds a reasonable default, the developer owns the file.

3. Opt-out — a new generateDownMigrations config flag (default true) is available for teams who'd rather manage rollback SQL entirely by hand. Setting it to false skips both the reverse diff and the down.sql emission.

Happy to iterate further if you'd like a different contract here.

@roor0 roor0 force-pushed the feat/down-migrations branch from 66d84f9 to d919cbc Compare May 7, 2026 18:43
roor0 added 5 commits May 28, 2026 16:09
Generate down.sql files alongside migration.sql during `drizzle-kit generate`.
Down SQL is produced by running the schema diff in reverse with rename-aware
resolvers so that renames produce RENAME statements instead of DROP/CREATE.

Adds `rollback()` export to every driver migrator and dialect class.
Rollback reads the N most-recently-applied migrations from the tracking table,
executes their down SQL in reverse order, and removes the tracking rows.

Bundled migration support (Expo, Durable SQLite, OP-SQLite) gains a
`downMigrations` record in the generated migrations.js file.
- Use sql.identifier() in proxy rollback DELETE statements instead of
  raw string interpolation (mysql-proxy, pg-proxy, sqlite-proxy,
  singlestore-proxy)
- Use path.dirname() instead of string replace for Windows-safe
  down.sql detection in embeddedMigrations
- Standardize SELECT id (not rowid) in SQLite/durable-sqlite rollback
- Tighten invertRenames matching to include schema/table properties,
  not just name, to correctly handle column renames across tables
- Fix embeddedMigrations output indentation
- Remove redundant downSqlDelimiter variable
- Add TODO comments for missing node-mssql and effect-postgres rollback
- Expand test coverage: breakpoints:false, multiple captures,
  schema/table disambiguation, partial match, breakpoint count/order
…e-hash collision

When two migrations have identical SQL content they share the same hash.
The previous `migrations.find(m => m.hash === dbMigration.hash)` could
return the wrong migration's downSql. Now all rollback lookups fetch
`name` from the tracking table and match on both hash and name, with a
guard for old rows that lack a name value.
Addresses the custom-migration question on drizzle-team#5476:

- `drizzle-kit generate --custom` now writes a `down.sql` scaffold
  (`-- Custom SQL rollback file, put your reverse statements below! --`)
  alongside the empty `migration.sql`, giving users a clear home for
  hand-written rollback SQL.
- Auto-generated `down.sql` files are now prefixed with a header noting
  they are editable and should be kept in sync with `migration.sql` when
  the developer hand-edits it.
- New `generateDownMigrations` config option (defaults to `true`) for
  teams that prefer to author rollback SQL entirely by hand — skips both
  the reverse diff and the down.sql file emission.
@roor0 roor0 force-pushed the feat/down-migrations branch from d919cbc to 609a762 Compare May 28, 2026 22:22
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.

[FEATURE]: Reverse/Down Migrations [FEATURE]: Migration Rollback feature

5 participants