drizzle-team / drizzle-team/drizzle-orm

[BUG]: After `drizzle-kit up` (Postgres) the next `generate` produces a large (no-op) migration causing full rewrite

Open
#6,020 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
35.8k
Forks
1.6k
Avg merge
2d 7h
Merged PRs (30d)
4

Description

### Report hasn't been filed before.

- [x] I have verified that the bug I'm about to report hasn't been filed before.

### What version of `drizzle-orm` are you using?

1.0.0-rc.4

### What version of `drizzle-kit` are you using?

1.0.0-rc.4

### Other packages

_No response_

### Describe the Bug

> This looks like the Postgres counterpart of #5661, where the SQLite `up` converter was fixed to normalize old default values.

After upgrading a Postgres project from `drizzle-orm@0.45.2` / `drizzle-kit@0.31.10` to `1.0.0-rc.4` and running `drizzle-kit up`, the very next `drizzle-kit generate` produces a large migration even though the schema hasn't changed.

In my case it emitted 24 `ALTER COLUMN … SET DATA TYPE … USING …` statements, plus a DROP/CREATE of a view, a DROP/CREATE of a unique index, and several DROP/ADD CHECK constraints.

None of these are real changes. The `SET DATA TYPE … USING …` statements are the dangerous part: Postgres runs a **full table rewrite** for each one, so applying this "empty" migration would rewrite every table (including our largest) and rebuild indexes for nothing.

The cause is that `drizzle-kit up` migrates the snapshot structure but keeps the SQL fragments in their 0.x textual form, while the rc.4 schema reader renders the same fragments slightly differently. `generate` then diffs "old string" against "new string", sees a difference everywhere, and emits DDL — even though Postgres treats the two forms as identical. The string drift I found between the converted snapshot and a fresh rc.4 read:

| | converted snapshot (0.x form) | rc.4 render |
|---|---|---|
| timestamp precision | `timestamp (3) with time zone` | `timestamp(3) with time zone` |
| check constraint | `"table"."billing_account_id" IS NULL …` | `"billing_account_id" IS NULL …` |
| partial index predicate | `"table"."is_empty" = true` | `"is_empty" = true` |
| view definition | `jsonb_object_agg("provider", …)` | `jsonb_object_agg("table"."provider", …)` |

To confirm the timestamp case: the converted snapshot contains 24 spaced `timestamp (3)` entries, a fresh snapshot contains 24 unspaced `timestamp(3)` entries, and the generated migration contains exactly 24 `SET DATA TYPE`. If I hand-normalize `timestamp (3)` → `timestamp(3)` in the converted snapshot and re-run `generate`, those 24 `ALTER`s disappear.

## What are the steps to reproduce it?

1. Start from a project on `drizzle-orm@0.45.2` / `drizzle-kit@0.31.10` whose schema uses at least one of: a `timestamp` column with `precision`, a `check()` constraint, a partial (`.where(...)`) index, or a `pgView`.
2. Upgrade to `drizzle-orm@1.0.0-rc.4` / `drizzle-kit@1.0.0-rc.4`.
3. Run `drizzle-kit up`.
4. Without touching the schema, run `drizzle-kit generate`.
5. A non-empty migration is produced (for the timestamp case, `ALTER COLUMN … SET DATA TYPE … USING …`).

## What is the desired result?

`drizzle-kit up` should normalize the SQL fragments in the converted snapshot to the same textual form the rc.4 reader produces, so that an unchanged schema yields an empty migration.

## Environment

- **Database engine / provider:** PostgreSQL (Neon in production).
- **Driver:** `pg` (`drizzle-orm/node-postgres`).
- **Monorepo:** yes — pnpm workspaces + Turborepo.
- **Types-related?** No, this is a `drizzle-kit` codegen issue, not a type-inference one.
- **Runtime:** Node.js.
- **Versions:** `drizzle-orm` and `drizzle-kit` at `1.0.0-rc.4`, upgraded from `0.45.2` / `0.31.10`.

Contributor guide

Open the contributing guide

Research direction

Start at the drizzle-kit up snapshot conversion and the schema reader used by generate, comparing the timestamp, constraint, partial-index, and view SQL fragments described in the report. Reproduce with a PostgreSQL schema containing one of those features, then run up followed by generate. Done means an unchanged schema produces an empty migration without redundant ALTER, DROP, or CREATE statements.

Written by the indexing model from the issue text.

Assessment

Tech stack
nodejs, postgresql, sql, typescript
Domain
databases, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.