@prisma/orm-mongo 8.0.0-rc.5 → 8.0.0-rc.5-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -10
- package/skills/prisma-8/SKILL.md +84 -0
- package/skills/prisma-8/references/build.md +142 -0
- package/skills/prisma-8/references/contract.md +417 -0
- package/skills/prisma-8/references/debug.md +141 -0
- package/skills/prisma-8/references/feedback.md +251 -0
- package/skills/prisma-8/references/migration-review.md +224 -0
- package/skills/prisma-8/references/migrations.md +519 -0
- package/skills/prisma-8/references/queries-mongo.md +236 -0
- package/skills/prisma-8/references/queries-postgres.md +415 -0
- package/skills/prisma-8/references/queries.md +168 -0
- package/skills/prisma-8/references/quickstart.md +326 -0
- package/skills/prisma-8/references/runtime.md +344 -0
- package/skills/prisma-8/references/supabase.md +244 -0
- package/skills/prisma-8/references/upgrade-app.md +101 -0
- package/skills/prisma-8/references/upgrade-extension.md +105 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.10-to-0.11/instructions.md +56 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/instructions.md +381 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-closed-mongo-contracts.ts +202 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-domain-namespaced-contracts.ts +201 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/re-emit-postgres-public-default.ts +198 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/instructions.md +339 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.12-to-0.13/re-emit-mti-variant-link-columns.ts +229 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/instructions.md +543 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.14-to-0.15/instructions.md +359 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.15-to-0.16/instructions.md +173 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/instructions.md +805 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.17-to-8.0.0-rc.1/instructions.md +72 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.7-to-0.8/instructions.md +8 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/instructions.md +36 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/instructions.md +86 -0
- package/skills/prisma-8/upgrading/app/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +588 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +158 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +42 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.10-to-0.11/instructions.md +276 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/instructions.md +738 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/migrate-contract-testing-imports.ts +97 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/regenerate-extension-public-baseline.ts +223 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.11-to-0.12/strip-migration-labels-hints.ts +340 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.12-to-0.13/instructions.md +266 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/instructions.md +522 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/migration-op-factories-to-methods.ts +290 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.13-to-0.14/uuid-preset-rename.ts +43 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.14-to-0.15/instructions.md +803 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.15-to-0.16/instructions.md +219 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/instructions.md +731 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.16-to-0.17/strip-sha256-hash-prefixes.ts +382 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.17-to-8.0.0-rc.1/instructions.md +194 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.7-to-0.8/instructions.md +8 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/instructions.md +57 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.8-to-0.9/strip-inline-contracts.ts +226 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/instructions.md +150 -0
- package/skills/prisma-8/upgrading/extension/upgrades/0.9-to-0.10/stamp-storage-types-kind.ts +360 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.1-to-8.0.0-rc.2/instructions.md +746 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.2-to-8.0.0-rc.3/instructions.md +5 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.3-to-8.0.0-rc.4/instructions.md +137 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.4-to-8.0.0-rc.5/instructions.md +129 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
---
|
|
2
|
+
from: "8.0.0-rc.1"
|
|
3
|
+
to: "8.0.0-rc.2"
|
|
4
|
+
changes:
|
|
5
|
+
- id: check-constraints-are-opaque-expressions
|
|
6
|
+
summary: |
|
|
7
|
+
CHECK constraints in `contract.json` changed shape: `{ name, column, valueSet }` became
|
|
8
|
+
`{ name, prefix, expression }`, where `expression` is the raw SQL predicate and `name`
|
|
9
|
+
is a content-addressed wire name (`<prefix>_<8hex>`, the same convention indexes and RLS
|
|
10
|
+
policies already use). Run `prisma-next contract emit` to regenerate `contract.json` and
|
|
11
|
+
`contract.d.ts` — an old-shape contract is rejected on read, so this is not optional.
|
|
12
|
+
Regeneration also changes the physical names of your enum CHECK constraints, because the
|
|
13
|
+
hash suffix is new: `prisma-next migration plan` will show a DROP of the old unsuffixed
|
|
14
|
+
constraint plus an ADD of the wire-named one. That plan needs `destructive` to drop the
|
|
15
|
+
stale constraint; under an additive-only policy the new constraint installs and the old
|
|
16
|
+
one survives, and `prisma-next db verify --strict` reports it as an undeclared extra
|
|
17
|
+
until you allow the drop. Every list (`many`) column now also carries a declared
|
|
18
|
+
element-non-null CHECK that the planner previously invented behind your back; it appears
|
|
19
|
+
in the contract and in the plan for the first time.
|
|
20
|
+
Introspection also stopped parsing predicates, so every CHECK constraint on a managed
|
|
21
|
+
table is now visible — including hand-written ones (`price > 0`, a composite `AND`, a
|
|
22
|
+
`NOT VALID` constraint) that earlier versions could not see at all. An undeclared check
|
|
23
|
+
is an extra: `prisma-next db verify --strict` reports it, and a plan run under a policy
|
|
24
|
+
that allows `destructive` emits a `dropCheckConstraint` operation for it. Read the first
|
|
25
|
+
plan for `dropCheckConstraint` operations naming constraints you wrote by hand. There is
|
|
26
|
+
To keep one, declare it: `@@check(expression: "…", map: "<its physical name>")` adopts the
|
|
27
|
+
constraint under the name it already has, after which `db verify` is clean and no plan drops
|
|
28
|
+
it. `contract infer` writes that form for you, so re-running a pull is the quickest route.
|
|
29
|
+
Let a drop through only when the constraint is deliberately retired. Running the table under
|
|
30
|
+
an additive-only policy also keeps it, but leaves it reported by `--strict` as undeclared.
|
|
31
|
+
detection:
|
|
32
|
+
glob: "**/contract.json"
|
|
33
|
+
contains:
|
|
34
|
+
- '"valueSet"'
|
|
35
|
+
- '"checks"'
|
|
36
|
+
anyMatch: true
|
|
37
|
+
- id: add-check-constraint-takes-an-expression
|
|
38
|
+
summary: |
|
|
39
|
+
In committed migration files, `this.addCheckConstraint({ schema, table, constraint,
|
|
40
|
+
column, values })` is now `this.addCheckConstraint({ schema, table, constraint,
|
|
41
|
+
expression })`. Replace the `column` and `values` pair with the predicate they used to
|
|
42
|
+
describe — `column: 'kind', values: ['admin', 'user']` becomes
|
|
43
|
+
`` expression: `"kind" IN ('admin', 'user')` `` — and use the wire name from your
|
|
44
|
+
regenerated contract as `constraint`. If the constraint was created by the same
|
|
45
|
+
migration's `createTable`, prefer moving it inline: add
|
|
46
|
+
`checkExpression(<name>, <expression>)` to that table's `constraints` array and delete
|
|
47
|
+
the follow-up `addCheckConstraint` call, which is what a freshly planned migration now
|
|
48
|
+
produces. Import `checkExpression` from the same migration entrypoint as `col` and
|
|
49
|
+
`primaryKey`.
|
|
50
|
+
detection:
|
|
51
|
+
glob: "**/migrations/**/*.ts"
|
|
52
|
+
contains:
|
|
53
|
+
- 'addCheckConstraint'
|
|
54
|
+
anyMatch: true
|
|
55
|
+
- id: specifier-default-control-policy-requires-create-namespace
|
|
56
|
+
summary: |
|
|
57
|
+
If your `prisma.config.ts` passes `defaultControlPolicy` in the options bag of
|
|
58
|
+
`typescriptContract` or `typescriptContractFromPath`, that bag now also requires
|
|
59
|
+
`createNamespace`. Stamping a default policy strips derived CHECK constraints from
|
|
60
|
+
tables the policy leaves non-managed, and the strip rebuilds storage namespaces through
|
|
61
|
+
the target's factory, so the two options travel together.
|
|
62
|
+
`typescriptContract(contract, output, { defaultControlPolicy: 'external' })` becomes
|
|
63
|
+
`typescriptContract(contract, output, { defaultControlPolicy: 'external',
|
|
64
|
+
createNamespace: postgresCreateNamespace })`, with `postgresCreateNamespace` imported
|
|
65
|
+
from the Postgres target's types entrypoint (`@internal/target-postgres/types`) — the
|
|
66
|
+
same factory the PSL specifier already takes. Calls without an options bag are
|
|
67
|
+
unchanged, and `emptyContract` already took `createNamespace`.
|
|
68
|
+
detection:
|
|
69
|
+
glob: "**/*.{ts,mts,cts}"
|
|
70
|
+
contains:
|
|
71
|
+
- 'typescriptContract'
|
|
72
|
+
- 'defaultControlPolicy'
|
|
73
|
+
anyMatch: false
|
|
74
|
+
- id: int-backed-enums-fail-at-authoring
|
|
75
|
+
summary: |
|
|
76
|
+
An `enumType()` whose codec is numeric (e.g. `pg/int4@1`) used to build fine and fail
|
|
77
|
+
later, at migrate time. It now throws `CONTRACT.ENUM_INVALID` while the contract is being
|
|
78
|
+
built, because a numeric member set cannot be rendered as a CHECK predicate. If
|
|
79
|
+
`prisma-next contract emit` fails with "numeric-enum CHECK constraints are not yet
|
|
80
|
+
supported", change that enum's codec to a text one (`pg/text@1`) and give each member a
|
|
81
|
+
string value, or replace it with a Postgres native enum (`pg.enum`), which enforces
|
|
82
|
+
membership through the column type and needs no CHECK at all.
|
|
83
|
+
detection:
|
|
84
|
+
glob: "**/*.{ts,mts,cts,prisma}"
|
|
85
|
+
contains:
|
|
86
|
+
- 'enumType'
|
|
87
|
+
anyMatch: true
|
|
88
|
+
- id: aggregate-methods-come-from-the-emitted-contract
|
|
89
|
+
summary: |
|
|
90
|
+
The aggregate methods — `count`, `sum`, `avg`, `min`, `max` — are no longer declared on the
|
|
91
|
+
ORM and SQL-builder surfaces outright. Each surface is derived from the operation names in
|
|
92
|
+
the emitted `contract.d.ts`'s `AggregateTypes` block, so a target or extension can contribute
|
|
93
|
+
an operation and it appears under its own name with no client change. Deriving the surface
|
|
94
|
+
neither adds nor removes a method by itself, but the block a re-emit produces is not the
|
|
95
|
+
list it was: PostgreSQL now contributes eight operations and SQLite seven, and every bare
|
|
96
|
+
result type moved — `count-over-a-field-counts-that-field` and
|
|
97
|
+
`aggregate-defaults-are-js-native-numbers` cover that, so work them too. If it is not — you author it
|
|
98
|
+
in code with `defineContract(...)` and hand that value straight to the client (the no-emit
|
|
99
|
+
flow), or you have not re-emitted since before 8.0.0-rc.1 — every aggregate surface resolves
|
|
100
|
+
to `AggregateOperationsUnavailable`, an empty type, and each call becomes
|
|
101
|
+
`Property 'count' does not exist on type 'AggregateOperationsUnavailable'`. That covers
|
|
102
|
+
`aggregate()`, `groupBy().aggregate()`, `groupBy().having()`, the `include(...)` reducers,
|
|
103
|
+
and `sql()`'s `fns.count` / `fns.sum` / … Runtime behaviour is unchanged; this is a
|
|
104
|
+
compile-time change. Re-emit the contract, or cast the builder to a dynamic record.
|
|
105
|
+
detection:
|
|
106
|
+
glob: "**/*.{ts,tsx,mts,cts}"
|
|
107
|
+
contains:
|
|
108
|
+
- "defineContract"
|
|
109
|
+
- ".aggregate("
|
|
110
|
+
- ".having("
|
|
111
|
+
- "fns.count"
|
|
112
|
+
anyMatch: true
|
|
113
|
+
- id: count-over-a-field-counts-that-field
|
|
114
|
+
summary: |
|
|
115
|
+
`aggregate.count(field)` renders `COUNT(<column>)`. It used to accept the argument, discard
|
|
116
|
+
it, and render `COUNT(*)`. PostgreSQL declares `count` over any input, so the derived method
|
|
117
|
+
carries both arities honestly: `count()` counts rows, `count(field)` counts that field's
|
|
118
|
+
non-null values. No previously type-safe call changes meaning — `count` took no argument
|
|
119
|
+
before, so the field-taking form never typechecked. What changes is a call that got past
|
|
120
|
+
the types: a `@ts-expect-error` above a `count(...)`, a `count(x as never)`, or dynamic
|
|
121
|
+
dispatch. Those now count a column instead of rows, which differs whenever the column holds
|
|
122
|
+
NULLs. Sweep them and drop the argument wherever `COUNT(*)` was what you meant.
|
|
123
|
+
detection:
|
|
124
|
+
glob: "**/*.{ts,tsx,mts,cts}"
|
|
125
|
+
contains:
|
|
126
|
+
- "count("
|
|
127
|
+
- ".aggregate("
|
|
128
|
+
anyMatch: true
|
|
129
|
+
- id: aggregate-defaults-are-js-native-numbers
|
|
130
|
+
summary: |
|
|
131
|
+
`count()`, `sum()` over an integer column, and `avg()` over an integer column all return
|
|
132
|
+
`number`. On PostgreSQL they returned, respectively, a `bigint`; a `bigint` or a decimal
|
|
133
|
+
string depending on the column's width; and a decimal string. On SQLite the first two
|
|
134
|
+
returned a `bigint` and `avg()` was already a `number`. The lossless results moved to three new
|
|
135
|
+
operations beside them — `countBigInt()` → `bigint`, `sumBigInt()` → `bigint` (on
|
|
136
|
+
PostgreSQL exact past 2^63 over a `BigInt` / `BigIntNumber` / `UnboundedInt` column, whose
|
|
137
|
+
total the database computes as `numeric`; over the narrower integers the total is an `int8`
|
|
138
|
+
and PostgreSQL raises `bigint out of range` past 2^63), `avgDecimal()` → decimal string
|
|
139
|
+
(PostgreSQL only; SQLite has no
|
|
140
|
+
decimal type and contributes none). An empty input set answers `count()` with `0`, not `0n`.
|
|
141
|
+
A `count()`, or a `sum()` over an integer column, whose value passes ±(2^53 − 1) raises
|
|
142
|
+
`RUNTIME.DECODE_FAILED` instead of returning a rounded number — on the `.include()` path as
|
|
143
|
+
well as the top level — so switch that call to `countBigInt()` / `sumBigInt()` wherever the
|
|
144
|
+
magnitude is real. No other result is guarded: a `sum` outside the integer columns keeps its
|
|
145
|
+
own family, and `avg` is a fraction that rounds as any double does.
|
|
146
|
+
Unchanged: `min` / `max`, `sum` / `avg` over a float column, `sum` over `numeric` (still a
|
|
147
|
+
decimal string), `sum` over `UnboundedInt` (still a `bigint`), and the ORM's `having(...)`
|
|
148
|
+
operands, which the ORM types as `number` whatever the aggregate's result type is. The SQL
|
|
149
|
+
builder's comparison operands are the other case, and they do move: `fns.gt(a, b)` types
|
|
150
|
+
both sides from one codec, so `fns.gt(fns.count(), 1n)` becomes `fns.gt(fns.count(), 1)`.
|
|
151
|
+
Sweep aggregate results for
|
|
152
|
+
`2n`-style literals, `String(count)` serialisation, `Number(...)` unwrapping, and `?? '0'`
|
|
153
|
+
coalescing, and write each as the plain number it now is. Then re-emit with
|
|
154
|
+
`prisma-next contract emit`: `contract.d.ts`'s `AggregateTypes` block carries the new
|
|
155
|
+
result codecs and the three new operations, and until you re-emit, the types describe the
|
|
156
|
+
old results and the new methods do not exist.
|
|
157
|
+
detection:
|
|
158
|
+
glob: "**/*.{ts,tsx,mts,cts}"
|
|
159
|
+
contains:
|
|
160
|
+
- ".aggregate("
|
|
161
|
+
- ".count()"
|
|
162
|
+
- ".groupBy("
|
|
163
|
+
- ".include("
|
|
164
|
+
anyMatch: true
|
|
165
|
+
- id: integer-columns-refuse-the-wrong-js-type
|
|
166
|
+
summary: |
|
|
167
|
+
A `BigInt` or `UnboundedInt` column refuses a JS `number`, and a `BigIntNumber` column
|
|
168
|
+
refuses a `bigint`, with `RUNTIME.ENCODE_FAILED` and a message naming the type that
|
|
169
|
+
arrived: `pg/int8@1 value must be a bigint, got number 9`. The wide-integer codecs used to
|
|
170
|
+
accept a number and stringify it, which let a fractional value such as `1.5` reach an
|
|
171
|
+
integer column unremarked. No typed call site changes — a `BigInt` column's application
|
|
172
|
+
type has always been `bigint` — so sweep the ones that bypassed the types: a
|
|
173
|
+
`// @ts-expect-error` over a create/update value, an `as never` / `as any` argument, a
|
|
174
|
+
value that came out of `JSON.parse` (which yields numbers, never bigints), and dynamic
|
|
175
|
+
dispatch. Convert each to the column's own type, `BigInt(value)` for a `bigint` column.
|
|
176
|
+
Schema-written literal defaults are unaffected: `BigInt @default(0)` still emits, because
|
|
177
|
+
the JSON side of these codecs accepts a safe-integer number and only the wire side does not.
|
|
178
|
+
detection:
|
|
179
|
+
glob: "**/*.{ts,tsx,mts,cts}"
|
|
180
|
+
contains:
|
|
181
|
+
- "bigint"
|
|
182
|
+
- "BigInt"
|
|
183
|
+
anyMatch: true
|
|
184
|
+
- id: authored-check-constraints
|
|
185
|
+
summary: |
|
|
186
|
+
A CHECK constraint can now be declared in the contract: `@@check(expression: "…", name: "…")`
|
|
187
|
+
in PSL, `check({ expression, name })` in the TypeScript builder. `name:` is a wire-name
|
|
188
|
+
prefix — the physical constraint becomes `name_<8hex>`, hashed over the predicate, and
|
|
189
|
+
compared by name, so Postgres reprinting the expression never causes drift. Use `map:`
|
|
190
|
+
instead to adopt a constraint that already exists under its own physical name; that form
|
|
191
|
+
compares the predicate byte-for-byte, and warns whenever the body was not captured from the
|
|
192
|
+
database — including on contracts `contract infer` wrote, which warn on the next
|
|
193
|
+
`contract emit`. `contract infer` now writes the `map:` form for you: pulling a database
|
|
194
|
+
emits `@@check` for every live check Prisma Next did not derive, so a hand-written constraint
|
|
195
|
+
is declared from the first pull instead of reading as an undeclared extra that a plan allowing
|
|
196
|
+
destructive changes would drop. Nothing is required of an existing contract — the surface is
|
|
197
|
+
additive.
|
|
198
|
+
detection:
|
|
199
|
+
glob: "**/*.{prisma,ts}"
|
|
200
|
+
contains:
|
|
201
|
+
- '@@check'
|
|
202
|
+
anyMatch: true
|
|
203
|
+
- id: runtime-query-execute-hard-cut
|
|
204
|
+
summary: |
|
|
205
|
+
Runtime row execution uses `query()`, while rc.1 prepared rows use `target.queryPrepared(prepared, params, options?)` and rc.2 uses `prepared.query(target, params, options?)`. Classify each call by its consumed result rather than replacing every `execute`: move row plans to `query`, prepared rows to `prepared.query(target, params, options?)`, and keep non-returning writes on `execute` while reading `affectedRows` when needed. Middleware uses operation-specific `beforeQuery` / `interceptQuery` / `afterQuery` and `beforeExecute` / `interceptExecute` / `afterExecute` hooks, with shared `beforeCompile`; interception returns `{ rows }` for queries and `{ stats }` for execution. There is no operation discriminator, compatibility alias, or generic fallback hook. The Mongo facade keeps static `db.query` and removes row `db.execute`; execute a built row plan through `(await db.runtime()).query(plan)`.
|
|
206
|
+
detection:
|
|
207
|
+
glob: "**/*.{ts,tsx,mts,cts}"
|
|
208
|
+
contains:
|
|
209
|
+
- ".execute("
|
|
210
|
+
- ".queryPrepared("
|
|
211
|
+
- "beforeQuery"
|
|
212
|
+
- "interceptExecute"
|
|
213
|
+
anyMatch: true
|
|
214
|
+
- id: config-file-is-prisma-config-with-an-orm-section
|
|
215
|
+
summary: |
|
|
216
|
+
The CLI config file is `prisma.config.ts` — the `prisma-next.config.ts` name is
|
|
217
|
+
deprecated — and the config value is engine-shaped: `defineConfig` from
|
|
218
|
+
`@prisma/cli-engine` wraps the whole ORM config as its `orm` section. Rename the file,
|
|
219
|
+
then wrap the existing export: alias the current `defineConfig` import (from the target
|
|
220
|
+
facade or CLI config-types) as `ormConfig` and write
|
|
221
|
+
`export default defineConfig({ orm: ormConfig({ ...existing config... }) })`, adding
|
|
222
|
+
`@prisma/cli-engine` to devDependencies. Both the deprecated filename and the flat shape
|
|
223
|
+
still load, each printing a deprecation warning on stderr, so the two steps can land
|
|
224
|
+
separately; anything asserting clean stderr around CLI invocations sees the warning
|
|
225
|
+
until both are done.
|
|
226
|
+
detection:
|
|
227
|
+
glob: "**/prisma*.config.*"
|
|
228
|
+
contains:
|
|
229
|
+
- "defineConfig"
|
|
230
|
+
anyMatch: true
|
|
231
|
+
- id: published-prisma-next-bin-retired
|
|
232
|
+
summary: |
|
|
233
|
+
Nothing published ships a `prisma-next` bin anymore: `@prisma/orm-toolchain` publishes
|
|
234
|
+
the `orm` command family at `@prisma/orm-toolchain/cli` and no bin, and the database
|
|
235
|
+
facades forward no launcher. The only user-facing binary is the unified `prisma` CLI
|
|
236
|
+
(the prisma-cli distribution), which mounts the same commands. Replace
|
|
237
|
+
`prisma-next <command>` invocations in package scripts and CI with the unified CLI's
|
|
238
|
+
equivalent, and drop any dependency that was taken only to put the bin on PATH.
|
|
239
|
+
detection:
|
|
240
|
+
glob: "**/package.json"
|
|
241
|
+
contains:
|
|
242
|
+
- "prisma-next"
|
|
243
|
+
anyMatch: true
|
|
244
|
+
- id: raw-is-a-reserved-storage-namespace
|
|
245
|
+
summary: |
|
|
246
|
+
A storage namespace named `raw` is refused: the SQL surface exposes the whole-query raw
|
|
247
|
+
statement tag as `db.sql.raw`, so a namespace of that name would be unreachable through
|
|
248
|
+
the builder while the emitted types still promised its tables. Building the client raises
|
|
249
|
+
`ORM.NAMESPACE_RESERVED` naming the namespace. Rename the namespace in your schema —
|
|
250
|
+
`@@schema("raw")` becomes any other name — re-emit the contract, and plan the rename
|
|
251
|
+
against the database as you would any other namespace rename. Only `raw` is reserved; every other namespace name is unaffected.
|
|
252
|
+
detection:
|
|
253
|
+
glob: "**/*.{prisma,json}"
|
|
254
|
+
contains:
|
|
255
|
+
- '@@schema("raw")'
|
|
256
|
+
- '"raw": {'
|
|
257
|
+
anyMatch: true
|
|
258
|
+
- id: codec-ids-are-checked-where-they-are-authored
|
|
259
|
+
summary: |
|
|
260
|
+
A codec id you write in a prepared declaration or in a contract-bound raw fragment is
|
|
261
|
+
now checked against your contract's codec map, so an id the contract does not carry is
|
|
262
|
+
a compile error where before it compiled and failed at execution with
|
|
263
|
+
`RUNTIME.PARAM_REF_MISSING_CODEC`. The usual cause is an unversioned id:
|
|
264
|
+
`db.prepare({ id: 'pg/int4' }, ...)` becomes `db.prepare({ id: 'pg/int4@1' }, ...)`, and
|
|
265
|
+
`fns.raw\`...\`.returns('pg/text')` becomes `.returns('pg/text@1')`. Take the id from your
|
|
266
|
+
emitted `contract.d.ts` — every id it carries now completes at both positions, so the
|
|
267
|
+
editor offers the correct spelling rather than accepting a wrong one. Raw fragments
|
|
268
|
+
built through a contract-free lane are unaffected; they have no map to check against.
|
|
269
|
+
detection:
|
|
270
|
+
glob: "**/*.{ts,tsx,mts,cts}"
|
|
271
|
+
contains:
|
|
272
|
+
- "prepare({"
|
|
273
|
+
- ".returns('pg/"
|
|
274
|
+
- ".returns(\"pg/"
|
|
275
|
+
anyMatch: true
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
# 8.0.0-rc.1 → 8.0.0-rc.2 — User upgrade instructions
|
|
279
|
+
|
|
280
|
+
## `aggregate-methods-come-from-the-emitted-contract`
|
|
281
|
+
|
|
282
|
+
Which aggregate methods exist is now the contract's answer rather than a fixed list in the client. The emitted `contract.d.ts` carries an `AggregateTypes` block naming every operation your target and extensions declare, and each surface below is derived from it:
|
|
283
|
+
|
|
284
|
+
| Surface | What it offers |
|
|
285
|
+
| --- | --- |
|
|
286
|
+
| `db.orm.User.aggregate((a) => …)` | one selector method per declared operation |
|
|
287
|
+
| `db.orm.User.groupBy('kind').aggregate((a) => …)` | the same |
|
|
288
|
+
| `db.orm.User.groupBy('kind').having((h) => …)` | the same, restricted to `count` / `sum` / `avg` / `min` / `max` |
|
|
289
|
+
| `db.orm.User.include('posts', (posts) => posts.count())` | one reducer per declared operation |
|
|
290
|
+
| `db.sql.public.user.select('n', (f, fns) => fns.count())` | one function per declared operation |
|
|
291
|
+
|
|
292
|
+
**If your contract is emitted, re-emit it — and keep reading.** Deriving the surface takes nothing away on its own: whatever the composed targets and extensions declare is what the block names. But the built-in targets changed what they declare in this same release. PostgreSQL now contributes eight operations and SQLite seven, and the bare results over integer columns moved — `count`, `sum`, and `avg`. `min` / `max` did not, nor did `sum` and `avg` over a float, `numeric`, or temporal column, nor `sum` over an `UnboundedInt` column. Two entries below carry those changes, and a re-emitted contract lands you in both: [`count-over-a-field-counts-that-field`](#count-over-a-field-counts-that-field) and [`aggregate-defaults-are-js-native-numbers`](#aggregate-defaults-are-js-native-numbers).
|
|
293
|
+
|
|
294
|
+
**If your contract's block is unknown, the surfaces are empty.** Two situations reach that state:
|
|
295
|
+
|
|
296
|
+
- You author the contract in TypeScript with `defineContract(...)` and pass the value straight to the client, never running `prisma-next contract emit`. A contract value built in code carries no emitted type maps.
|
|
297
|
+
- You are still using a `contract.d.ts` emitted before 8.0.0-rc.1, when the `AggregateTypes` block did not exist yet.
|
|
298
|
+
|
|
299
|
+
Either way the call is a compile error:
|
|
300
|
+
|
|
301
|
+
```text
|
|
302
|
+
Property 'count' does not exist on type 'AggregateOperationsUnavailable'.
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The type is an empty interface whose name is the diagnosis; hovering it shows the reason. Nothing changes at runtime — the client installs its aggregate methods from the composed target and extensions, exactly as it always has.
|
|
306
|
+
|
|
307
|
+
**Preferred fix: emit the contract.** Run
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
prisma-next contract emit
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
and type the client from the emitted `Contract`. That gives you the whole aggregate surface back, plus the per-operation result types and the field names each operation admits.
|
|
314
|
+
|
|
315
|
+
**Alternative, where the contract is deliberately un-emitted:** cast the builder and dispatch by name.
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
import type { AggregateSpec } from '@prisma/orm-postgres/orm-client';
|
|
319
|
+
|
|
320
|
+
type DynamicAggregates = Record<string, (field?: string) => AggregateSpec[string]>;
|
|
321
|
+
|
|
322
|
+
const stats = await db.User.aggregate((aggregate) => {
|
|
323
|
+
const dynamic = aggregate as DynamicAggregates;
|
|
324
|
+
return { total: dynamic['sum']!('views'), peak: dynamic['max']!('views') };
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
If you previously widened the *argument* instead — `aggregate.sum('views' as never)`, which compiled because the admitted field names were already `never` for such a contract — move the cast from the argument to the builder and pass the field name as a plain string.
|
|
329
|
+
|
|
330
|
+
## `count-over-a-field-counts-that-field`
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
await db.User.aggregate((aggregate) => ({ all: aggregate.count() }));
|
|
334
|
+
// SELECT COUNT(*) …
|
|
335
|
+
|
|
336
|
+
await db.User.aggregate((aggregate) => ({ withEmail: aggregate.count('email') }));
|
|
337
|
+
// SELECT COUNT("email") … — rows whose email is NULL are not counted
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
The second form used to render `COUNT(*)`: the argument was accepted and thrown away. Both arities are now read off what the target declares for `count` — PostgreSQL declares it over any input, which means both a call with a value and a call without one — so the argument is honoured.
|
|
341
|
+
|
|
342
|
+
No previously type-safe call changes meaning, because `count` took no argument and the field-taking form did not typecheck. Sweep instead for calls that bypassed the types:
|
|
343
|
+
|
|
344
|
+
- a `// @ts-expect-error` directly above a `count(...)` call — where the argument is a field your contract admits, that suppression is now unused and TypeScript flags the unused directive;
|
|
345
|
+
- `count(field as never)` or `count(field as any)`;
|
|
346
|
+
- dynamic dispatch through a `Record<string, …>` cast.
|
|
347
|
+
|
|
348
|
+
For each, decide which count you meant: `count()` for rows, `count(field)` for that field's non-null values.
|
|
349
|
+
|
|
350
|
+
## `aggregate-defaults-are-js-native-numbers`
|
|
351
|
+
|
|
352
|
+
The aggregate vocabulary is split in two. The bare operations answer in the type a JS developer expects; three new suffixed operations answer losslessly.
|
|
353
|
+
|
|
354
|
+
| Call | Reads as | Empty input set |
|
|
355
|
+
| --- | --- | --- |
|
|
356
|
+
| `count()` | `number` | `0` |
|
|
357
|
+
| `countBigInt()` | `bigint` | `0n` |
|
|
358
|
+
| `sum(field)` over `Int` / `BigInt` / `BigIntNumber` | `number \| null` | `null` |
|
|
359
|
+
| `sumBigInt(field)` over any integer column | `bigint \| null` | `null` |
|
|
360
|
+
| `avg(field)` over any integer column | `number \| null` | `null` |
|
|
361
|
+
| `avgDecimal(field)` over any integer or `Decimal` column | decimal `string \| null` | `null` |
|
|
362
|
+
|
|
363
|
+
These do not move: `min` / `max` keep the column's own type; `sum` and `avg` over a float column stay `number`; `sum` over `Decimal` stays a decimal string; `sum` over `UnboundedInt` stays a `bigint`; and the ORM's `having(...)` operands stay plain numbers, because the ORM types a HAVING comparand as `number` whatever result type the aggregate carries.
|
|
364
|
+
|
|
365
|
+
The SQL builder's comparison operands are the other case, and they do move. `fns.gt(a, b)` types both sides from one codec, so a literal compared against an aggregate follows that aggregate's result codec:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
// before
|
|
369
|
+
.having((_f, fns) => fns.gt(fns.count(), 1n))
|
|
370
|
+
// after
|
|
371
|
+
.having((_f, fns) => fns.gt(fns.count(), 1))
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Make the same one-token change wherever a `fns.count()` or an integer `fns.sum(...)` meets a literal — in `having(...)`, in `where(...)`, and inside a larger expression.
|
|
375
|
+
|
|
376
|
+
SQLite states the same policy in its own terms — `count`, integer `sum`, and `avg` are all `number`, with `countBigInt` and `sumBigInt` beside them. **SQLite has no `avgDecimal`**: an exact mean needs a decimal type the database does not have, so the method is absent from a SQLite contract and calling it is a type error.
|
|
377
|
+
|
|
378
|
+
### What to change
|
|
379
|
+
|
|
380
|
+
1. **Re-emit first.** `prisma-next contract emit` rewrites the `AggregateTypes` block. Until you do, the types describe the old results and the three new methods do not exist.
|
|
381
|
+
2. **Unwrap the bigint handling around bare aggregates.** Each of these is now noise or a type error:
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
const { total } = await db.User.aggregate((a) => ({ total: a.count() }));
|
|
385
|
+
|
|
386
|
+
total === 2n // ← was needed; now `total === 2`
|
|
387
|
+
Number(total) // ← was needed; `total` is already a number
|
|
388
|
+
String(total) // ← was needed for JSON; JSON.stringify handles it now
|
|
389
|
+
JSON.stringify(rows, (_k, v) => typeof v === 'bigint' ? String(v) : v)
|
|
390
|
+
// ↑ the replacer can go
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
3. **Change the method, not the value, where you need exactness.** A decimal-string average was doing real work in a money or reporting path; `avgDecimal(field)` returns exactly what `avg(field)` used to, and `countBigInt()` exactly what `count()` used to.
|
|
394
|
+
|
|
395
|
+
`sumBigInt(field)` matches the old `sum(field)` everywhere but one column class. On PostgreSQL, a `BigInt` or `BigIntNumber` column's `sum` used to be a decimal `string`, because the database totals a 64-bit column as `numeric`; `sumBigInt` reads that same total as a `bigint`. So a money path summing a `BigInt` column gets a `bigint` where it had a string — exact either way, but a different type. Convert at the consumption site (`String(total)`) if a decimal library or a string comparison is downstream. Over every other integer column, and on SQLite, `sumBigInt` is the old `sum` unchanged.
|
|
396
|
+
|
|
397
|
+
### The bare operations throw rather than round
|
|
398
|
+
|
|
399
|
+
A `count()`, or a `sum()` over an integer column, whose value passes ±(2^53 − 1) raises a structured error instead of answering with a rounded one:
|
|
400
|
+
|
|
401
|
+
```text
|
|
402
|
+
RUNTIME.DECODE_FAILED: pg/int8number@1 value must be an integer within
|
|
403
|
+
the safe integer range, got 9007199254740992
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
That is the trade these defaults make: a value you can compare, serialise, and do arithmetic with, and a loud failure rather than a quietly wrong total. It fires on the `.include()` path too — the reducer's value travels as a JSON number, but the guard runs after the parse, and rounding is monotone, so a value that was outside the range is still outside it after parsing.
|
|
407
|
+
|
|
408
|
+
Those two are the results a guarded integer codec produces. A `sum` over a `Decimal`, `UnboundedInt`, or float column stays in that column's own family and has no such guard, and neither does `avg`, which is a fraction already and rounds as any double does — reach for `avgDecimal` where the exact mean matters.
|
|
409
|
+
|
|
410
|
+
Totals cross the boundary in practice where counts do not: summing 64-bit IDs, or cent amounts across a large table. If a `sum` in your code can plausibly get there, move it to `sumBigInt` now rather than waiting for the error in production.
|
|
411
|
+
|
|
412
|
+
### If you are upgrading from before 8.0.0-rc.1
|
|
413
|
+
|
|
414
|
+
You cross two hops, and the aggregate result types move in both. The `0.17 → 8.0.0-rc.1` step changes `count()` to `bigint` and integer averages to decimal strings; this step changes those same calls to `number` and adds the suffixed variants. Apply the steps in order — that is what the upgrade skill does — but do the sweeping once, at the end: for `count()` and integer `sum()` / `avg()`, the destination is `number`, which is where a pre-8.0.0-rc.1 codebase already was. What genuinely changed for you across both hops is the throw outside ±(2^53 − 1) on `count()` and integer `sum()`, and the three new operations; the empty-relation `count` ends where it started, at `0`.
|
|
415
|
+
|
|
416
|
+
## `integer-columns-refuse-the-wrong-js-type`
|
|
417
|
+
|
|
418
|
+
Writing a JS `number` to a `BigInt` or `UnboundedInt` column now fails before any SQL runs:
|
|
419
|
+
|
|
420
|
+
```text
|
|
421
|
+
RUNTIME.ENCODE_FAILED: pg/int8@1 value must be a bigint, got number 9
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
The codec used to accept the number and stringify it, so `9` wrote `9` and `1.5` wrote `1.5` — a fractional value in an integer column, unremarked. The mirror case reports as clearly: passing `9n` to a `BigIntNumber` column names the type that arrived rather than complaining about a range the value is plainly inside.
|
|
425
|
+
|
|
426
|
+
No typed call site changes, because a `BigInt` column's application type has always been `bigint`. Sweep the ones that got past the types:
|
|
427
|
+
|
|
428
|
+
- a `// @ts-expect-error` over a `create(...)` / `update(...)` value;
|
|
429
|
+
- `value as never` or `value as any` in a write;
|
|
430
|
+
- a value that came out of `JSON.parse`, which yields numbers and never bigints;
|
|
431
|
+
- dynamic dispatch through a `Record<string, unknown>`.
|
|
432
|
+
|
|
433
|
+
Convert each to the column's own type — `BigInt(value)` for a `bigint` column, and a plain number for a `BigIntNumber` one.
|
|
434
|
+
|
|
435
|
+
Schema-written defaults need nothing. `BigInt @default(0)` still emits and still migrates: the JSON side of these codecs accepts a safe-integer number, because a schema language writes no `bigint` literal, and only the query-parameter side requires the exact type.
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
## `config-file-is-prisma-config-with-an-orm-section`
|
|
439
|
+
|
|
440
|
+
Two mechanical steps, in either order:
|
|
441
|
+
|
|
442
|
+
1. `git mv prisma-next.config.ts prisma.config.ts` (same for `.mts` / `.mjs` variants).
|
|
443
|
+
2. Wrap the flat export in the engine shape:
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
// before
|
|
447
|
+
import { defineConfig } from '@prisma/orm-postgres/config';
|
|
448
|
+
export default defineConfig({ ... });
|
|
449
|
+
|
|
450
|
+
// after
|
|
451
|
+
import { defineConfig } from '@prisma/cli-engine';
|
|
452
|
+
import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';
|
|
453
|
+
export default defineConfig({ orm: ormConfig({ ... }) });
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Add `@prisma/cli-engine` to `devDependencies`. The inner config is unchanged — only the file
|
|
457
|
+
name and the outer wrapper move. The loader still discovers the deprecated filename and still
|
|
458
|
+
accepts the flat shape, each with a stderr deprecation warning, so nothing breaks mid-rename;
|
|
459
|
+
finish both steps to silence the warnings.
|
|
460
|
+
|
|
461
|
+
## `published-prisma-next-bin-retired`
|
|
462
|
+
|
|
463
|
+
`prisma-next ...` in a package script resolved through a bin the database facades forwarded
|
|
464
|
+
from the toolchain. That chain is gone: the published toolchain is bin-less and exports the
|
|
465
|
+
`orm` command family at `@prisma/orm-toolchain/cli` for the unified `prisma` CLI (the
|
|
466
|
+
prisma-cli distribution) to mount. Point scripts and CI at the unified CLI, which serves the
|
|
467
|
+
same command paths.
|
|
468
|
+
|
|
469
|
+
<!--
|
|
470
|
+
PR #30049: `changes: []`. The 8.0.0-rc.2 release cut. The example diff is the lockstep version bump, the `@prisma/cli-engine` peer moving to 0.1.1, and the supabase contract fixtures re-emitted so their embedded extension version matches. No user API, contract, configuration, generated-artifact, or source translation follows from it — the consumer-facing changes this release ships are the entries above, recorded by the PRs that made them.
|
|
471
|
+
PR #29910: `changes: []`. The example changes repair test instrumentation and fixture/runtime isolation after the driver SPI split; they require no user API, contract, configuration, generated-artifact, or source translation.
|
|
472
|
+
PR #29902: `changes: []`. Generated contracts gain additive aggregate rows for new opt-in integer representation codecs, but existing schemas and source require no migration; users re-emit only when adopting the new target-scoped types.
|
|
473
|
+
PR #29950: `changes: []`. The demo applications adopt the integer representation types and the precision-preserving aggregates on their own models, and the reference docs gain the matching examples; the diff is confined to example apps and documentation and requires no user API, contract, configuration, generated-artifact, or source translation.
|
|
474
|
+
PR #29939: `changes: []`. Dependabot's weekly runtime-dependency bumps (`ws`, `lucide-react`, `postcss`, `uniku`, `@vercel/detect-agent`, and the `@types/node` / `@types/pg` / `tsx` catalog entries). The example diff is dependency version strings, and requires no user API, contract, configuration, generated-artifact, or source translation.
|
|
475
|
+
PR #29940: `changes: []`. Dependabot's weekly dev-dependency bumps (type packages, wrangler, biome, test tooling) plus the matching `biome.jsonc` `$schema` realignment; the example diffs are devDependency version strings and a schema URL, and require no user API, contract, configuration, generated-artifact, or source translation.
|
|
476
|
+
PR #29965: `changes: []`. Dependabot's dev-dependency bumps (`wrangler`, `@cloudflare/vitest-pool-workers`, `@biomejs/biome`) plus the matching `biome.jsonc` `$schema` realignment to 2.5.7; the example diffs are devDependency version strings and a schema URL, and require no user API, contract, configuration, generated-artifact, or source translation.
|
|
477
|
+
PR #30031: `changes: []`. Dependabot's weekly runtime-dependency bumps (`esbuild`, `evlog`, `next`). The example diff is dependency version strings, and requires no user API, contract, configuration, generated-artifact, or source translation.
|
|
478
|
+
-->
|
|
479
|
+
|
|
480
|
+
## Regenerating is the first step
|
|
481
|
+
|
|
482
|
+
`prisma-next contract emit` rewrites `contract.json` / `contract.d.ts` into the new check shape
|
|
483
|
+
and mints the wire names every later step refers to. Do it before editing migration files, so
|
|
484
|
+
the constraint names you paste into `addCheckConstraint` / `checkExpression` are the ones the
|
|
485
|
+
contract actually declares.
|
|
486
|
+
## What the first plan after upgrading looks like
|
|
487
|
+
|
|
488
|
+
For each enum-restricted column: a DROP of the old unsuffixed constraint and an ADD of the
|
|
489
|
+
wire-named one. For each list column: an ADD of an element-non-null constraint that was
|
|
490
|
+
previously created without ever being declared. Neither is a data change — but the DROP is
|
|
491
|
+
classified `destructive`, so a plan run under an additive-only policy converges only partway
|
|
492
|
+
and `db verify --strict` will report the leftovers until you allow it.
|
|
493
|
+
|
|
494
|
+
There may also be a third kind of operation, and it is the one to read carefully. Introspection
|
|
495
|
+
no longer parses predicates: it captures every CHECK constraint on a managed table verbatim,
|
|
496
|
+
including the hand-written and platform-installed ones that earlier versions were structurally
|
|
497
|
+
unable to see. A check the contract does not declare is an undeclared extra, so
|
|
498
|
+
`db verify --strict` reports it and a plan run under a policy that allows `destructive` emits a
|
|
499
|
+
`dropCheckConstraint` for it — a constraint you wrote by hand and that has been enforcing your
|
|
500
|
+
data all along. Grep the first plan for `dropCheckConstraint` and check every constraint named:
|
|
501
|
+
|
|
502
|
+
- to keep it, run plans for that table under an additive-only policy. The constraint stays in
|
|
503
|
+
place and keeps enforcing; plain `db verify` tolerates it, and only `--strict` reports it as
|
|
504
|
+
an undeclared extra. Better: declare it with `@@check(expression: "…", map: "<physical name>")`,
|
|
505
|
+
or re-run `contract infer`, which now emits exactly that for every live check Prisma Next did
|
|
506
|
+
not derive — the constraint becomes declared and stops being an extra at all;
|
|
507
|
+
- if it was already dead, let the drop through under the destructive plan.
|
|
508
|
+
|
|
509
|
+
Nothing drops silently — an additive-only policy never emits the operation at all — but the
|
|
510
|
+
first plan after upgrading is the moment to look, because it is the first plan that can see
|
|
511
|
+
these constraints.
|
|
512
|
+
|
|
513
|
+
## `runtime-query-execute-hard-cut`
|
|
514
|
+
|
|
515
|
+
Runtime operations state whether the caller expects rows or statement statistics. Do not apply a global `execute` → `query` replacement: an insert, update, or delete that does not return rows belongs on `execute`, while a select, a returning write, a Mongo command-result plan, or any plan whose result is iterated, awaited as an array, indexed, decoded, or otherwise read belongs on `query`.
|
|
516
|
+
|
|
517
|
+
| 8.0.0-rc.1 | 8.0.0-rc.2 |
|
|
518
|
+
| --- | --- |
|
|
519
|
+
| `await runtime.execute(rowPlan)` | `await runtime.query(rowPlan)` |
|
|
520
|
+
| `runtime.execute(rowPlan).toArray()` | `runtime.query(rowPlan).toArray()` |
|
|
521
|
+
| `await target.queryPrepared(prepared, params, options?)` | `await prepared.query(target, params, options?)` |
|
|
522
|
+
| `await runtime.execute(nonReturningWrite)` with ignored rows | `await runtime.execute(nonReturningWrite)` and ignore the returned statistics |
|
|
523
|
+
| A count or status derived from rows returned by a non-returning write | `const stats = await runtime.execute(writePlan)` and use `stats.affectedRows` |
|
|
524
|
+
|
|
525
|
+
Apply the same classification to connection and transaction scopes. `query()` and `prepared.query(target, params, options?)` remain lazy row results, so consume them inside the scope when their connection or transaction must remain valid. `execute()` is eager and resolves to `{ affectedRows: number }`; it does not return an iterable, and `affectedRows` must not be synthesized from a row array's length.
|
|
526
|
+
|
|
527
|
+
If the application defines runtime middleware, use the operation-specific hooks: query interception returns `{ rows }`, execute interception returns `{ stats }`, and completion handlers use their matching `afterQuery` or `afterExecute` result. `beforeQuery` / `interceptQuery` / `onRow` / `afterQuery` and `beforeExecute` / `interceptExecute` / `afterExecute` are distinct capabilities, while `beforeCompile` remains shared. Hook selection carries the operation distinction; contexts and results have no operation discriminator. Row-oriented middleware must not derive statistics from rows, and no compatibility aliases or generic fallback hooks are provided.
|
|
528
|
+
|
|
529
|
+
Tests that observe row queries should spy on `driver.query`, not `driver.execute`; statistics tests should observe `driver.execute`. Keep separate row-result and statistics queues so a wrong route fails loudly. Behavior intended for both operations assigns one private implementation to both corresponding hook names. Mongo keeps `db.query` as the static builder and has no row-execution `db.execute` facade method: build with `db.query`, obtain the connected runtime, then query through `(await db.runtime()).query(plan)`.
|
|
530
|
+
|
|
531
|
+
Search broadly for `.execute(` and retired prepared execution, then inspect each candidate's plan and downstream use. Rows being iterated, indexed, decoded, compared as arrays, or passed to a row mapper identify `query`; reads of `affectedRows` or ignored results from non-returning DML identify `execute`. Leave unrelated APIs such as migration runners alone.
|
|
532
|
+
|
|
533
|
+
## `raw-is-a-reserved-storage-namespace`
|
|
534
|
+
|
|
535
|
+
The SQL surface answers `db.sql.raw` with the whole-query raw statement tag, so `raw` is no
|
|
536
|
+
longer available as a storage namespace name. A contract that declares one is refused where the
|
|
537
|
+
client is built, before any query runs:
|
|
538
|
+
|
|
539
|
+
```text
|
|
540
|
+
ORM.NAMESPACE_RESERVED: The SQL surface exposes the raw statement tag as "db.raw", so a storage
|
|
541
|
+
namespace named "raw" cannot be reached through it. Rename the namespace in the schema.
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
Rename the namespace and re-emit:
|
|
545
|
+
|
|
546
|
+
```prisma
|
|
547
|
+
// Before: unreachable through the builder
|
|
548
|
+
model Event {
|
|
549
|
+
id String @id
|
|
550
|
+
@@schema("raw")
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// After: any other name
|
|
554
|
+
model Event {
|
|
555
|
+
id String @id
|
|
556
|
+
@@schema("ingest")
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Then re-emit the contract, and plan the rename against the database as you would any
|
|
561
|
+
other namespace rename — the physical schema still carries the old name until a plan moves it.
|
|
562
|
+
Only `raw` is reserved; no other namespace name is affected.
|
|
563
|
+
|
|
564
|
+
## `codec-ids-are-checked-where-they-are-authored`
|
|
565
|
+
|
|
566
|
+
Two places where you write a codec id by hand now check it against the codec map your
|
|
567
|
+
contract emitted: the declaration passed to `db.prepare(...)`, and `.returns(...)` on a raw
|
|
568
|
+
fragment built from a contract-bound tag.
|
|
569
|
+
|
|
570
|
+
```ts
|
|
571
|
+
// Before: compiled, then failed at execution with RUNTIME.PARAM_REF_MISSING_CODEC
|
|
572
|
+
await db.prepare({ id: 'pg/int4' }, (sql, params) => ...);
|
|
573
|
+
const upper = fns.raw`UPPER(${f.email})`.returns('pg/text');
|
|
574
|
+
|
|
575
|
+
// After: the id is the one your contract carries
|
|
576
|
+
await db.prepare({ id: 'pg/int4@1' }, (sql, params) => ...);
|
|
577
|
+
const upper = fns.raw`UPPER(${f.email})`.returns('pg/text@1');
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
The compile error is the messenger, not the injury: an id no codec registry carries could
|
|
581
|
+
never have executed. If a declaration or fragment of yours stops compiling, the id in it was
|
|
582
|
+
already wrong at runtime.
|
|
583
|
+
|
|
584
|
+
Read the correct spelling off your emitted `contract.d.ts`, or let the editor offer it — the
|
|
585
|
+
ids now complete at both positions, which is the other half of this change.
|
|
586
|
+
|
|
587
|
+
A raw fragment built through a contract-free lane keeps accepting any string: that lane has
|
|
588
|
+
no contract map to check an id against.
|