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
- 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
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