drizzle-team / drizzle-team/drizzle-orm

[BUG]: SQLite pull reads a partial index's WHERE off the table's DDL, losing real predicates and inventing false ones

Open
#6,223 1 comment 0 reactions 0 assignees View on GitHub
bug bug/fixed-in-beta drizzle/kit
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

better-sqlite3@13.0.3, node:sqlite (Node 24.17.0)

### Describe the Bug

On SQLite, `pull` never keeps a partial index's `WHERE`, and on some tables it reports one that isn't there. The index query in `fromDatabase` selects `m.sql` under `WHERE m.type = 'table'`, so `m.sql` is the table's `CREATE TABLE`. The predicate is then taken off that with `it.sql.toLowerCase().indexOf(" where ")` and `it.sql.slice(whereIdx + 7)`, not off the index's own DDL.

Easiest to see on an index that isn't partial at all:

```sql
CREATE TABLE t (id INTEGER PRIMARY KEY, code TEXT NOT NULL, note TEXT DEFAULT 'ask where it came from');
CREATE INDEX i ON t (code);
```

`drizzle-kit pull` writes into `snapshot.json`:

```json
{ "columns": [{ "value": "code", "isExpression": false }], "isUnique": false, "where": "it came from')", "origin": "manual", "name": "i", "entityType": "indexes", "table": "t" }
```

`it came from')` is the tail of the column default. Same under `node:sqlite` and `better-sqlite3`, so it isn't the driver.

A `CREATE TABLE` rarely contains " where ", so the usual outcome is the other one, where the predicate is just gone. That changes what the schema allows when the index is unique.

```sql
CREATE TABLE t (id INTEGER PRIMARY KEY, code TEXT NOT NULL, status TEXT);
CREATE UNIQUE INDEX i ON t (code) WHERE status = 'active';
```

pulls as `uniqueIndex("i").on(table.code)`, and the baseline migration emits the same index with no `WHERE`. Two rows with `code = 'ABC'` and `status = 'archived'` then fail against it with `UNIQUE constraint failed: t.code`. The original takes them, since the constraint only covers active rows.

Nothing downstream is missing the field: `IndexConfig` has `where`, `.where()` is in the DSL, and the snapshot entry above has a `where` key. It is only being filled from the wrong string.

The index's own DDL isn't in the result set, so the query needs it. `pragma_index_list` carries a `partial` flag and `sqlite_master` has the `CREATE INDEX` under `type = 'index'`. #5078 does exactly that, but against `serializer/sqliteSerializer.ts`, which the rc line replaced with `dialects/sqlite/introspect.ts`, so it doesn't reach this.

The same `index.sql` turns up in the error beside it: `CREATE UNIQUE INDEX i ON t (lower(code), id)` fails with `unexpected unique index 'i' with expression value: CREATE TABLE t (id INTEGER PRIMARY KEY, code TEXT NOT NULL)`, which is the table again.

Same on 1.0.0-rc.5-ab785fc. On 0.31.10 the predicate is lost too, but there is no `where` key on the index entry at all, so nothing is invented, which is #4688.

Contributor guide

Open the contributing guide

Research direction

Start in dialects/sqlite/introspect.ts and inspect the index query and how its SQL is parsed; compare the approach with #5078 and the sqlite_master entries for indexes. Verify the fix with the partial-index and ordinary-index examples in the issue: pull must preserve a real WHERE predicate and must not invent one from the table DDL.

Written by the indexing model from the issue text.

Assessment

Tech stack
sqlite, typescript
Domain
databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.