drizzle-team / drizzle-team/drizzle-orm

[BUG]: drizzle-kit v1 drops NOT NULL from SQLite TEXT primary keys — but SQLite allows NULL in non-INTEGER PRIMARY KEY columns (regression vs 0.31)

Open
#6,165 0 comments 0 reactions 0 assignees View on GitHub
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

**What is the undesired behavior?**

For SQLite, drizzle-kit v1 no longer emits `NOT NULL` on primary-key columns — even when `.notNull()` is explicit in the schema:

```ts
import { sqliteTable, text } from "drizzle-orm/sqlite-core";

export const users = sqliteTable("users", {
id: text("id").primaryKey(), // e.g. a UUID PK
// id: text("id").primaryKey().notNull() — same result
});
```

`drizzle-kit generate` (1.0.0-rc.4) produces:

```sql
CREATE TABLE `users` (
`id` text PRIMARY KEY
);
```

drizzle-kit 0.31.x produced `` `id` text PRIMARY KEY NOT NULL `` for the same schema.

This looks like an intentional "PRIMARY KEY implies NOT NULL" normalization — the v1 SQLite serializer drops the flag at snapshot level:

```js
notNull: column.notNull && !primaryKey,
```

That assumption is correct for standard SQL, but **not for SQLite**. Due to a long-standing documented quirk, SQLite allows NULLs in PRIMARY KEY columns unless the column is `INTEGER PRIMARY KEY`, or the table is `WITHOUT ROWID` or `STRICT` ([SQLite docs, "The PRIMARY KEY"](https://www.sqlite.org/lang_createtable.html#the_primary_key)):

> According to the SQL standard, PRIMARY KEY should always imply NOT NULL. Unfortunately, due to a bug in some early versions, this is not the case in SQLite. […] NULL values are considered distinct from all other values, including other NULLs.

So with the generated DDL above, this succeeds silently:

```sql
INSERT INTO users (id) VALUES (NULL); -- accepted!
INSERT INTO users (id) VALUES (NULL); -- accepted again — NULLs are pairwise distinct
```

A table with a TEXT (e.g. UUID) primary key can now accumulate multiple NULL "primary keys". That is a silent data-integrity regression for every SQLite/Turso/D1 project using non-integer PKs.

**What are the steps to reproduce it?**

1. `pnpm add drizzle-orm@1.0.0-rc.4 drizzle-kit@1.0.0-rc.4`
2. Schema as above (`text().primaryKey()`), `dialect: "sqlite"` (also reproduces with `"turso"`)
3. `npx drizzle-kit generate` → migration SQL contains `` `id` text PRIMARY KEY `` with no `NOT NULL`
4. Apply it, then `INSERT INTO users (id) VALUES (NULL);` → row is inserted

**What is the desired result?**

- Keep emitting `NOT NULL` for SQLite primary-key columns that are not `INTEGER PRIMARY KEY` (rowid alias) — i.e. restore the 0.31 behavior for TEXT/BLOB/REAL PKs
- At the very least, an explicit `.notNull()` in the schema should never be discarded

Related: #2611 asked to drop the *redundant* `NOT NULL` for `integer().primaryKey()` (where SQLite really does enforce non-NULL via the rowid alias). The v1 behavior looks like that request applied to *all* SQLite PK columns, which overshoots — for non-INTEGER PKs the `NOT NULL` was load-bearing.

**Workaround**: hand-edit each generated migration to add `NOT NULL` back to text PK columns (the snapshot normalizes both sides the same way, so the manual patch doesn't cause spurious diffs on subsequent generates).

**Environment**: Turso (SQLite) with TEXT UUID primary keys; Node.js 22; pnpm monorepo.

Contributor guide

Open the contributing guide

Research direction

Reproduce the issue with the provided text primary-key schema and run drizzle-kit generate for SQLite. Inspect the v1 SQLite serializer at the shown notNull expression, then verify that generated SQL retains NOT NULL for non-INTEGER primary keys and explicit .notNull() declarations without changing INTEGER PRIMARY KEY behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, sqlite, typescript
Domain
databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.