CLI: Rename Prisma 2 to Prisma Framework - #940
Merged
Merged
Conversation
Member
|
LGTM |
janpio
approved these changes
Nov 16, 2019
Contributor
|
(I still think we should have a definitive decision if it should be |
|
|
It used to be TheFacebook... used to be. |
tensordreams
added a commit
that referenced
this pull request
Jul 28, 2026
## Linked issue Refs [TML-2979](https://linear.app/prisma-company/issue/TML-2979/sql-orm-updatecountdeletecount-compile-mti-variant-table-predicates) ## Summary `updateCount`, `deleteCount`, and the include-backed `deleteAll` path now compile MTI variant-narrowed predicates through a correlated joined subquery instead of placing variant-table references directly in the outer write. The matching count read also keeps variant context so it counts the same joined row set that the write targets. ## At a glance ```ts const count = await mixedPoly .variant('Feature') .where((task) => task.priority.gt(1)) .updateCount({ title: 'Queued' }); ``` Before this change, the accepted predicate could compile into a bare `UPDATE ... WHERE features.priority > ...` without `features` in scope. ## Decision This PR ships correlated-subquery planning for MTI variant-narrowed `updateCount`, `deleteCount`, and the internal count delete used by `deleteAll()` with includes. The count read propagates `variantName` and `modelName` into `compileSelect`, so the existing read planner joins the selected MTI table. The count write detects MTI variant narrowing and wraps the original filters in `WHERE EXISTS (...)`, where the inner query aliases the base table, joins the variant table, and correlates back to the outer write by primary key. ## Reviewer notes - The important behavior lives in `compileUpdateCount` / `compileDeleteCount`; non-variant and STI paths intentionally keep the old plain-filter shape. - The include-backed `deleteAll()` path threads `variantName` and `modelName` into `compileDeleteCount`, keeping its snapshot read and delete mutation aligned. - The write plan uses one SQL statement and avoids materialising matching primary keys in JS. - The new tests avoid adding bare `as` casts; the package lint still prints existing no-bare-cast infos from older production files, but it exits successfully. ## How it fits together 1. `Collection.updateCount` and `Collection.deleteCount` build a matching primary-key read before the write. That read now carries `variantName` and `modelName` into `compileSelect`, allowing the normal MTI join machinery to join `features`. 2. `query-plan-mutations` adds `buildCountMutationWhere`, which only switches behavior when both a selected variant and model name identify an MTI variant. 3. The MTI write filter creates an inner base-table alias, rewrites base-table references in the original filters to that alias, joins the selected variant table, and correlates the inner row to the outer write target on the primary key. 4. Scalar variant fields and variant-declared relation predicates both work because their `features.*` references now live inside the joined subquery scope. 5. `deleteAll()` with includes passes its collection variant context into the internal count delete, so that path also routes through the MTI-aware predicate builder. ## Behavior changes & evidence - **MTI scalar predicates in count writes are executable**: `.variant('Feature').where((task) => task.priority.gt(1)).updateCount(...)` now scopes `features.priority` through a joined subquery. - Implementation: [packages/3-extensions/sql-orm-client/src/query-plan-mutations.ts](packages/3-extensions/sql-orm-client/src/query-plan-mutations.ts), [packages/3-extensions/sql-orm-client/src/collection.ts](packages/3-extensions/sql-orm-client/src/collection.ts) - Evidence: [packages/3-extensions/sql-orm-client/test/collection-variant.test.ts](packages/3-extensions/sql-orm-client/test/collection-variant.test.ts) - **Variant-declared relation predicates in count writes are executable**: `.variant('Feature').where((task) => task.assignee.some()).deleteCount()` now scopes `features.assignee_id` through the same joined subquery. - Implementation: [packages/3-extensions/sql-orm-client/src/query-plan-mutations.ts](packages/3-extensions/sql-orm-client/src/query-plan-mutations.ts) - Evidence: [packages/3-extensions/sql-orm-client/test/collection-variant.test.ts](packages/3-extensions/sql-orm-client/test/collection-variant.test.ts) - **Include-backed MTI deletes keep variant predicates scoped**: `.variant('Feature').where(...).include('assignee').deleteAll()` now threads variant context into the delete mutation instead of emitting the original outer predicate. - Implementation: [packages/3-extensions/sql-orm-client/src/collection.ts](packages/3-extensions/sql-orm-client/src/collection.ts) - Evidence: [packages/3-extensions/sql-orm-client/test/collection-variant.test.ts](packages/3-extensions/sql-orm-client/test/collection-variant.test.ts) ## Compatibility / migration / risk No public API or extension-author migration change. The SQL shape changes only for MTI variant-narrowed count writes, including the internal count delete used by `deleteAll()` with includes; plain, non-variant, and STI count writes keep the existing direct WHERE shape. ## Testing performed - `pnpm --filter @prisma-next/sql-orm-client typecheck` - `pnpm --filter @prisma-next/sql-orm-client test -- collection-variant.test.ts` - `pnpm --filter @prisma-next/sql-orm-client test -- query-plan-mutations.test.ts` - `pnpm --filter @prisma-next/sql-orm-client lint` - `pnpm --filter @prisma-next/sql-orm-client build` - `pnpm lint:casts` - `pnpm check:upgrade-coverage --mode pr` - `git diff --check` ## Skill update n/a — internal bug fix; no user-facing commands, flags, public TypeScript API, error codes, or glossary terms changed. ## Alternatives considered - **Materialise matching primary keys before writing**: this would reuse the joined read path directly, but it changes bulk count writes into a read-then-write-by-key workflow and can move an unbounded matching set through JS. - **Add `UPDATE ... FROM` / joined delete support to the mutation AST**: this would make the join explicit in the write AST, but it broadens the shared mutation AST and renderer surface for a narrow bug fix. The correlated subquery fits the existing AST. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). The DCO status check will block merge if any commit is missing a `Signed-off-by:` trailer. - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated (or `n/a` if the change is doc-only / refactor with no behavioural delta). - [x] The PR title is in `TML-NNNN: <sentence-case title>` form (Linear ticket prefix + concise title naming the concrete deliverable). See `.claude/skills/create-pr/SKILL.md` for the full convention. - [x] The **Skill update** section above is filled in (or stated `n/a — internal only`). ## Notes for the reviewer The branch records an incidental `changes: []` declaration in `skills/extension-author/prisma-next-extension-upgrade/upgrades/0.15-to-0.16/instructions.md`; no extension-author action is required for this internal planner bug fix. --------- Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
SevInf
added a commit
that referenced
this pull request
Aug 6, 2026
Shaping artifacts for the **affected-row-counts** project — spec and three-slice plan, per the project lifecycle in `projects/README.md`. ## What this project does `updateAndCount` and `deleteAndCount` run two statements: a `SELECT` of every matching primary key, then the write — returning the *read's* row count and discarding whatever the write reported. That is not atomic (outside a transaction a concurrent insert is updated but not counted), it evaluates the filter twice and materialises every matching key in JS purely to call `.length` on it, and it builds the two `WHERE` clauses through different code paths that already drifted once for MTI variants (#940). The count already exists and is thrown away — Postgres reports it in the `CommandComplete` tag, SQLite in `sqlite3_changes64()` via `StatementSync.run()`. The gap is structural: `RuntimeScope.execute()` returns a row stream with nowhere for statement metadata to live. After this project the driver SPI splits along the question being asked, named the way every prior art names it (JDBC, ADO.NET, Go): query<Row>(req): AsyncIterable<Row> // rows execute(req): Promise<SqlStatementStats> // { affectedRows: number } `affectedRows` is not optional — absence is not a state either engine has for the statements `execute()` exists to serve. Statistics never travel through a row stream, so the seven `for await` re-wrap sites between driver and caller stop being a hazard. Prepared-ness rides on the request rather than doubling the method surface, so four driver methods become two. ## Scope boundaries Streaming write terminals, `createAndCount`, and new targets are out. Count semantics are deliberately *not* unified across targets — Postgres's command tag, SQLite's `sqlite3_changes64()`, and Mongo's `modifiedCount` each mean something different, and the project documents the difference rather than reconciling it. ## Decision provenance Three questions were settled with the operator at spec time and moved into the spec body: the execution shape (an earlier single-`execute()` frame-yielding design was considered and reversed), the naming falling out of that shape, and per-driver count semantics. Spec § Open Questions records the reversal. The project amends ADR 210 — Prepared Statements rather than adding a new ADR: every principle it states survives, only the shape they were expressed through changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Steven McClankerton <tatarintsev@prisma.io>
SevInf
added a commit
that referenced
this pull request
Aug 6, 2026
Shaping artifacts for the **affected-row-counts** project — spec and three-slice plan, per the project lifecycle in `projects/README.md`. ## What this project does `updateAndCount` and `deleteAndCount` run two statements: a `SELECT` of every matching primary key, then the write — returning the *read's* row count and discarding whatever the write reported. That is not atomic (outside a transaction a concurrent insert is updated but not counted), it evaluates the filter twice and materialises every matching key in JS purely to call `.length` on it, and it builds the two `WHERE` clauses through different code paths that already drifted once for MTI variants (#940). The count already exists and is thrown away — Postgres reports it in the `CommandComplete` tag, SQLite in `sqlite3_changes64()` via `StatementSync.run()`. The gap is structural: `RuntimeScope.execute()` returns a row stream with nowhere for statement metadata to live. After this project the driver SPI splits along the question being asked, named the way every prior art names it (JDBC, ADO.NET, Go): query<Row>(req): AsyncIterable<Row> // rows execute(req): Promise<SqlStatementStats> // { affectedRows: number } `affectedRows` is not optional — absence is not a state either engine has for the statements `execute()` exists to serve. Statistics never travel through a row stream, so the seven `for await` re-wrap sites between driver and caller stop being a hazard. Prepared-ness rides on the request rather than doubling the method surface, so four driver methods become two. ## Scope boundaries Streaming write terminals, `createAndCount`, and new targets are out. Count semantics are deliberately *not* unified across targets — Postgres's command tag, SQLite's `sqlite3_changes64()`, and Mongo's `modifiedCount` each mean something different, and the project documents the difference rather than reconciling it. ## Decision provenance Three questions were settled with the operator at spec time and moved into the spec body: the execution shape (an earlier single-`execute()` frame-yielding design was considered and reversed), the naming falling out of that shape, and per-driver count semantics. Spec § Open Questions records the reversal. The project amends ADR 210 — Prepared Statements rather than adding a new ADR: every principle it states survives, only the shape they were expressed through changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Steven McClankerton <tatarintsev@prisma.io>
SevInf
added a commit
that referenced
this pull request
Aug 6, 2026
Shaping artifacts for the **affected-row-counts** project — spec and three-slice plan, per the project lifecycle in `projects/README.md`. ## What this project does `updateAndCount` and `deleteAndCount` run two statements: a `SELECT` of every matching primary key, then the write — returning the *read's* row count and discarding whatever the write reported. That is not atomic (outside a transaction a concurrent insert is updated but not counted), it evaluates the filter twice and materialises every matching key in JS purely to call `.length` on it, and it builds the two `WHERE` clauses through different code paths that already drifted once for MTI variants (#940). The count already exists and is thrown away — Postgres reports it in the `CommandComplete` tag, SQLite in `sqlite3_changes64()` via `StatementSync.run()`. The gap is structural: `RuntimeScope.execute()` returns a row stream with nowhere for statement metadata to live. After this project the driver SPI splits along the question being asked, named the way every prior art names it (JDBC, ADO.NET, Go): query<Row>(req): AsyncIterable<Row> // rows execute(req): Promise<SqlStatementStats> // { affectedRows: number } `affectedRows` is not optional — absence is not a state either engine has for the statements `execute()` exists to serve. Statistics never travel through a row stream, so the seven `for await` re-wrap sites between driver and caller stop being a hazard. Prepared-ness rides on the request rather than doubling the method surface, so four driver methods become two. ## Scope boundaries Streaming write terminals, `createAndCount`, and new targets are out. Count semantics are deliberately *not* unified across targets — Postgres's command tag, SQLite's `sqlite3_changes64()`, and Mongo's `modifiedCount` each mean something different, and the project documents the difference rather than reconciling it. ## Decision provenance Three questions were settled with the operator at spec time and moved into the spec body: the execution shape (an earlier single-`execute()` frame-yielding design was considered and reversed), the naming falling out of that shape, and per-driver count semantics. Spec § Open Questions records the reversal. The project amends ADR 210 — Prepared Statements rather than adding a new ADR: every principle it states survives, only the shape they were expressed through changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Steven McClankerton <tatarintsev@prisma.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I've adjusted the wording in the
prisma2CLI commands to reference the "Prisma Framework" instead of "Prisma 2".