Relation filters and `_count` on a delegate base model give wrong results when the related model is in the same hierarchy
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 52/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- sql, typescript
- Ambito
- backend-api-design, databases
Direzione di ricerca
Start by reproducing the reported db.item relation-filter and _count cases, then trace SQL generation for delegate base-model relation subqueries; the issue does not name source files or tests. Compare the generated SQL with the working db.task query and check how the subquery's join to the base table affects the outer correlation. Done means the reported filters and count return the expected rows on SQLite and PostgreSQL.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Version
@zenstackhq/orm on dev at b51d33c4 (after 3.9.7). Same result on SQLite and PostgreSQL 17.
Summary
A relation filter or _count queried through a delegate base model gives a wrong result when the related model is a sub model of the same hierarchy. The same filter queried through the sub model is correct.
Reproduction
model Item {
id String @id @default(cuid())
itemKind String
notesAsSource Note[] @relation("NoteSource")
@@delegate(itemKind)
}
model Task extends Item {}
model Note extends Item {
sourceId String?
source Item? @relation("NoteSource", fields: [sourceId], references: [id])
}
const task = await db.task.create({ data: {} });
await db.note.create({ data: { sourceId: task.id } });
await db.item.findMany({ where: { notesAsSource: { some: {} } } }); // [] (expected: the task)
await db.item.count({ where: { notesAsSource: { some: {} } } }); // 0 (expected: 1)
await db.item.findMany({ where: { notesAsSource: { none: {} } } }); // both rows (expected: the note only)
await db.item.findMany({ include: { _count: { select: { notesAsSource: true } } } }); // task: 0 (expected: 1)
await db.task.findMany({ where: { notesAsSource: { some: {} } } }); // the task (correct)
A plain self relation without @@delegate works correctly.
Cause
This is the generated SQL for db.item.findMany({ where: { notesAsSource: { some: {} } } }) (SQLite):
select "Item"."id", ... from "Item"
left join "Task" on "Item"."id" = "Task"."id"
left join "Note" on "Item"."id" = "Note"."id"
where exists (
select 1 from "Note" as "$$t1"
left join "Item" on "$$t1"."id" = "Item"."id"
where "Item"."id" = "$$t1"."sourceId"
)
The subquery joins Note's base table as "Item" without an alias. The correlation "Item"."id" = "$$t1"."sourceId" then reads the inner "Item" (the note's own base row) instead of the outer row. So it compares the note's id with its own sourceId, which never matches.
Through db.task, the outer table is "Task", so there is no name clash and the result is correct.
This looks like the same shadowing problem that 3.9.5 fixed for access policy rules that traverse a self relation, here in the join that the delegate sub model's base table adds to a relation subquery. A possible fix is to give that join a unique alias, as the subquery already does for "$$t1".
Environment
- Node.js 24.18.0
- pnpm 10.33.0
- Lingua principale
- TypeScript
- Stelle
- 2.9k
- Fork
- 157
- Merge medio
- 11h 42m
- PR unite (30g)
- 20
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di zenstackhq/zenstack
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
zenstackhq/zenstack#2873 ·
I maintainer di solito rispondono entro 1 giorno
-
runtime
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
zenstackhq/zenstack#2868 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
zenstackhq/zenstack#2694 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 68/100
zenstackhq/zenstack#2659 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
zenstackhq/zenstack#2542 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di zenstackhq/zenstack
Issue simili
-
First unknown-user login after boot is one scrypt run slower than a real user's wrong passwordApertaarea: backend bug priority: low
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
snapotter-hq/SnapOtter#2254 ·
I maintainer di solito rispondono entro 1 giorno
-
bug ticket
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
cratestack/cratestack#1154 ·
I maintainer di solito rispondono entro 1 giorno
-
server 消息处理器 cmd 分支补显式错误回报——竞态非法命令现走未处理拒绝Forse già presa @openaddr l’ha presa oggi. Apertaready-for-agent refactor wayfinder:task
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
openaddr/dafung-web#428 ·
I maintainer di solito rispondono entro 1 giorno
-
Flaky: mongodb-memory-server 'Port already in use' when another process starts a mongod concurrentlyApertaarea:testing bug effort:S priority:P2
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
I maintainer di solito rispondono entro 1 giorno
-
lens:agent lens:process process
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
thebristolsound/birdbrain#1772 ·
I maintainer di solito rispondono entro 1 giorno