Skip to content

fix(sql-orm-client): alias child table for self-referential 1:N relation predicates - #1010

Open
prisma-gremlin[bot] wants to merge 1 commit into
mainfrom
fix/980-self-referential-1n-predicate-alias
Open

fix(sql-orm-client): alias child table for self-referential 1:N relation predicates#1010
prisma-gremlin[bot] wants to merge 1 commit into
mainfrom
fix/980-self-referential-1n-predicate-alias

Conversation

@prisma-gremlin

@prisma-gremlin prisma-gremlin Bot commented Jul 20, 2026

Copy link
Copy Markdown

Fixes #980

Root cause

A relation predicate (some/every/none) on a self-referential one-to-many
relation emitted a correlated EXISTS subquery whose child FROM referenced the
unaliased parent table. For User.invitedUsers (a 1:N self-relation via
invited_by_id, both sides resolving to users), the generated SQL was:

SELECT * FROM users WHERE EXISTS (
  SELECT 1 FROM users WHERE users.invited_by_id = users.id AND users.name = $1
)

In SQL the inner FROM users shadows the outer users correlation, so
users.invited_by_id = users.id compared the inner row against itself (matching
only rows that invited themselves) instead of correlating the child's
invited_by_id to the outer parent's id. The predicate therefore silently
matched nothing — invitedUsers.some({ name: 'Bob' }) returned [] instead of
[Alice].

Only the 1:N relation-predicate path in buildExistsExpr
(packages/3-extensions/sql-orm-client/src/model-accessor.ts) was affected. The
sibling M:N predicate path (buildManyToManyExistsExpr) and the include path
(resolveChildTableSource in query-plan-select.ts) already alias the child
table to ${relationName}__child for self-relations; the 1:N predicate path was
the only one missing this aliasing. include() on the same self-relation worked
because it goes through the already-aliased include path, which is why the bug
was specific to relation predicates on self-referential relations.

Fix

Make the 1:N buildExistsExpr mirror the M:N path: when the parent and child
resolve to the same underlying table (same namespace + same table name), alias
the child FROM to ${relationName}__child, route the join correlation's
target column and the EXISTS projection through that alias, and remap the
predicate's column refs onto the alias via the existing remapColumnRefs helper.
Non-self-relations are unchanged — the alias is undefined and
remapColumnRefs short-circuits when the table name equals the ref.

buildJoinWhere's parameter was renamed from relatedTableName to
relatedTableRef so the correlation's child side follows the alias; it has a
single caller.

Tests

  • New test/integration/test/sql-orm-client/ported-regressions.test.ts
    (entry 107) reproduces the issue against PGlite: some/none/every on
    User.invitedUsers with the issue's exact seed. some returned [] before
    the fix; all three return the correct sets after.
  • New unit tests in packages/3-extensions/sql-orm-client/test/model-accessor.test.ts
    assert the self-referential 1:N predicate emits the invitedUsers__child
    alias on from/projection/where, with the parent kept on users and the
    predicate remapped onto the alias. These fail without the fix and pass with it.
  • Updated the existing subtasks polymorphic self-relation unit test, which had
    been codifying the buggy tasks.parent_id = tasks.id AST, to expect the
    subtasks__child alias. Its intent (base relations resolve against the base
    table even when a variant is selected) is preserved — the parent side remains
    tasks.

Verification

  • pnpm test (sql-orm-client): 61 files / 665 tests pass, no type errors.
  • pnpm test (integration, full test/sql-orm-client/): 33 files / 251 tests
    pass — no regressions across self-relations, M:N filters, includes,
    polymorphism, nested includes.
  • pnpm typecheck (sql-orm-client and integration-tests): clean.
  • pnpm lint (both packages): clean (only pre-existing no-bare-cast infos in
    untouched files; no new bare casts — as only in test files, which are
    exempt).
  • pnpm lint:deps: no dependency violations.
  • Confirmed the new tests fail without the fix (integration: 3 failed with
    some returning []; unit: 2 failed with alias: undefined) and pass with
    it.

Created by Mohawk

Summary by CodeRabbit

  • Bug Fixes
    • Fixed self-referential one-to-many relation filtering so correlated some/none/every predicates generate correct SQL and return accurate results.
    • Improved join predicate handling for self-referential relations, including cases with selected table variants.
  • Tests
    • Added unit tests covering aliasing and correlated predicate SQL generation for self-referential invitedUsers and subtasks.
    • Added integration regression tests validating invitee predicate behavior end-to-end.
  • Documentation
    • Updated upgrade notes to clarify the self-referential relation SQL fix.

@prisma-gremlin
prisma-gremlin Bot requested a review from a team as a code owner July 20, 2026 12:33
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 7a087204-b7ca-4a2f-aaba-8758ab8cc5d3

📥 Commits

Reviewing files that changed from the base of the PR and between a90267c and 48309de.

📒 Files selected for processing (4)
  • packages/3-extensions/sql-orm-client/src/model-accessor.ts
  • packages/3-extensions/sql-orm-client/test/model-accessor.test.ts
  • skills/extension-author/prisma-next-extension-upgrade/upgrades/0.15-to-0.16/instructions.md
  • test/integration/test/sql-orm-client/ported-regressions.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/integration/test/sql-orm-client/ported-regressions.test.ts
  • packages/3-extensions/sql-orm-client/test/model-accessor.test.ts

📝 Walkthrough

Walkthrough

Self-referential one-to-many relation predicates now alias the child table in correlated EXISTS subqueries. Unit tests verify generated SQL, and integration tests cover some, none, and every behavior with seeded users.

Changes

Self-referential relation predicates

Layer / File(s) Summary
Alias self-referential EXISTS subqueries
packages/3-extensions/sql-orm-client/src/model-accessor.ts, packages/3-extensions/sql-orm-client/test/model-accessor.test.ts
Self-referential child tables use relation-specific aliases, child predicates are remapped, join predicates use the aliased reference, and generated SQL expectations are updated.
Validate relation predicate results
test/integration/test/sql-orm-client/ported-regressions.test.ts, skills/extension-author/.../instructions.md
Seeded users are used to verify some, none, and every predicates, and the upgrade instructions document the internal fix and its unchanged public surface.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RelationPredicate
  participant buildExistsExpr
  participant ChildExistsSubquery
  RelationPredicate->>buildExistsExpr: Build invitedUsers relation predicate
  buildExistsExpr->>ChildExistsSubquery: Generate aliased correlated EXISTS subquery
  ChildExistsSubquery-->>RelationPredicate: Return filtered user rows
Loading

Suggested reviewers: tensordreams

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: aliasing child tables for self-referential relation predicates.
Linked Issues check ✅ Passed The code and tests address the self-referential some/every/none aliasing bug described in #980 and keep the fix scoped to relation predicates.
Out of Scope Changes check ✅ Passed The added docs and tests support the same bug fix, and no unrelated code changes are indicated.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/980-self-referential-1n-predicate-alias

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 158.96 KB (+0.01% 🔺)
postgres / emit 132.48 KB (+0.02% 🔺)
mongo / no-emit 98.7 KB (0%)
mongo / emit 89.43 KB (0%)
cf-worker / no-emit 185.15 KB (0%)
cf-worker / emit 155.91 KB (0%)

@pkg-pr-new

pkg-pr-new Bot commented Jul 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

npm i https://pkg.pr.new/@prisma-next/extension-author-tools@1010

@prisma-next/mongo-runtime

npm i https://pkg.pr.new/@prisma-next/mongo-runtime@1010

@prisma-next/family-mongo

npm i https://pkg.pr.new/@prisma-next/family-mongo@1010

@prisma-next/sql-runtime

npm i https://pkg.pr.new/@prisma-next/sql-runtime@1010

@prisma-next/family-sql

npm i https://pkg.pr.new/@prisma-next/family-sql@1010

@prisma-next/extension-arktype-json

npm i https://pkg.pr.new/@prisma-next/extension-arktype-json@1010

@prisma-next/middleware-cache

npm i https://pkg.pr.new/@prisma-next/middleware-cache@1010

@prisma-next/mongo

npm i https://pkg.pr.new/@prisma-next/mongo@1010

@prisma-next/extension-paradedb

npm i https://pkg.pr.new/@prisma-next/extension-paradedb@1010

@prisma-next/extension-pgvector

npm i https://pkg.pr.new/@prisma-next/extension-pgvector@1010

@prisma-next/extension-postgis

npm i https://pkg.pr.new/@prisma-next/extension-postgis@1010

@prisma-next/postgres

npm i https://pkg.pr.new/@prisma-next/postgres@1010

@prisma-next/sql-orm-client

npm i https://pkg.pr.new/@prisma-next/sql-orm-client@1010

@prisma-next/sqlite

npm i https://pkg.pr.new/@prisma-next/sqlite@1010

@prisma-next/extension-supabase

npm i https://pkg.pr.new/@prisma-next/extension-supabase@1010

@prisma-next/target-mongo

npm i https://pkg.pr.new/@prisma-next/target-mongo@1010

@prisma-next/adapter-mongo

npm i https://pkg.pr.new/@prisma-next/adapter-mongo@1010

@prisma-next/driver-mongo

npm i https://pkg.pr.new/@prisma-next/driver-mongo@1010

@prisma-next/contract

npm i https://pkg.pr.new/@prisma-next/contract@1010

@prisma-next/utils

npm i https://pkg.pr.new/@prisma-next/utils@1010

@prisma-next/config

npm i https://pkg.pr.new/@prisma-next/config@1010

@prisma-next/errors

npm i https://pkg.pr.new/@prisma-next/errors@1010

@prisma-next/framework-components

npm i https://pkg.pr.new/@prisma-next/framework-components@1010

@prisma-next/operations

npm i https://pkg.pr.new/@prisma-next/operations@1010

@prisma-next/ts-render

npm i https://pkg.pr.new/@prisma-next/ts-render@1010

@prisma-next/contract-authoring

npm i https://pkg.pr.new/@prisma-next/contract-authoring@1010

@prisma-next/ids

npm i https://pkg.pr.new/@prisma-next/ids@1010

@prisma-next/psl-parser

npm i https://pkg.pr.new/@prisma-next/psl-parser@1010

@prisma-next/psl-printer

npm i https://pkg.pr.new/@prisma-next/psl-printer@1010

@prisma-next/cli

npm i https://pkg.pr.new/@prisma-next/cli@1010

@prisma-next/cli-telemetry

npm i https://pkg.pr.new/@prisma-next/cli-telemetry@1010

@prisma-next/config-loader

npm i https://pkg.pr.new/@prisma-next/config-loader@1010

@prisma-next/emitter

npm i https://pkg.pr.new/@prisma-next/emitter@1010

@prisma-next/language-server

npm i https://pkg.pr.new/@prisma-next/language-server@1010

@prisma-next/migration-tools

npm i https://pkg.pr.new/@prisma-next/migration-tools@1010

prisma-next

npm i https://pkg.pr.new/prisma-next@1010

@prisma-next/vite-plugin-contract-emit

npm i https://pkg.pr.new/@prisma-next/vite-plugin-contract-emit@1010

@prisma-next/mongo-codec

npm i https://pkg.pr.new/@prisma-next/mongo-codec@1010

@prisma-next/mongo-contract

npm i https://pkg.pr.new/@prisma-next/mongo-contract@1010

@prisma-next/mongo-value

npm i https://pkg.pr.new/@prisma-next/mongo-value@1010

@prisma-next/mongo-contract-psl

npm i https://pkg.pr.new/@prisma-next/mongo-contract-psl@1010

@prisma-next/mongo-contract-ts

npm i https://pkg.pr.new/@prisma-next/mongo-contract-ts@1010

@prisma-next/mongo-emitter

npm i https://pkg.pr.new/@prisma-next/mongo-emitter@1010

@prisma-next/mongo-schema-ir

npm i https://pkg.pr.new/@prisma-next/mongo-schema-ir@1010

@prisma-next/mongo-query-ast

npm i https://pkg.pr.new/@prisma-next/mongo-query-ast@1010

@prisma-next/mongo-orm

npm i https://pkg.pr.new/@prisma-next/mongo-orm@1010

@prisma-next/mongo-query-builder

npm i https://pkg.pr.new/@prisma-next/mongo-query-builder@1010

@prisma-next/mongo-lowering

npm i https://pkg.pr.new/@prisma-next/mongo-lowering@1010

@prisma-next/mongo-wire

npm i https://pkg.pr.new/@prisma-next/mongo-wire@1010

@prisma-next/sql-contract

npm i https://pkg.pr.new/@prisma-next/sql-contract@1010

@prisma-next/sql-errors

npm i https://pkg.pr.new/@prisma-next/sql-errors@1010

@prisma-next/sql-operations

npm i https://pkg.pr.new/@prisma-next/sql-operations@1010

@prisma-next/sql-schema-ir

npm i https://pkg.pr.new/@prisma-next/sql-schema-ir@1010

@prisma-next/sql-contract-psl

npm i https://pkg.pr.new/@prisma-next/sql-contract-psl@1010

@prisma-next/sql-contract-ts

npm i https://pkg.pr.new/@prisma-next/sql-contract-ts@1010

@prisma-next/sql-contract-emitter

npm i https://pkg.pr.new/@prisma-next/sql-contract-emitter@1010

@prisma-next/sql-lane-query-builder

npm i https://pkg.pr.new/@prisma-next/sql-lane-query-builder@1010

@prisma-next/sql-relational-core

npm i https://pkg.pr.new/@prisma-next/sql-relational-core@1010

@prisma-next/sql-builder

npm i https://pkg.pr.new/@prisma-next/sql-builder@1010

@prisma-next/target-postgres

npm i https://pkg.pr.new/@prisma-next/target-postgres@1010

@prisma-next/target-sqlite

npm i https://pkg.pr.new/@prisma-next/target-sqlite@1010

@prisma-next/adapter-postgres

npm i https://pkg.pr.new/@prisma-next/adapter-postgres@1010

@prisma-next/adapter-sqlite

npm i https://pkg.pr.new/@prisma-next/adapter-sqlite@1010

@prisma-next/driver-postgres

npm i https://pkg.pr.new/@prisma-next/driver-postgres@1010

@prisma-next/driver-sqlite

npm i https://pkg.pr.new/@prisma-next/driver-sqlite@1010

commit: 48309de

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/3-extensions/sql-orm-client/src/model-accessor.ts`:
- Around line 370-393: Limit the alias remapping in the current EXISTS
construction around buildJoinWhere and remapColumnRefs so it only rewrites
ColumnRefs belonging to this relation-filter level. Update rewrite() to avoid
descending into nested subqueries, preserving their internal aliases and
correlations while still remapping direct references from relatedTableName to
relatedTableRef.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e28fa923-2e17-48db-82a7-3760330c2d14

📥 Commits

Reviewing files that changed from the base of the PR and between 310f70b and a90267c.

📒 Files selected for processing (3)
  • packages/3-extensions/sql-orm-client/src/model-accessor.ts
  • packages/3-extensions/sql-orm-client/test/model-accessor.test.ts
  • test/integration/test/sql-orm-client/ported-regressions.test.ts

Comment thread packages/3-extensions/sql-orm-client/src/model-accessor.ts
…ion predicates

A relation predicate (some/every/none) on a self-referential one-to-many
relation emitted a correlated EXISTS subquery whose child FROM referenced the
unaliased parent table, so the inner FROM shadowed the outer correlation and
the predicate silently matched nothing (e.g. invitedUsers.some({ name: 'Bob' })
returned [] instead of [Alice]).

Mirror the M:N predicate and include paths: when parent and child resolve to
the same table, alias the child FROM to ${relationName}__child, route the join
correlation's target column and the EXISTS projection through the alias, and
remap the predicate's column refs onto it. Non-self-relations are unchanged.

Fixes #980

Signed-off-by: Gremlin <gremlin@users.noreply.github.com>
@prisma-gremlin
prisma-gremlin Bot force-pushed the fix/980-self-referential-1n-predicate-alias branch from a90267c to 48309de Compare July 20, 2026 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sql-orm-client: relation predicate (some/every/none) returns wrong results on self-referential relations

0 participants