drizzle-team / drizzle-team/drizzle-orm
[BUG]: drizzle-kit pull (Postgres, 0.31.10) introspection is not generate-clean: cast-form CHECKs, numeric defaults, partitioned parents, index opclasses
- 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?
0.45.2
### What version of `drizzle-kit` are you using?
0.31.10
### Describe the Bug
Running `drizzle-kit pull` against a Postgres 16 database and then `drizzle-kit generate` produces spurious DROP/ADD statements — the introspected schema is not symmetric with the generator. Four concrete defects, all verified against the same database:
**1. CHECK constraints re-imported with mid-expression casts, so the next generate emits spurious CHECK DROP/ADD.**
In the database (and in `pg_get_expr`), the constraint is:
```sql
CONSTRAINT shop_profiles_vertical_check CHECK (((vertical)::text = ANY (ARRAY['supplements'::text, ...])))
```
The introspection code strips casts only with a trailing-only regex (`res.replace(/(::[\w.\s]+...)+$/gi, '')`), so mid-expression casts like `(vertical)::text` survive in the pulled `schema.ts`. The generated `check()` then differs textually from the snapshot, and `generate` wants to drop and re-add the constraint.
**2. `numeric` column defaults re-imported as strings.**
A column with `DEFAULT 1` (`numeric`) is pulled as `.default('1')` (string literal), while the schema defines `default(1)`. `generate` emits a spurious default-value diff. (Same family as #5661, which was fixed for SQLite via a default-value normalizer — Postgres never got one.)
**3. Partitioned parent tables are dropped from introspection entirely.**
The introspection query filters `relkind IN ('r','v','m')` — partitioned parents (`relkind = 'p'`) are never selected, so a `PARTITION BY RANGE` parent table simply disappears from the pulled schema (its leaf partitions survive as plain tables). I see rc.4's query now includes `'p'` (`relkind IN ('r','p','v','m')`), but since the drizzle schema DSL has no partition concept, it's unclear whether rc can actually model it — worth verifying before 1.0 goes stable.
**4. Index opclasses are mis-assigned or lost.**
Four of our indexes declare opclasses (`uuid_ops` on a btree over uuid, `text_ops`/`timestamptz_ops` elsewhere). The pulled schema assigns wrong opclasses to some of them and drops others, producing spurious index diffs on the next `generate`.
### Expected behaviour
`pull` → `generate` on an unchanged database should report `No schema changes`. Today it emits DROP/ADD pairs for every defect above — and since these DROPs have no `IF EXISTS`, applying the generated migration against a rebuilt database fails loudly or destroys constraints.
### Additional context
- Looks like the same asymmetry family as #6020 (Postgres `up` → `generate` no-op rewrite) and #5661 (SQLite, fixed via a default normalizer). @AlexBlokh mentioned a snapshots rewrite is in progress — do these four 0.31 pull defects fall inside that rewrite's scope? Happy to provide a minimal repro repo if useful.
- Our workaround meanwhile: we hand-align the snapshot and treat `generate` reporting `No schema changes` as the acceptance check, and we've removed `db:pull` from our scripts entirely.
Contributor guide
Research direction
Start at the PostgreSQL introspection path used by drizzle-kit pull, including the relkind query, then reproduce the report against PostgreSQL 16 and run drizzle-kit generate. Done means an unchanged pull→generate reports “No schema changes” while preserving CHECK expressions, numeric defaults, partitioned parents, and index opclasses.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- postgresql, typescript
- Domain
- cli, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100