fix(sqlite-kit): preserve NOT NULL on non-integer primary key columns - #6168
fix(sqlite-kit): preserve NOT NULL on non-integer primary key columns#6168webdevsamran wants to merge 1 commit into
Conversation
SQLite only enforces NOT NULL on INTEGER PRIMARY KEY columns; for every other PK shape (TEXT/BLOB/REAL, INT, composite) NULLs are allowed, so the flag is load-bearing. The v1 schema serializer and the v6->v7 snapshot upgrader stripped notNull from all primary key columns, so generated migrations emitted nullable text primary keys (regression from 0.31 behavior). The DDL convertor already omits NOT NULL for single-column integer PKs, so preserving the flag here keeps that behavior intact while restoring NOT NULL everywhere else.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c9191c0d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| type: column.getSQLType(), | ||
| default: defalutValue, | ||
| notNull: column.notNull && !primaryKey, | ||
| notNull: column.notNull, |
There was a problem hiding this comment.
Emit a migration for existing nullable primary keys
For projects whose existing v7 snapshot or database was produced by the buggy release, this changes the desired text-PK column from notNull: false to true, but ddlDiff unconditionally deletes every single-column primary-key notNull alteration in drizzle-kit/src/dialects/sqlite/diff.ts:259-260. Both generate and push therefore emit no table recreation, and generate records the new snapshot as non-null despite leaving the actual column nullable, so affected databases are never repaired. Restrict that suppression to actual INTEGER PRIMARY KEY rowid aliases so non-integer PKs receive a corrective migration.
Useful? React with 👍 / 👎.
Summary
Fixes #6165.
On the v1 (
beta) line,drizzle-kit generatedropsNOT NULLfrom every primary-key column when serializing a Drizzle schema into the interim/snapshot representation:drizzle-kit/src/dialects/sqlite/drizzle.ts:notNull: column.notNull && !primaryKeydrizzle-kit/src/cli/commands/up-sqlite.ts(v6→v7 upgrader):notNull: column.notNull && !column.primaryKeySince SQLite only enforces non-NULL values on
INTEGER PRIMARY KEYcolumns (rowid alias),NOT NULLis load-bearing for every other PK shape —text('id').primaryKey()produced:which silently allows
NULLids — a regression from 0.31 behavior and a data-integrity hazard for anyone who doesn't hand-patch their migrations.The DDL convertor (
drizzle-kit/src/dialects/sqlite/convertor.ts) already implements the correct SQLite-specific rule: it omitsNOT NULLonly for single-column integer PKs (omitNotNull = isColumnPk && column.type.toLowerCase().startsWith('int')). The serializer stripping the flag upstream of it made that logic unreachable for PK columns. This change makes the serializer faithful (notNull: column.notNull) and lets the convertor keep deciding what to emit, which:NOT NULLfor TEXT/BLOB/REAL/INT PKs and any explicitly.notNull()PK column,NOT NULLfor single-columninteger().primaryKey()(the behavior requested in [FEATURE]: SQLite.primaryKey().notNull()#2611),Reproduction (before)
With
drizzle-orm@1.0.0-rc.4+drizzle-kit@1.0.0-rc.4:npx drizzle-kit generate→`id` text PRIMARY KEY(noNOT NULL; verified: SQLite acceptsINSERT INTO users VALUES (NULL)).Testing
New regression tests in
drizzle-kit/tests/sqlite/sqlite-pk-notnull.test.ts:notNull: truein the serialized schema`id` text PRIMARY KEY NOT NULLfor text PKs`id` integer PRIMARY KEYwithoutNOT NULLfor single-column integer PKsVerified red→green: with the fix stashed, 2 of the 3 new tests fail; with the fix applied all pass. Full
tests/sqlite/suite comparison on this machine (Windows checkout lacks a better-sqlite3 prebuilt for Node 24, so DB-backed suites fail identically before and after the change): cleanbeta→ 23 failed / 21 passed; with this change → 21 failed / 23 passed (+2 = the new regression tests; no previously passing test regressed).oxlintclean on all changed files.