Skip to content

TML-2785: M:N slice 1 — correlated include read through the junction#679

Open
tensordreams wants to merge 17 commits into
mainfrom
tml-2785-slice-1-correlated-read
Open

TML-2785: M:N slice 1 — correlated include read through the junction#679
tensordreams wants to merge 17 commits into
mainfrom
tml-2785-slice-1-correlated-read

Conversation

@tensordreams

@tensordreams tensordreams commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Slice 1 of the SQL ORM: Many-to-Many End to End project (Linear project). Reads an M:N relation through its junction.

Stacked PR. Base is tml-2784 (#678, slice 0) → tml-2597 (#673) → tml-2729 (#667) → main. Review/merge bottom-up.

Overview

db.orm.User.include(tags) now resolves a many-to-many relation to { …user, tags: Tag[] } in a single correlated subquery that walks parent → junction → target — no LATERAL, no second query. Built on slice 0s ResolvedRelation.through.

Changes (4 commits)

  • fcecac5b3 — integration fixture gains a User ↔ Tag M:N relation via a UserTag junction (composite PK user_id/tag_id); contract.json/.d.ts re-emitted.
  • e587b433c — read path: IncludeExpr.through (surfaced by resolveIncludeRelation), and buildCorrelatedIncludeProjection gains an M:N branch — buildManyToManyJunctionArtifacts builds a non-LATERAL inner join to the junction (junction.childColumns = target.targetColumns) correlated to the parent (junction.parentColumns = parent anchor), composite-key AND-ed; FK decode path reused. Unit-tested at the AST level.
  • b9c3e9f7b — replace 2 bare as casts with castAs; add the missing M:N + distinct + non-leaf unit test.
  • d3232cbad — 7 integration tests (PGlite).

Integration tests (per the project standard)

Whole-row toEqual; 6/7 use explicit .select(...) (so adding a model field wont churn assertions); test 5 uses implicit/default selection (full User + tags: Tag[] shape); a single-execution / no-LATERAL assertion; depth-2 nesting (invitedUsers → tags); edges (user with no tags → tags: []; a tag shared by multiple users).

Why

This is the first of the three relation-shaped M:N consumers (read / filter / write) over slice 0s shared through primitive. The correlated-only approach matches the post-TML-2729 read path (no LATERAL to reintroduce).

Scope / notes

Read only — filter (TML-2786) and write (TML-2787) are later slices. The fixture is one-directional (User.tags; reverse Tag.users deferred — adding it trips a latent create-overload type fragility in unrelated mutation-defaults tests; see the projects unattended-decisions log). Fixture re-emit used a tsx bypass because the CLI contract emit fails on a sandbox config-load env issue — CI fixtures:check is the real golden-stability gate; please confirm its green (or re-run the canonical emit). Broad integration runs show pre-existing PGlite/WASM JIT flakiness; the M:N tests pass on targeted runs.

Refs: TML-2785.

Summary by CodeRabbit

  • New Features

    • Added many-to-many relationship support through correlated junction-table includes, enabling efficient queries of related records via intermediate tables with composite-key relationship support.
  • Tests

    • Added comprehensive integration tests validating many-to-many include queries with nested and sibling relationship combinations.

@tensordreams tensordreams requested a review from a team as a code owner June 1, 2026 19:16
@coderabbitai

coderabbitai Bot commented Jun 1, 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: 401ebb85-009e-4495-8f8e-57055748a457

📥 Commits

Reviewing files that changed from the base of the PR and between f4f4d0b and 905f19c.

📒 Files selected for processing (6)
  • packages/3-extensions/sql-orm-client/src/collection-contract.ts
  • packages/3-extensions/sql-orm-client/src/collection.ts
  • packages/3-extensions/sql-orm-client/src/query-plan-select.ts
  • packages/3-extensions/sql-orm-client/test/query-plan-select.test.ts
  • skills/extension-author/prisma-next-extension-upgrade/upgrades/0.12-to-0.13/instructions.md
  • test/integration/test/sql-orm-client/mn-include.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • skills/extension-author/prisma-next-extension-upgrade/upgrades/0.12-to-0.13/instructions.md
  • packages/3-extensions/sql-orm-client/src/collection-contract.ts
  • packages/3-extensions/sql-orm-client/src/collection.ts
  • test/integration/test/sql-orm-client/mn-include.test.ts

📝 Walkthrough

Walkthrough

This pull request implements end-to-end many-to-many include support in sql-orm-client. It extends type contracts to define junction-table metadata, resolves and propagates this metadata through query construction, generates correlated subqueries with junction joins for M:N relations, and validates the implementation with comprehensive unit and integration tests.

Changes

Many-to-many include support

Layer / File(s) Summary
Type contracts for through-table metadata
packages/3-extensions/sql-orm-client/src/types.ts
Introduces IncludeThroughDescriptor interface capturing junction table name, namespace, and parent/child/target column mappings; extends IncludeExpr with optional through field.
Contract resolution for M:N relations
packages/3-extensions/sql-orm-client/src/collection-contract.ts
Extended ResolvedIncludeRelation with optional through descriptor; resolveIncludeRelation detects relation.through and computes junction metadata including parentLocalColumns mapping.
Include expression propagation
packages/3-extensions/sql-orm-client/src/collection.ts
Collection.include() now conditionally includes resolved through descriptor in the generated includeExpr object via ifDefined.
Dispatch join-column selection
packages/3-extensions/sql-orm-client/src/collection-dispatch.ts
Updated dispatchWithIncludes to derive parentJoinColumns from through.parentLocalColumns for M:N relations, otherwise from direct localColumn.
Query planning for M:N includes and junction logic
packages/3-extensions/sql-orm-client/src/query-plan-select.ts
New buildManyToManyJunctionArtifacts helper constructs composite-key correlation WHERE expressions and junction joins; buildIncludeChildRowsSelect branches on through presence; distinct non-leaf path threads junction joins into inner deduplication query via merged join lists.
Unit test infrastructure and M:N query plan tests
packages/3-extensions/sql-orm-client/test/query-plan-select.test.ts
New test suite validates correlated subquery SQL-plan structure for single/composite-key M:N junctions, FK non-regression, and distinct+nested-include edge cases.
Integration test schema setup for M:N
test/integration/test/sql-orm-client/runtime-helpers.ts
Adds user_tags junction table with composite PK on (user_id, tag_id), tags table, and seeding helpers seedTags/seedUserTags.
End-to-end M:N include integration tests
test/integration/test/sql-orm-client/mn-include.test.ts
Comprehensive integration test suite validating M:N include correctness: whole-row selection, single-execution guarantee, empty arrays for unassociated users, shared tags across users, depth-2 nesting under 1:N includes, and sibling M:N + 1:N composition.
Release notes documentation
skills/extension-author/prisma-next-extension-upgrade/upgrades/0.12-to-0.13/instructions.md
Documents internal N:M runtime support via correlated includes; explicitly notes no extension API or contract changes beyond prior M:N descriptor work.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • prisma/prisma-next#678: Introduces the foundational IncludeThroughDescriptor contract and relation.through resolver that this PR consumes and extends into query planning.
  • prisma/prisma-next#555: Refactors the buildIncludeChildRowsSelect and related include pipeline that this PR extends to add M:N junction lowering logic.
  • prisma/prisma-next#619: Refactors dispatchWithIncludes in collection-dispatch.ts; this PR extends that same function to derive join columns from through.parentLocalColumns.

Suggested reviewers

  • aqrln

Poem

🐰 Through tables bloom in junction space,
Correlations form with grace,
M:N includes now take flight,
Subqueries weave left and right,
Tags and users join as one,
Query planning work is done! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% 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 describes the main change: implementing M:N (many-to-many) includes via correlated subquery reads through junction tables, which aligns with all the substantial changes across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 tml-2785-slice-1-correlated-read
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch tml-2785-slice-1-correlated-read

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 150.9 KB (+0.17% 🔺)
postgres / emit 119.7 KB (+0.22% 🔺)
mongo / no-emit 76.67 KB (0%)
mongo / emit 70.96 KB (0%)
cf-worker / no-emit 179.98 KB (0%)
cf-worker / emit 145.5 KB (0%)

@pkg-pr-new

pkg-pr-new Bot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

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

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-arktype-json

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

@prisma-next/middleware-cache

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

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/extension-postgis

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/extension-supabase

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

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/ts-render

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/cli-telemetry

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

@prisma-next/emitter

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

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-query-builder

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

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: f4f4d0b

@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch from 4ee6af6 to 78affcf Compare June 2, 2026 12:39
@tensordreams tensordreams force-pushed the tml-2784-slice-0-contract-resolver-foundation branch from b3bba1c to e44f8aa Compare June 2, 2026 13:07
@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch from 78affcf to e4e3004 Compare June 2, 2026 13:07
@tensordreams tensordreams force-pushed the tml-2784-slice-0-contract-resolver-foundation branch from e44f8aa to aaefe75 Compare June 3, 2026 08:51
@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch 2 times, most recently from 9e24a8b to ed06084 Compare June 3, 2026 11:32
@tensordreams tensordreams force-pushed the tml-2784-slice-0-contract-resolver-foundation branch 2 times, most recently from 87de795 to 4763690 Compare June 4, 2026 15:12
@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch 2 times, most recently from 38bcb74 to c32cd78 Compare June 4, 2026 15:41
@tensordreams tensordreams force-pushed the tml-2784-slice-0-contract-resolver-foundation branch from d15ad0b to 5450121 Compare June 5, 2026 12:44
@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch 4 times, most recently from 7e21c60 to f383943 Compare June 8, 2026 10:19
@tensordreams tensordreams force-pushed the tml-2784-slice-0-contract-resolver-foundation branch 2 times, most recently from 67f9537 to 2aaf8c0 Compare June 8, 2026 14:46
@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch from a36b0d5 to 07aa1cf Compare June 8, 2026 14:46
@tensordreams tensordreams force-pushed the tml-2784-slice-0-contract-resolver-foundation branch from 2aaf8c0 to cf13cac Compare June 8, 2026 16:45
@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch from 07aa1cf to 12ec127 Compare June 8, 2026 16:45
Base automatically changed from tml-2784-slice-0-contract-resolver-foundation to main June 8, 2026 17:58
…ndard (TML-2785)

Read slice (correlated include through junction). Bakes the operator
integration-test standard (whole-row asserts, explicit select, +implicit
nested-M:N case) into the project cross-cutting requirements and slice 1.
3 dispatches: fixture M:N / read code / integration tests.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Add many-to-many include support via a single correlated subquery that
joins the child table to the junction on junction.childColumns = child.targetColumns
and correlates to the parent via WHERE junction.parentColumns = parent.parentLocalColumns.
Composite keys AND across all column pairs. No LATERAL joins.

- IncludeExpr gains `through?: IncludeThroughDescriptor` carrying junction table
  name, parentColumns, childColumns, targetColumns, and parentLocalColumns.
- `resolveIncludeRelation` in collection-contract.ts surfaces `through` from the
  contract relation when present, resolving field names to column names for the
  parent local columns.
- `Collection.include()` propagates `through` into IncludeExpr via spread.
- `buildManyToManyJunctionArtifacts` in query-plan-select.ts builds the JOIN ON
  expression (BinaryExpr or AndExpr over child column pairs) and the correlated
  WHERE (BinaryExpr or AndExpr over parent column pairs), producing a non-lateral
  inner JoinAst to the junction table.
- `buildIncludeChildRowsSelect` detects `include.through` and uses the M:N
  artifacts instead of the FK equality WHERE; `buildDistinctNonLeafChildRowsSelect`
  receives and applies the same junction joins.
- `dispatchWithIncludes` in collection-dispatch.ts forces all `through.parentLocalColumns`
  (not just `localColumn`) into the parent SELECT augmentation for composite M:N keys.
- `buildManyToManyContract` test helper and M:N unit tests covering single-column
  and composite-key junction shapes, plus a FK path non-regression test.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…ManyJunctionArtifacts; add M:N+distinct+non-leaf test

F1: the two bare `as AnyExpression` casts in `buildManyToManyJunctionArtifacts` are replaced with
`castAs<AnyExpression>(…!)` — BinaryExpr is a union member of AnyExpression, so the assertion is
type-checked; the non-null assertion is safe because the branch is only taken when `length === 1`.
Adds the `castAs` import from `@prisma-next/utils/casts`. `lint:casts` delta: -1.

F2: new unit test `attaches junction join to baseInner in M:N + distinct + nested non-leaf path`
in the `M:N include correlated subquery` describe block. Constructs a contract with
parents→children (M:N via parent_child junction) + children→grandchildren (FK), sets
`distinct: ['name']` and a nested grandchild include on the M:N IncludeExpr, calls
`compileSelectWithIncludes`, and asserts:
  - junction join (`INNER JOIN parent_child`, `lateral: false`) attaches to `baseInner`
    (the innermost scalar SELECT inside the ROW_NUMBER wrap), not to the dedup wrapper or
    outer distinct SELECT
  - correlated WHERE (`parent_child.parent_id = parents.id`) is present at `baseInner`
  - no junction join leaks to `innerSelect` or the outer `childRows`

All 493 tests pass; typecheck clean.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Adds `mn-include.test.ts` covering the end-to-end M:N include path for
`User.tags` via the `user_tags` junction, using the PGlite harness.

Tests satisfy the operator integration-test standard:
- Whole-row toEqual assertions on every test
- Explicit .select() on 6/7 tests
- One implicit/default-selection test (full User + tags: Tag[] shape)
- Single-execution assertion + no LATERAL in emitted SQL
- Depth-2: M:N tags nested under invitedUsers (1:N self-relation)
- Sibling depth-2: include("tags") alongside include("posts") in one execution
- Edge: user with no tags returns tags: []
- Edge: tag shared by multiple users resolves independently for each

Also extends `setupTestSchema` and adds `seedTags`/`seedUserTags` helpers
to `runtime-helpers.ts` to support the junction table in integration tests.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
… (TML-2785)

Orchestrator artifacts for the read slice (fixture / read-path / integration
dispatches; read-path took 3 rounds incl. a truncation recovery). Review log
under reviews/ is gitignored.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…-2605 rebase

Carry the resolved junction namespaceId onto IncludeThroughDescriptor and
build the correlated include's junction TableSource with it, so the read
path emits namespace-qualified SQL for the junction like the rest of the
runtime after TML-2605.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…ough descriptor

The contract now carries the junction's namespaceId on through (slice 0);
the include descriptor passes it through as a required field instead of
re-deriving it from storage. Re-emit the M:N fixture so the User<->Tag
through block carries it.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
… M:N contracts (TML-2808 rebase)

Main moved namespace tables under entries.table; the hand-built M:N test
contracts still used the flat tables record, so table resolution missed
them.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…eated_at)

The shared fixture's junction declares a nullable note and a defaulted
created_at (exclusion-path coverage for requiredPayloadColumns); create
the columns so the schema matches the contract.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…junction artifacts

biome `noNonNullAssertion` (error-on-warnings) flagged the `joinOnPairs[0]!`
/ `correlationPairs[0]!` non-null assertions. Narrow with a truthy guard on
the destructured first element instead, which also removes the castAs (so
its import goes too).

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…-2841 coordinate rebase

Main un-pinned the ORM client onto ADR 221 coordinates: resolveFieldToColumn
gained a leading namespaceId, IncludeExpr requires relatedNamespaceId, and
compileSelectWithIncludes takes (contract, namespaceId, table, state). Adapt
the M:N parentLocalColumns resolution and the read-path tests accordingly.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…tions

Slice 0 (the contract-shape change) merged into main, so its instructions
touch is no longer in the open PRs diff — but slices 1-3 still touch
packages/3-extensions/, which the check:upgrade-coverage gate requires be
accompanied by an instructions touch. Record the M:N runtime slices as
incidental for extension authors (no API/contract change beyond TML-2784).

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
@tensordreams tensordreams force-pushed the tml-2785-slice-1-correlated-read branch from 12ec127 to f4f4d0b Compare June 9, 2026 11:07

@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: 3

🤖 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/query-plan-select.ts`:
- Around line 367-372: Add explicit fail-fast length checks instead of using the
fallback indexing in the M:N junction logic: before computing joinOnPairs (the
BinaryExpr.eq calls that use ColumnRef.of(junctionTable, junctionCol) and
ColumnRef.of(childTableRef, targetColumns[i] ?? junctionCol)), assert
childColumns.length === targetColumns.length and similarly assert
parentColumns.length === parentLocalColumns.length (or the actual parent-side
variable names used where parentLocalColumns are indexed). Throw a clear Error
with a descriptive message (e.g., "M:N junction: childColumns and targetColumns
length mismatch") so misaligned through descriptor arrays fail fast rather than
silently falling back.

In
`@skills/extension-author/prisma-next-extension-upgrade/upgrades/0.12-to-0.13/instructions.md`:
- Around line 55-59: Update the paragraph that claims full M:N support so it
accurately reflects this PR implements only the read-only slice: change wording
to state that TML-2785 implements correlated include reads (see
buildManyToManyJunctionArtifacts and include("tags") tests), and remove or
separate claims about EXISTS-through-junction filters and junction-table nested
writes (connect/disconnect/create) which belong to TML-2786/TML-2787
write/filter slices; explicitly note that write/filter functionality is planned
in those separate tickets and no extension-author action is required for the
current read-only runtime change.

In `@test/integration/test/sql-orm-client/mn-include.test.ts`:
- Line 106: The assertions checking for absence of "LATERAL" are case-sensitive;
update them to be case-insensitive by changing the checks on
runtime.executions[0]?.sql (and the similar check around line 279) to use a
case-insensitive match — e.g. replace .not.toContain('LATERAL') with
.not.toMatch(/lateral/i) or transform the SQL to one case and check
(.toUpperCase().not.toContain('LATERAL') or
.toLowerCase().not.toContain('lateral')) so both "LATERAL" and "lateral" are
caught.
🪄 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: 83c5097d-c7ab-45bc-a8be-19c90fd0f633

📥 Commits

Reviewing files that changed from the base of the PR and between b5c38a0 and f4f4d0b.

⛔ Files ignored due to path filters (10)
  • projects/sql-orm-many-to-many/learnings.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/slices/01-correlated-read-through-junction/dispatches/01-fixture-m2n.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/slices/01-correlated-read-through-junction/dispatches/02-read-path.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/slices/01-correlated-read-through-junction/dispatches/02-read-path.r2.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/slices/01-correlated-read-through-junction/dispatches/02-read-path.r3.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/slices/01-correlated-read-through-junction/dispatches/03-integration-tests.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/slices/01-correlated-read-through-junction/plan.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/slices/01-correlated-read-through-junction/spec.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/spec.md is excluded by !projects/**
  • projects/sql-orm-many-to-many/trace.jsonl is excluded by !projects/**
📒 Files selected for processing (10)
  • packages/3-extensions/sql-orm-client/src/collection-contract.ts
  • packages/3-extensions/sql-orm-client/src/collection-dispatch.ts
  • packages/3-extensions/sql-orm-client/src/collection.ts
  • packages/3-extensions/sql-orm-client/src/query-plan-select.ts
  • packages/3-extensions/sql-orm-client/src/types.ts
  • packages/3-extensions/sql-orm-client/test/helpers.ts
  • packages/3-extensions/sql-orm-client/test/query-plan-select.test.ts
  • skills/extension-author/prisma-next-extension-upgrade/upgrades/0.12-to-0.13/instructions.md
  • test/integration/test/sql-orm-client/mn-include.test.ts
  • test/integration/test/sql-orm-client/runtime-helpers.ts

Comment thread packages/3-extensions/sql-orm-client/src/query-plan-select.ts Outdated
Comment thread test/integration/test/sql-orm-client/mn-include.test.ts Outdated
Comment thread packages/3-extensions/sql-orm-client/src/collection-contract.ts Outdated
Comment thread packages/3-extensions/sql-orm-client/src/collection.ts Outdated
Comment thread packages/3-extensions/sql-orm-client/test/helpers.ts Outdated
Comment thread packages/3-extensions/sql-orm-client/test/query-plan-select.test.ts Outdated
Comment thread packages/3-extensions/sql-orm-client/test/query-plan-select.test.ts Outdated
Comment thread test/integration/test/sql-orm-client/mn-include.test.ts Outdated
Replace the silent `?? junctionCol` fallback indexing in
buildManyToManyJunctionArtifacts with explicit length invariants over the
through descriptor's column arrays (childColumns/targetColumns and
parentColumns/parentLocalColumns), then index with assertDefined narrowing
so a misalignment throws a clear error instead of emitting a wrong join or
correlation predicate.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…spread

Replace the manual `...(x !== undefined ? { through: x } : {})` spreads in
resolveIncludeRelation and the include builder with the ifDefined helper from
@prisma-next/utils/defined, per the use-if-defined convention.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
Migrate the M:N include query-plan unit tests off the hand-rolled
buildManyToManyContract helper and onto the real emitted fixture's relations:
User.tags via user_tags (single-column), Project.related via project_links
(composite, self-referential), and the distinct + nested-non-leaf lowering via
Project.related distinct('name').include('related'). Each test now asserts the
whole compiled plan.ast with a single toEqual and drops the lateral / 'no
LATERAL' assertions (there is only one strategy). buildManyToManyContract (and
its RawColumn type) had no remaining uses on this branch and is removed.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
…M:N include

Drop the SQL-string LATERAL assertions from the M:N include integration tests.
LATERAL can't appear (no such strategy exists in code), so the assertion tested
an implementation detail. The real non-functional invariant — the include
resolves in a single SQL execution — is already asserted via
runtime.executions length.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
The 0.12->0.13 extension-author note overstated delivered functionality: it
claimed EXISTS-through-junction filters (TML-2786) and junction nested writes
(TML-2787) alongside the correlated include read. This slice is read-only, so
narrow the note to TML-2785's M:N correlated include read; the filter and write
paths are documented in their own slices.

Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
@tensordreams

Copy link
Copy Markdown
Contributor Author

Thanks — the three findings in this review summary are already tracked as inline threads and have been addressed: query-plan-select.ts 367-372 fail-fast (6d924db), the 0.12->0.13 instructions scope (905f19c), and the LATERAL guard on mn-include.test.ts (removed entirely in f27df69, superseding the case-insensitivity suggestion). No separate change needed here.

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.

2 participants