@prisma/orm-mongo 8.0.0-rc.9-dev.7 → 8.0.0-rc.9-dev.9
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 +8 -8
- package/skills/prisma-8/SKILL.md +16 -13
- package/skills/prisma-8/references/contract.md +60 -31
- package/skills/prisma-8/references/debug.md +44 -41
- package/skills/prisma-8/references/migration-model.md +2 -2
- package/skills/prisma-8/references/migration-review.md +28 -15
- package/skills/prisma-8/references/migrations.md +85 -69
- package/skills/prisma-8/references/queries-mongo.md +16 -16
- package/skills/prisma-8/references/queries-postgres.md +78 -78
- package/skills/prisma-8/references/queries.md +54 -28
- package/skills/prisma-8/references/quickstart.md +32 -41
- package/skills/prisma-8/references/runtime.md +76 -54
- package/skills/prisma-8/references/supabase.md +15 -28
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.9-to-8.0.0-rc.10/instructions.md +1 -0
|
@@ -54,18 +54,15 @@ The graph is a static, committed artifact. Several branch tips may coexist, roll
|
|
|
54
54
|
|
|
55
55
|
### Diagnostic codes
|
|
56
56
|
|
|
57
|
-
`migration status`
|
|
57
|
+
`migration status` answers with a document (`--json`): `spaces[]`, each with `currentContract` (the marker hash, or `null` when the database has no marker for that space), `targetContract`, and `migrations[]` carrying the `applied` / `pending` / `unreachable` status above; a `summary` line; and `diagnostics[]`. The ordinary states — up to date, N pending, no marker yet, marker on another branch, no path to the target — are read from `spaces[]` and the summary, not from diagnostic codes. The three diagnostics it can attach are all `warn`, and each carries a `message` and `hints` — the same hints the CLI prints under the summary line:
|
|
58
58
|
|
|
59
|
-
| Code |
|
|
60
|
-
|
|
61
|
-
| `MIGRATION.
|
|
62
|
-
| `MIGRATION.
|
|
63
|
-
| `
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
| `MIGRATION.DIVERGED` | warn | Multiple valid leaves; the destination is ambiguous. | Pass `--to <name>`, or `migration ref set <name> <hash>` to create one. |
|
|
67
|
-
| `CONTRACT.AHEAD` | warn | Contract head is not in the graph — the contract was edited without re-planning. | `migration plan` to extend the graph. |
|
|
68
|
-
| `CONTRACT.UNREADABLE` | warn | `contract.json` couldn't be read. | `contract emit` to regenerate it. |
|
|
59
|
+
| Code | Meaning in the navigation model | Next move |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `MIGRATION.MARKER_NOT_IN_HISTORY` | Online; marker hash is not a node in the graph. The database was changed outside the migration system. | Decide which side is truth: `db sign` (accept DB as truth), `db update` (push contract to DB), `contract infer` (re-derive contract from DB), or `db verify` (inspect first). **Not** the same as `MIGRATION.MARKER_MISMATCH`, which `db migrate` raises as an error before any DDL when the marker hash is not a graph node. |
|
|
62
|
+
| `MIGRATION.MISSING_INVARIANTS` | Marker reached the destination structurally but lacks invariants the target ref declares. | `db migrate --to <name> --db $URL` to take a path that covers them. |
|
|
63
|
+
| `CONTRACT.UNREADABLE` | `contract.json` couldn't be read. | `contract emit` to regenerate it. |
|
|
64
|
+
|
|
65
|
+
Conditions that make the run *refuse* instead (exit `2`) arrive as ordinary errors: `MIGRATION.NO_INVARIANT_PATH` (no path covers the missing invariants), `MIGRATION.UNKNOWN_INVARIANT` (the ref names an invariant no edge provides), and the ref-resolution errors for a bad `--to` / `--from`.
|
|
69
66
|
|
|
70
67
|
### Graph-tree output
|
|
71
68
|
|
|
@@ -182,9 +179,25 @@ For a human-readable ordered preview of the migration path before applying, use
|
|
|
182
179
|
--to staging --db "$STAGING_DATABASE_URL" --json > status.json
|
|
183
180
|
node -e '
|
|
184
181
|
const s = JSON.parse(require("fs").readFileSync("status.json", "utf8"));
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
const problems = [];
|
|
183
|
+
for (const d of s.diagnostics ?? []) {
|
|
184
|
+
if (d.severity === "warn") problems.push(`${d.code}: ${d.message}`);
|
|
185
|
+
}
|
|
186
|
+
for (const space of s.spaces ?? []) {
|
|
187
|
+
if (space.currentContract === null) problems.push(`${space.space}: database has no marker`);
|
|
188
|
+
const unreachable = space.migrations.filter(m => m.status === "unreachable");
|
|
189
|
+
if (unreachable.length) problems.push(`${space.space}: ${unreachable.length} unreachable migration(s)`);
|
|
190
|
+
}
|
|
191
|
+
// Pending migrations are the normal case before Apply; block on them
|
|
192
|
+
// only if this job is a verify-only gate (set EXPECT_UP_TO_DATE=1).
|
|
193
|
+
if (process.env.EXPECT_UP_TO_DATE === "1") {
|
|
194
|
+
for (const space of s.spaces ?? []) {
|
|
195
|
+
const pending = space.migrations.filter(m => m.status === "pending");
|
|
196
|
+
if (pending.length) problems.push(`${space.space}: ${pending.length} pending migration(s)`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (problems.length) {
|
|
200
|
+
console.error("Blocking:\n" + problems.join("\n"));
|
|
188
201
|
process.exit(1);
|
|
189
202
|
}
|
|
190
203
|
'
|
|
@@ -192,7 +205,7 @@ For a human-readable ordered preview of the migration path before applying, use
|
|
|
192
205
|
run: pnpm prisma db migrate --to staging --db "$STAGING_DATABASE_URL"
|
|
193
206
|
```
|
|
194
207
|
|
|
195
|
-
`migration status` exits non-zero only on hard errors (unreadable migrations directory, unsatisfiable invariants, unreconstructable history).
|
|
208
|
+
`migration status` exits non-zero only on hard errors (unreadable migrations directory, unsatisfiable invariants, unreconstructable history). Pending migrations, a missing marker (`currentContract: null`), and the `warn` diagnostics (`MIGRATION.MARKER_NOT_IN_HISTORY`, `MIGRATION.MISSING_INVARIANTS`, `CONTRACT.UNREADABLE`) all leave the exit code at `0` — the agent (or a CI gate) must inspect `spaces[]` and `diagnostics[]` and fail the build itself. Use `--json` so the gate parses a structured shape rather than the human summary.
|
|
196
209
|
|
|
197
210
|
`db migrate` is interactive-free and has no destructive-op confirmation prompt — the safety rails that prompt for destructive changes live on `db update` (see the `references/migrations.md` skill). Whatever the planner put in the migration graph is what `db migrate` runs; review happens at `migration plan` and at `migration status` time, before the apply step.
|
|
198
211
|
|
|
@@ -18,7 +18,7 @@ Once the contract changes, you choose how the change reaches the database. This
|
|
|
18
18
|
- User edited the contract and wants to apply the change to the DB.
|
|
19
19
|
- User wants to author a migration with a data transform.
|
|
20
20
|
- User wants to run pending migrations against a local DB.
|
|
21
|
-
- User hit `MIGRATION.HASH_MISMATCH`, `
|
|
21
|
+
- User hit `MIGRATION.HASH_MISMATCH`, `MIGRATION.UNFILLED_PLACEHOLDER`, or a partially-applied migration.
|
|
22
22
|
- User mentions: *migrate, migration, db push, db update, `prisma migrate dev`, `prisma migrate deploy`, drift, hash mismatch, data backfill*.
|
|
23
23
|
|
|
24
24
|
## When Not to Use
|
|
@@ -30,7 +30,7 @@ Once the contract changes, you choose how the change reaches the database. This
|
|
|
30
30
|
|
|
31
31
|
## Key Concepts
|
|
32
32
|
|
|
33
|
-
- **`db update` (quick path).** Reads the emitted contract, diffs against the live DB, applies the change. Optional `--dry-run` prints the plan without executing.
|
|
33
|
+
- **`db update` (quick path).** Reads the emitted contract, diffs against the live DB, applies the change. Optional `--dry-run` prints the plan without executing. A destructive operation is applied only with consent: interactively you type the database name; non-interactively pass `--confirm <database>` (`--yes` does not grant it). **Writes no migration directory.** Operations needing data transforms are not handled by this path — `db update` excludes the `data` operation class entirely and short-circuits where a data transform would be required. Use only against a database that has no shared history with anyone else (your local dev DB).
|
|
34
34
|
- **`migration plan` (formal path).** Reads the emitted contract, diffs it against a resolved origin — explicit `--from`, else the `db` ref, else the empty database; there is no "head of the graph" to chain from (see `references/migration-model.md`) — and writes a new migration package under `migrations/app/<YYYYMMDDTHHMM>_<snake_slug>/`. If any operation needs a data transform, the package's `migration.ts` contains `placeholder(...)` calls you fill in.
|
|
35
35
|
- **The `app/` segment in migration paths is the consuming application's contract-space id.** Every migration *you* author lives under `migrations/app/`. Extensions your contract depends on get their own sibling directories (`migrations/<extension-space-id>/`) — those are managed by the extension package and you don't write into them. The `app/` segment lands automatically the first time you run `migration plan` / `db init` against an app-level config.
|
|
36
36
|
- **Migration package files** (inside each `migrations/app/<dir>/`):
|
|
@@ -39,40 +39,41 @@ Once the contract changes, you choose how the change reaches the database. This
|
|
|
39
39
|
- `migration.ts` — TypeScript authoring source, **framework-rendered** by `migration plan` (or `migration new`). You edit specific holes in it (see *Fill a placeholder* below) and re-emit `ops.json` / `migration.json` by running it.
|
|
40
40
|
- **Contract snapshots.** `migration.ts` imports its bookend contracts from the shared, content-addressed store at `migrations/snapshots/<hex>/contract.json` + `contract.d.ts` (`<hex>` is the contract's 64-hex storage hash) — not from files inside the migration package.
|
|
41
41
|
- **Self-emit.** Running `node migrations/app/<dir>/migration.ts` regenerates `ops.json` and `migration.json` from the (possibly edited) TS source. This is the only supported way to update an existing migration package after edits.
|
|
42
|
-
- **`migration.ts` shape.** Framework-rendered. A class
|
|
43
|
-
- **`placeholder(slot)`.** A sentinel the planner emits into the rendered `migration.ts` (from
|
|
42
|
+
- **`migration.ts` shape.** Framework-rendered. A class `M extends Migration<Start, End>` (from `@internal/postgres/migration` on Postgres, `@prisma/orm-mongo/target/migration` on Mongo — see the framing block below) that assigns the two snapshot imports to `startContractJson` / `endContractJson` (`Start` is `never` and there is no `startContractJson` on a baseline) and has an `operations` getter returning an array of operation values. **On Postgres the operation factories are methods on the base class** (`this.addColumn({...})`, `this.setNotNull({...})`, `this.dataTransform(...)`) taking one options object; free helpers like `col(...)` build the column descriptors they take. **On Mongo they are free factories** (`createIndex(...)`, `dataTransform(...)`) imported beside `Migration`. The file ends with `MigrationCLI.run(import.meta.url, M)` so executing it self-emits.
|
|
43
|
+
- **`placeholder(slot)`.** A sentinel the planner emits into the rendered `migration.ts` (from the same `.../migration` import as `Migration`) wherever a data transform is needed. Calling `placeholder(...)` at emit time throws `MIGRATION.UNFILLED_PLACEHOLDER` with `meta.slot` naming the hole. The user replaces the `() => placeholder(...)` arrow with a real query-plan closure (Postgres) or fills `dataTransform({ check, run })` sources (Mongo — see *Fill a placeholder*), then self-emits.
|
|
44
44
|
- **`this.dataTransform(endContract, name, { check, run })`.** The data-transform factory. `check` is a rowset query whose presence-of-any-row signals "work remains"; `run` is one or more mutation queries that perform the backfill. Both are lazy closures returning query-plans built against `endContract`. The runner wraps `check` as `EXISTS(...)` for precheck and `NOT EXISTS(...)` for postcheck, so the same closure asserts both "there is work" and "the work is done".
|
|
45
|
-
- **`pendingPlaceholders`.** A boolean field on the JSON result of `migration plan`. `true` means the package was written but contains unfilled placeholders — `db migrate` will throw `
|
|
45
|
+
- **`pendingPlaceholders`.** A boolean field on the JSON result of `migration plan`. `true` means the package was written but contains unfilled placeholders — `db migrate` will throw `MIGRATION.UNFILLED_PLACEHOLDER` until you edit `migration.ts` and self-emit.
|
|
46
46
|
- **`migrationHash`.** Content-addressed identity of a migration package. `MIGRATION.HASH_MISMATCH` fires when the stored hash in `migration.json` disagrees with the hash recomputed from the on-disk files (almost always: someone edited `migration.ts` without self-emitting).
|
|
47
47
|
- **Marker.** Records "this database is at contract hash X for space Y". **Postgres:** a row in `prisma_contract.marker`. **Mongo:** a document in the `_prisma_migrations` collection (keyed by space). Each successful migration advances the marker once schema verification passes for that space. `db sign` writes the marker from the current contract hash, but only after a schema-verification pass succeeds (it will not sign a database whose live schema disagrees with the contract).
|
|
48
|
-
- **Apply atomicity.** **Postgres:**
|
|
48
|
+
- **Apply atomicity.** **Postgres:** one `db migrate` run is one transaction — the runner issues a single `BEGIN`, applies every pending migration for every contract space, then one `COMMIT`; any failure issues `ROLLBACK` for the whole run, so the marker stays where it was before the command. **Mongo:** DDL ops (`createCollection`, `createIndex`, `collMod`, `setValidation`, …) are not wrapped in a multi-document transaction; the runner applies ops, verifies the live schema against the destination contract, and advances the marker only on verify-pass (resumable across spaces — see the MongoDB family doc). Ordinary DDL + `dataTransform` flows stay consistent; partial state from failed mid-migration runs is diagnosed with `db verify` / `db schema`, not assumed away.
|
|
49
49
|
- **Operation classes.** Every operation declares an `operationClass`: `additive`, `widening`, `data`, or `destructive`. The CLI surfaces these in the plan preview and in JSON output. There is no `long-running` class and the framework does not emit `CREATE INDEX CONCURRENTLY` — operations stay transactional.
|
|
50
50
|
|
|
51
51
|
## `migration.ts` is framework-rendered, not hand-authored
|
|
52
52
|
|
|
53
53
|
Files under `migrations/<space-id>/<timestamp>/migration.ts` (for your own app, `<space-id>` is always `app/`) are **rendered for you** by the framework — `prisma migration plan` writes a populated package whenever the contract changes, and `prisma migration new` writes an empty scaffold when you want to author operations directly. You do not write these files from scratch. You edit specific holes the framework leaves behind — chiefly replacing `placeholder("<slot>")` sentinels (Postgres) or filling `dataTransform({ check, run })` pipeline slots (Mongo) — then self-emit.
|
|
54
54
|
|
|
55
|
-
**Postgres** rendered imports point at `@internal/postgres/migration` (or `@internal/sqlite/migration` for SQLite projects).
|
|
55
|
+
**Postgres** rendered imports point at `@internal/postgres/migration` (or `@internal/sqlite/migration` for SQLite projects): one line carrying `Migration`, `MigrationCLI`, `col`, `placeholder`, `rawSql`, and any other free helper the operations need.
|
|
56
56
|
|
|
57
|
-
**Mongo** rendered imports
|
|
57
|
+
**Mongo** rendered imports point at one module too, `@prisma/orm-mongo/target/migration`, which carries `Migration`, `MigrationCLI`, `placeholder`, and the operation factories (`createIndex`, `dataTransform`, …). Raw command classes for data transforms come from `@prisma/orm-mongo/query-ast/execution`.
|
|
58
58
|
|
|
59
59
|
Treat the rendered import lines as framework-managed on both targets:
|
|
60
60
|
|
|
61
|
-
- Leave them where they are. Don't rewrite them to a different
|
|
62
|
-
- If you need an additional
|
|
61
|
+
- Leave them where they are. Don't rewrite them to a different path; the framework's renderer is the authoritative shape and any change you make by hand will be reverted (and may trip `MIGRATION.HASH_MISMATCH`) the next time the package is re-rendered or self-emitted.
|
|
62
|
+
- If you need an additional helper symbol, **add it to the existing rendered import line** rather than introducing a second import from a different subpath.
|
|
63
63
|
- The "user code imports only from `@internal/<target>`" convention applies to *your* own modules (queries, runtime setup, contract authoring). The framework-rendered `migration.ts` scaffold is the framework's surface, not yours; the rule is suspended for that one file.
|
|
64
64
|
|
|
65
65
|
## Diagnostic codes you route on
|
|
66
66
|
|
|
67
67
|
| Code | Source | Move |
|
|
68
68
|
|---|---|---|
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
69
|
+
| `MIGRATION.UNFILLED_PLACEHOLDER` | Throwing `placeholder(...)` at emit time | Open `migration.ts`, replace the `placeholder("<slot>")` call named by `meta.slot` with the real query closure, self-emit. |
|
|
70
|
+
| `MIGRATION.FILE_MISSING` | Reading a migration package | `migration.ts`, `migration.json`, or `ops.json` is absent. Recover from version control, re-emit via `migration.ts`, or run `prisma migration new` for a fresh one. |
|
|
71
|
+
| `MIGRATION.INVALID_DEFAULT_EXPORT` | Loading `migration.ts` | The file's default export is not a `Migration` subclass or factory function. Restore the planner-emitted scaffold from version control or re-run `migration plan` for a clean package. |
|
|
72
|
+
| `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH` | Building a data-transform query plan | The query builder was instantiated with a contract reference different from the `endContract` passed to `this.dataTransform(...)`. Use the `endContract` imported at module scope for both. |
|
|
73
73
|
| `MIGRATION.HASH_MISMATCH` *Migration package is corrupt* | `db migrate` (or any read of the package) | `ops.json` / `migration.json` were edited without self-emitting. Run `node migrations/app/<dir>/migration.ts` to re-emit, then re-run `db migrate`. |
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
74
|
+
| `MIGRATION.DESTRUCTIVE_CHANGES` | `db update` run non-interactively without consent | Re-run with `--confirm <database>` (the database name from the connection), or `--dry-run` to preview. |
|
|
75
|
+
| `CONTRACT.MARKER_MISMATCH` | `db verify` (finding, exit 4) | The marker disagrees with the contract hash (**Postgres:** `prisma_contract.marker`; **Mongo:** `_prisma_migrations`). The DB is at a different contract version than the code thinks. Either run a migration forward, or — if the DB is correct and the marker is stale after a manual fix-up — run `db sign`. |
|
|
76
|
+
| `CONTRACT.MARKER_MISSING` | `db verify` (finding, exit 4), runtime startup (warning) | The DB has no marker yet. Run `prisma db init --db <url>` to baseline an empty database, or `db update --db <url>` to apply the current contract directly. |
|
|
76
77
|
|
|
77
78
|
## Decision — which path do you take?
|
|
78
79
|
|
|
@@ -143,7 +144,7 @@ Canonical detail: [Migration System § Contract resolution through the snapshot
|
|
|
143
144
|
|
|
144
145
|
## Workflow — `db update` (quick path)
|
|
145
146
|
|
|
146
|
-
The concept: `db update` resolves the destination (`emitted contract`) against the live DB and applies the difference. Preview with `--dry-run`. Destructive ops
|
|
147
|
+
The concept: `db update` resolves the destination (`emitted contract`) against the live DB and applies the difference. Preview with `--dry-run`. Destructive ops need consent: interactively the command asks you to type the database name; with `--no-interactive` (CI) it reads `--confirm <database>` instead, and refuses with `MIGRATION.DESTRUCTIVE_CHANGES` if neither is given. `--yes` accepts prompt defaults and never grants this consent. The path excludes operations of the `data` class entirely — if the diff requires a data transform, `db update` fails with a planning error and you switch to `migration plan` to author the transform.
|
|
147
148
|
|
|
148
149
|
Run after a contract edit:
|
|
149
150
|
|
|
@@ -167,7 +168,7 @@ The JSON contains `plan.operations[]` with each `operationClass`, plus (in apply
|
|
|
167
168
|
|
|
168
169
|
## Workflow — `migration plan` + `db migrate` (formal path)
|
|
169
170
|
|
|
170
|
-
The concept: `migration plan` writes a new migration package on disk. If the planner needed any data transforms, the package is *pending* — `migration.ts` holds `placeholder(...)` calls until you fill them in. `db migrate` runs every pending package in graph order
|
|
171
|
+
The concept: `migration plan` writes a new migration package on disk. If the planner needed any data transforms, the package is *pending* — `migration.ts` holds `placeholder(...)` calls until you fill them in. `db migrate` runs every pending package in graph order — on Postgres inside one transaction for the whole run; on Mongo op by op with verify-gated marker advancement (see *Apply atomicity* above).
|
|
171
172
|
|
|
172
173
|
Plan a change:
|
|
173
174
|
|
|
@@ -183,11 +184,11 @@ Read the result. The JSON shape exposes the queryable signals:
|
|
|
183
184
|
- `operations[].operationClass` — for spotting `destructive` and `data` ops.
|
|
184
185
|
- `preview.statements` — family-agnostic textual preview.
|
|
185
186
|
|
|
186
|
-
Inspect the package:
|
|
187
|
+
Inspect the package (the `<target>` positional is required — a directory name, hash or hash prefix, ref, or path):
|
|
187
188
|
|
|
188
189
|
```bash
|
|
189
|
-
pnpm prisma migration show
|
|
190
190
|
pnpm prisma migration show <dirName-or-migrationHash-prefix>
|
|
191
|
+
pnpm prisma migration show migrations/app/20260515T1200_add_user_email
|
|
191
192
|
```
|
|
192
193
|
|
|
193
194
|
`migration show` displays a single migration package. To see the ordered list of migrations that would run — across all contract spaces — use `db migrate --show`:
|
|
@@ -222,22 +223,28 @@ The scaffold the planner emits looks like:
|
|
|
222
223
|
|
|
223
224
|
```typescript
|
|
224
225
|
// migrations/app/20260515T1200_add_user_name/migration.ts
|
|
226
|
+
import { col, Migration, MigrationCLI, placeholder } from '@internal/postgres/migration';
|
|
227
|
+
import type { Contract as End } from '../../snapshots/93f07d1b…c9e1e5a2/contract';
|
|
225
228
|
import endContract from '../../snapshots/93f07d1b…c9e1e5a2/contract.json' with { type: 'json' };
|
|
226
|
-
import {
|
|
229
|
+
import type { Contract as Start } from '../../snapshots/f62a4154…d07dddc/contract';
|
|
230
|
+
import startContract from '../../snapshots/f62a4154…d07dddc/contract.json' with { type: 'json' };
|
|
231
|
+
|
|
232
|
+
export default class M extends Migration<Start, End> {
|
|
233
|
+
override readonly startContractJson = startContract;
|
|
234
|
+
override readonly endContractJson = endContract;
|
|
227
235
|
|
|
228
|
-
export default class M extends Migration {
|
|
229
236
|
override get operations() {
|
|
230
237
|
return [
|
|
231
|
-
addColumn(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
nullable: true,
|
|
238
|
+
this.addColumn({
|
|
239
|
+
schema: 'public',
|
|
240
|
+
table: 'user',
|
|
241
|
+
column: col('name', 'text', { codecRef: { codecId: 'pg/text@1' } }),
|
|
236
242
|
}),
|
|
237
|
-
this.dataTransform(endContract, 'backfill
|
|
238
|
-
check: () => placeholder('backfill
|
|
239
|
-
run:
|
|
243
|
+
this.dataTransform(endContract, 'backfill-user-name', {
|
|
244
|
+
check: () => placeholder('backfill-user-name:check'),
|
|
245
|
+
run: () => placeholder('backfill-user-name:run'),
|
|
240
246
|
}),
|
|
247
|
+
this.setNotNull({ schema: 'public', table: 'user', column: 'name' }),
|
|
241
248
|
];
|
|
242
249
|
}
|
|
243
250
|
}
|
|
@@ -245,29 +252,38 @@ export default class M extends Migration {
|
|
|
245
252
|
MigrationCLI.run(import.meta.url, M);
|
|
246
253
|
```
|
|
247
254
|
|
|
255
|
+
(`examples/prisma-8-demo/migrations/app/20260810T1108_add_post_engagement_counters/migration.ts` is a committed rendered package to compare against.)
|
|
256
|
+
|
|
248
257
|
Replace both `placeholder(...)` calls with query-plan closures built from `endContract`. The `check` closure must return a **rowset query whose presence of any row signals "work remains"** — conventionally `<table>.select('id').where(<violation predicate>).limit(1)`. Scalar/aggregate shapes (`count(*)`, `bool_and(...)`) silently break the contract: the runner wraps `check` twice (`EXISTS(...)` for precheck, `NOT EXISTS(...)` for postcheck), and a query that always returns one row makes `EXISTS` always true and `NOT EXISTS` always false.
|
|
249
258
|
|
|
250
|
-
Build the query builder against `endContract` so the storage hashes line up — using a different contract reference raises `
|
|
259
|
+
Build the query builder against `endContract` so the storage hashes line up — using a different contract reference raises `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH`. The cheapest way to get a typed SQL builder over the end contract is the façade itself: `postgres<End>({ contractJson: endContract })` connects lazily, so constructing it inside `migration.ts` opens no connection; its `sql` is the builder and its `contract` is the validated contract to hand to `this.dataTransform`. The filled-in shape is the rendered scaffold above with only the two `placeholder(...)` arrows replaced (the operation list, including the `this.setNotNull({...})` the planner rendered after the transform, stays as rendered):
|
|
251
260
|
|
|
252
261
|
```typescript
|
|
262
|
+
import { col, Migration, MigrationCLI } from '@internal/postgres/migration';
|
|
263
|
+
import postgres from '@internal/postgres/runtime';
|
|
264
|
+
import type { Contract as End } from '../../snapshots/93f07d1b…c9e1e5a2/contract';
|
|
253
265
|
import endContract from '../../snapshots/93f07d1b…c9e1e5a2/contract.json' with { type: 'json' };
|
|
254
|
-
import {
|
|
255
|
-
import
|
|
266
|
+
import type { Contract as Start } from '../../snapshots/f62a4154…d07dddc/contract';
|
|
267
|
+
import startContract from '../../snapshots/f62a4154…d07dddc/contract.json' with { type: 'json' };
|
|
268
|
+
|
|
269
|
+
const { sql: db, contract } = postgres<End>({ contractJson: endContract });
|
|
270
|
+
|
|
271
|
+
export default class M extends Migration<Start, End> {
|
|
272
|
+
override readonly startContractJson = startContract;
|
|
273
|
+
override readonly endContractJson = endContract;
|
|
256
274
|
|
|
257
|
-
export default class M extends Migration {
|
|
258
275
|
override get operations() {
|
|
259
276
|
return [
|
|
260
|
-
addColumn(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
nullable: true,
|
|
277
|
+
this.addColumn({
|
|
278
|
+
schema: 'public',
|
|
279
|
+
table: 'user',
|
|
280
|
+
column: col('name', 'text', { codecRef: { codecId: 'pg/text@1' } }),
|
|
265
281
|
}),
|
|
266
|
-
this.dataTransform(
|
|
267
|
-
check: () => db.
|
|
268
|
-
run:
|
|
282
|
+
this.dataTransform(contract, 'backfill-user-name', {
|
|
283
|
+
check: () => db.public.user.select('id').where((f, fns) => fns.eq(f.name, null)).limit(1),
|
|
284
|
+
run: () => db.public.user.update({ name: '' }).where((f, fns) => fns.eq(f.name, null)),
|
|
269
285
|
}),
|
|
270
|
-
setNotNull('public', 'user', 'name'),
|
|
286
|
+
this.setNotNull({ schema: 'public', table: 'user', column: 'name' }),
|
|
271
287
|
];
|
|
272
288
|
}
|
|
273
289
|
}
|
|
@@ -285,17 +301,15 @@ Self-emit regenerates `ops.json` and recomputes `migrationHash` in `migration.js
|
|
|
285
301
|
|
|
286
302
|
### Mongo
|
|
287
303
|
|
|
288
|
-
Mongo `dataTransform` operations
|
|
304
|
+
Mongo `dataTransform` operations are free factories taking `{ check, run }` objects whose `source` / `run` return Mongo query-plan shapes (often `RawAggregateCommand` / `RawUpdateManyCommand` from `@prisma/orm-mongo/query-ast/execution`). The planner may leave `placeholder(...)` inside those sources until you fill them. A rendered package binds its bookends through `startContractJson` / `endContractJson` exactly as on Postgres; a hand-authored `migration new` package may instead override `describe()` with the `from` / `to` hashes from its `migration.json`, as below. Everything comes from one import:
|
|
289
305
|
|
|
290
306
|
```typescript
|
|
291
|
-
import { MigrationCLI } from '@
|
|
292
|
-
import {
|
|
293
|
-
import { createIndex, dataTransform } from '@internal/target-mongo/migration';
|
|
294
|
-
import { RawAggregateCommand, RawUpdateManyCommand } from '@internal/mongo-query-ast/execution';
|
|
307
|
+
import { createIndex, dataTransform, Migration, MigrationCLI } from '@prisma/orm-mongo/target/migration';
|
|
308
|
+
import { RawAggregateCommand, RawUpdateManyCommand } from '@prisma/orm-mongo/query-ast/execution';
|
|
295
309
|
|
|
296
310
|
class M extends Migration {
|
|
297
311
|
override describe() {
|
|
298
|
-
return { from: '<hex>', to: '<hex>'
|
|
312
|
+
return { from: '<hex>', to: '<hex>' };
|
|
299
313
|
}
|
|
300
314
|
|
|
301
315
|
override get operations() {
|
|
@@ -340,20 +354,21 @@ The concept: the same `Migration` class shape lets you author operations directl
|
|
|
340
354
|
pnpm prisma migration new --name <snake_slug>
|
|
341
355
|
```
|
|
342
356
|
|
|
343
|
-
|
|
357
|
+
On Postgres the operations are **methods on the `Migration` base class**, each taking one options object (`this.addColumn({ schema, table, column })`); only helpers such as `col(...)` and `rawSql(...)` are imported, on the rendered `@internal/postgres/migration` line. On Mongo the operations are **free factories** imported from `@prisma/orm-mongo/target/migration`. The authoritative list for either target is the base class / module's declaration file in your `node_modules`.
|
|
344
358
|
|
|
345
|
-
**Postgres**
|
|
359
|
+
**Postgres** operations (representative set, all `this.<name>({...})`):
|
|
346
360
|
|
|
347
361
|
- Tables: `createTable`, `dropTable`.
|
|
348
|
-
- Columns: `addColumn
|
|
349
|
-
- Constraints: `addPrimaryKey`, `addForeignKey`, `addUnique`, `dropConstraint`.
|
|
350
|
-
- Indexes: `createIndex`, `dropIndex`.
|
|
351
|
-
- Enums: `
|
|
352
|
-
-
|
|
353
|
-
-
|
|
354
|
-
-
|
|
362
|
+
- Columns: `addColumn` (`column: col(name, nativeType, { codecRef })`), `dropColumn`, `alterColumnType`, `setNotNull`, `dropNotNull`, `setDefault`, `dropDefault`.
|
|
363
|
+
- Constraints: `addPrimaryKey`, `addForeignKey`, `addUnique`, `addCheckConstraint`, `renameCheckConstraint`, `dropCheckConstraint`, `dropConstraint`.
|
|
364
|
+
- Indexes: `createIndex`, `renameIndex`, `dropIndex`.
|
|
365
|
+
- Enums: `createNativeEnumType`, `addNativeEnumValue`, `dropNativeEnumType`.
|
|
366
|
+
- Row-level security: `enableRowLevelSecurity`, `disableRowLevelSecurity`, `createRlsPolicy`, `renameRlsPolicy`, `dropRlsPolicy`.
|
|
367
|
+
- Dependencies: `createSchema`, `installExtension`.
|
|
368
|
+
- Free helpers on the import line: `col`, `primaryKey`, `unique`, `foreignKey`, `checkExpression`, `lit`, `fn` (column and constraint descriptors), `createExtension`, and the raw escape hatch `rawSql({ id, label, operationClass, target, precheck, execute, postcheck, ... })`.
|
|
369
|
+
- Data transforms: `this.dataTransform(endContract, name, { check, run })`.
|
|
355
370
|
|
|
356
|
-
**Mongo** factories (from `@
|
|
371
|
+
**Mongo** factories (from `@prisma/orm-mongo/target/migration`):
|
|
357
372
|
|
|
358
373
|
- Collections: `createCollection`, `dropCollection`, `validatedCollection`, `setValidation`.
|
|
359
374
|
- Indexes: `createIndex`, `dropIndex`.
|
|
@@ -380,7 +395,7 @@ The concept: `db verify` is a **standalone diagnostic** — not a routine step a
|
|
|
380
395
|
- Following manual SQL or ad-hoc edits outside Prisma 8.
|
|
381
396
|
- When restoring a database from backup.
|
|
382
397
|
- If a `db migrate` fails or partially applies (especially on Mongo, where DDL is resumable rather than transaction-wrapped).
|
|
383
|
-
- When `
|
|
398
|
+
- When `CONTRACT.MARKER_MISMATCH` / `CONTRACT.MARKER_MISSING` surfaces at runtime or from another command.
|
|
384
399
|
|
|
385
400
|
Modes:
|
|
386
401
|
|
|
@@ -393,7 +408,7 @@ Modes:
|
|
|
393
408
|
pnpm prisma db verify --db $DATABASE_URL
|
|
394
409
|
```
|
|
395
410
|
|
|
396
|
-
|
|
411
|
+
`db verify` exits `0` when everything matches, `4` when it ran and found something, and `2` only when it could not run. Findings ride the completed envelope as `error` diagnostics: `CONTRACT.MARKER_MISMATCH`, `CONTRACT.MARKER_MISSING`, `CONTRACT.TARGET_MISMATCH`, `CONTRACT.SCHEMA_VERIFICATION_FAILED` (with `meta.issues` naming the drifted paths).
|
|
397
412
|
|
|
398
413
|
## Workflow — Re-sign the marker
|
|
399
414
|
|
|
@@ -421,9 +436,9 @@ Use `db verify` to confirm which side is wrong, then re-run it after either bran
|
|
|
421
436
|
|
|
422
437
|
## Workflow — Recover from a partially-applied migration
|
|
423
438
|
|
|
424
|
-
The concept: on **Postgres**,
|
|
439
|
+
The concept: on **Postgres**, the whole `db migrate` run is one transaction — a failure anywhere rolls back every migration the run had applied, and the marker stays where it was before the command. On **Mongo**, DDL is resumable with verify-gated marker advancement; diagnose with `db verify` / `db schema`, fix the failed package's `migration.ts`, self-emit, and re-run `db migrate`.
|
|
425
440
|
|
|
426
|
-
Failures that *can* leak partial state
|
|
441
|
+
Failures that *can* leak partial state: Mongo DDL that partially applied before verify failed, and external side-effects (calls out to other systems from a `run` closure). On Postgres nothing runs outside the transaction — `rawSql(...)` steps are ordinary steps inside it and roll back with the rest.
|
|
427
442
|
|
|
428
443
|
Diagnose:
|
|
429
444
|
|
|
@@ -467,20 +482,21 @@ Routing:
|
|
|
467
482
|
- Re-shape the migration via `migration plan` and hand-edit `migration.ts` to preserve the data (e.g. copy-to-new-column, then drop), or
|
|
468
483
|
- Skip the destructive operation by reverting the contract change.
|
|
469
484
|
|
|
470
|
-
In non-interactive contexts (CI, `--no-interactive
|
|
485
|
+
Interactively, consent is typing the database name back (the prompt names it). In non-interactive contexts (CI, `--no-interactive`), the destructive-op response is returned as `MIGRATION.DESTRUCTIVE_CHANGES` — `meta.destructiveOperations[]` lists what would have been dropped. Re-run with `--confirm <database>` to grant consent (`--yes` does not), or address each operation individually.
|
|
471
486
|
|
|
472
487
|
## Common Pitfalls
|
|
473
488
|
|
|
474
489
|
1. **Using `db update` against shared or production databases.** Never. The change leaves no migration history. Use `migration plan` + `db migrate`.
|
|
475
|
-
2. **Skipping a data transform.** Leaving `placeholder(...)` in `migration.ts` makes the next `db migrate` throw `
|
|
490
|
+
2. **Skipping a data transform.** Leaving `placeholder(...)` in `migration.ts` makes the next `db migrate` throw `MIGRATION.UNFILLED_PLACEHOLDER`. Fill every placeholder slot and self-emit.
|
|
476
491
|
3. **Editing `ops.json` directly.** It's the canonical artifact, not the authoring source. Edit `migration.ts`, then self-emit.
|
|
477
492
|
4. **Forgetting to self-emit after editing `migration.ts`.** The next `db migrate` either uses the stale `ops.json` (if you only added comments) or fails with `MIGRATION.HASH_MISMATCH` (if you changed operations). Always self-emit.
|
|
478
493
|
5. **Routine `db verify` after a successful `db update` or `db migrate`.** Redundant on the happy path — reserve `db verify` for drift diagnosis (manual edits, restore, failed `db migrate`).
|
|
479
494
|
6. **Aggregate `check` closure in Postgres `this.dataTransform`.** Returning `count(*)` or `bool_and(...)` breaks the precheck/postcheck contract — both sides resolve to constants. Use a rowset shape: `select('id').where(<violation>).limit(1)`.
|
|
480
|
-
7. **Two contract references in one migration.** Building a query plan against a different contract than the one passed to `this.dataTransform(endContract, ...)` raises `
|
|
495
|
+
7. **Two contract references in one migration.** Building a query plan against a different contract than the one passed to `this.dataTransform(endContract, ...)` raises `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH`. Always import `endContract` once at module scope and use the same reference.
|
|
496
|
+
11. **Calling Postgres operations as free functions.** `addColumn('public', 'user', {...})` does not exist as an import; the operations are `this.addColumn({ schema, table, column })` and friends on the `Migration` base class, with `col(...)` building the column. Only `col`, `rawSql`, `placeholder`, `Migration`, and `MigrationCLI` are imported.
|
|
481
497
|
8. **Renaming and expecting the planner to detect it (Postgres).** Prisma 8 has no in-contract rename hint today; the planner emits a destructive drop+add. Hand-edit `migration.ts` to rewrite the destructive op as a `rawSql({ ... })` that issues `ALTER TABLE ... RENAME COLUMN ...` (or use the two-migration keep / backfill / drop pattern), then self-emit. See `references/contract.md` § *Edit a field — rename*.
|
|
482
498
|
9. **Planning with no `db` ref and no `--from` in a project that already has migrations.** The origin falls through to the empty database, which would make the plan a full-create migration; `migration plan` refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN` rather than writing it. Pick the exit that matches your intent — the error lists them, and `references/migration-model.md` § *The trap* explains which to choose.
|
|
483
|
-
10. **Hand-authoring `migration.ts` from a blank file, or rewriting the rendered import line.** Migration files are framework-rendered — let `prisma migration plan` (or `migration new`) render the package, then edit only the holes the framework leaves for you. On Postgres leave the rendered `@internal/postgres/migration` (or `@internal/sqlite/migration`) import path alone; on Mongo
|
|
499
|
+
10. **Hand-authoring `migration.ts` from a blank file, or rewriting the rendered import line.** Migration files are framework-rendered — let `prisma migration plan` (or `migration new`) render the package, then edit only the holes the framework leaves for you. On Postgres leave the rendered `@internal/postgres/migration` (or `@internal/sqlite/migration`) import path alone; on Mongo leave `@prisma/orm-mongo/target/migration` as rendered. Add symbols to the existing import line rather than introducing new import paths.
|
|
484
500
|
|
|
485
501
|
## What Prisma 8 doesn't do yet
|
|
486
502
|
|
|
@@ -511,7 +527,7 @@ The CLI collects anonymous usage data by default. To opt out, set `PRISMA_NEXT_D
|
|
|
511
527
|
- [ ] Contract emitted (`contract.json` + `contract.d.ts` current).
|
|
512
528
|
- [ ] Chose the right path: `db update` (local dev) vs `migration plan` + `db migrate` (anything shared).
|
|
513
529
|
- [ ] For `migration plan`: confirmed the output's `from:` line names the intended origin — not `(baseline)` over an existing graph (`references/migration-model.md`).
|
|
514
|
-
- [ ] For `migration plan`: ran `migration show
|
|
530
|
+
- [ ] For `migration plan`: ran `migration show <dir>` to review before `db migrate`.
|
|
515
531
|
- [ ] Filled every `placeholder(...)` in `migration.ts` (if any), built against `endContract`.
|
|
516
532
|
- [ ] `check` closures are rowset queries, not scalar aggregates.
|
|
517
533
|
- [ ] Self-emitted (`node migrations/app/<dir>/migration.ts`) after editing the TS.
|
|
@@ -519,4 +535,4 @@ The CLI collects anonymous usage data by default. To opt out, set `PRISMA_NEXT_D
|
|
|
519
535
|
- [ ] Used `db verify` only when diagnosing drift — not as a routine post-apply step.
|
|
520
536
|
- [ ] Did NOT use `db update` against a shared or production database.
|
|
521
537
|
- [ ] Did NOT edit `ops.json` directly.
|
|
522
|
-
- [ ] Did NOT skip a destructive-op prompt without inspecting `meta.destructiveOperations[]`.
|
|
538
|
+
- [ ] Did NOT skip a destructive-op prompt without inspecting `meta.destructiveOperations[]`; granted consent with the database name (or `--confirm <database>`), not `--yes`.
|
|
@@ -9,7 +9,7 @@ Shared concepts (result consumption, script teardown, cross-target pitfalls, cap
|
|
|
9
9
|
**Mongo** (`mongo<Contract>(...)` from `@internal/mongo/runtime`):
|
|
10
10
|
|
|
11
11
|
- **`db.orm.<root>`** — ORM, lowercased plural contract root (`db.orm.users`, `db.orm.posts`). Same fluent chaining; `.where({ field: value })` object equality is the idiomatic filter form.
|
|
12
|
-
- **`db.query`** — typed aggregation-pipeline builder. Start with `db.query.from('<root>')`, chain `.match(...)` / `.project(...)` / `.group(...)` / `.lookup(...)`, terminal with `.build()`.
|
|
12
|
+
- **`db.query`** — typed aggregation-pipeline builder. Start with `db.query.from('<root>')`, chain `.match(...)` / `.project(...)` / `.group(...)` / `.lookup(...)`, terminal with `.build()`. Run via `(await db.runtime()).query(plan)` for anything that returns documents; `execute(plan)` is only for a write you want an affected count from, and it throws `RUNTIME.MONGO_STATISTICS_UNSUPPORTED` on a find or aggregate.
|
|
13
13
|
|
|
14
14
|
Reach for the ORM first; drop to `db.query` when the ORM can't express the shape. Lane choice is local — one query function picks one lane, not the whole app.
|
|
15
15
|
|
|
@@ -49,7 +49,7 @@ const recent = await db.orm.posts
|
|
|
49
49
|
|
|
50
50
|
**`.where(...)`** accepts a plain object whose keys are model field names and values are compared with equality (codec-aware — `ObjectId` fields accept string ids from the contract). Chain multiple `.where({ ... })` calls to AND-compose filters.
|
|
51
51
|
|
|
52
|
-
For operators the object form doesn't cover (`.in([...])`, range comparisons, nested logic), pass a `MongoFilterExpr` — today that means importing filter helpers from `@
|
|
52
|
+
For operators the object form doesn't cover (`.in([...])`, range comparisons, nested logic), pass a `MongoFilterExpr` — today that means importing filter helpers from `@prisma/orm-mongo/query-ast/execution` (a façade-completeness gap; see *What Prisma 8 doesn't do yet* in [`queries.md`](./queries.md)). Prefer the object form whenever equality suffices.
|
|
53
53
|
|
|
54
54
|
**Polymorphic roots.** When the contract declares variants on a model, narrow before querying:
|
|
55
55
|
|
|
@@ -123,7 +123,7 @@ await db.orm.users.where({ email: 'alice@example.com' }).upsert({
|
|
|
123
123
|
The Mongo ORM does not expose `.aggregate(...)` / `.groupBy(...)`. Express aggregations through **`db.query`** — the pipeline builder — with `.group(...)` and accumulator helpers:
|
|
124
124
|
|
|
125
125
|
```typescript
|
|
126
|
-
import { acc } from '@
|
|
126
|
+
import { acc } from '@prisma/orm-mongo/query-builder';
|
|
127
127
|
|
|
128
128
|
const runtime = await db.runtime();
|
|
129
129
|
const plan = db.query
|
|
@@ -137,18 +137,18 @@ const plan = db.query
|
|
|
137
137
|
.sort({ postCount: -1 })
|
|
138
138
|
.build();
|
|
139
139
|
|
|
140
|
-
const byKind = await runtime.
|
|
140
|
+
const byKind = await runtime.query(plan);
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
Import `acc` and expression helpers (`fn`) from `@
|
|
143
|
+
Import `acc` and expression helpers (`fn`) from `@prisma/orm-mongo/query-builder` when building computed pipeline stages (as `examples/mongo-demo/src/server.ts` does).
|
|
144
144
|
|
|
145
145
|
## Workflow — Query builder (`db.query`)
|
|
146
146
|
|
|
147
|
-
The concept: `db.query.from('<root>')` starts a typed aggregation-pipeline chain. Terminal methods produce a `MongoQueryPlan`;
|
|
147
|
+
The concept: `db.query.from('<root>')` starts a typed aggregation-pipeline chain. Terminal methods produce a `MongoQueryPlan`; run it through the runtime with `query(plan)` (an `AsyncIterableResult` of documents — `await` it for an array):
|
|
148
148
|
|
|
149
149
|
```typescript
|
|
150
150
|
// src/queries/analytics.ts
|
|
151
|
-
import { acc, fn } from '@
|
|
151
|
+
import { acc, fn } from '@prisma/orm-mongo/query-builder';
|
|
152
152
|
import { db } from '../prisma/db';
|
|
153
153
|
|
|
154
154
|
const runtime = await db.runtime();
|
|
@@ -161,7 +161,7 @@ const plan = db.query
|
|
|
161
161
|
.limit(10)
|
|
162
162
|
.project('title', 'authorId', 'createdAt')
|
|
163
163
|
.build();
|
|
164
|
-
const recent = await runtime.
|
|
164
|
+
const recent = await runtime.query(plan);
|
|
165
165
|
|
|
166
166
|
// Cross-collection join ($lookup).
|
|
167
167
|
const withAuthor = db.query
|
|
@@ -175,26 +175,26 @@ const withAuthor = db.query
|
|
|
175
175
|
.as('author'),
|
|
176
176
|
)
|
|
177
177
|
.build();
|
|
178
|
-
const rows = await runtime.
|
|
178
|
+
const rows = await runtime.query(withAuthor);
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
**Filters — `.match(...)`.** Callback form: `.match((f) => f.status.eq('active'))`. Filters AND-compose across chained `.match(...)` calls. Field accessors support property access (`f.email`), callable dot paths (`f('address.city').eq('NYC')`), and `f.rawPath('path')` for migration/backfill paths outside the current contract.
|
|
182
182
|
|
|
183
|
-
**Write terminals on the builder.** After `.from('users')` or `.from('users').match(...)`, use insert/update/delete terminals:
|
|
183
|
+
**Write terminals on the builder.** After `.from('users')` or `.from('users').match(...)`, use insert/update/delete terminals. Write plans run through `query(plan)` too (one result row carrying the driver's response — the inserted ids, or the document `findOneAndUpdate` returns); reach for `execute(plan)` only on an `update*` / `delete*` plan when all you want is the affected count (any other command kind throws `RUNTIME.MONGO_STATISTICS_UNSUPPORTED`):
|
|
184
184
|
|
|
185
185
|
```typescript
|
|
186
|
-
await runtime.
|
|
186
|
+
const inserted = await runtime.query(
|
|
187
187
|
db.query.from('users').insertOne({ name: 'Alice', email: 'a@e.com', bio: null }),
|
|
188
188
|
);
|
|
189
189
|
|
|
190
|
-
await runtime.execute(
|
|
190
|
+
const { affectedRows } = await runtime.execute(
|
|
191
191
|
db.query
|
|
192
192
|
.from('users')
|
|
193
193
|
.match((f) => f.name.eq('Alice'))
|
|
194
194
|
.updateMany((f) => [f.bio.set('filled')]),
|
|
195
195
|
);
|
|
196
196
|
|
|
197
|
-
await runtime.
|
|
197
|
+
const [updated] = await runtime.query(
|
|
198
198
|
db.query
|
|
199
199
|
.from('users')
|
|
200
200
|
.match((f) => f.email.eq('a@e.com'))
|
|
@@ -204,7 +204,7 @@ await runtime.execute(
|
|
|
204
204
|
|
|
205
205
|
Update callbacks return arrays of field operations (`.set`, `.inc`, `.push`, `.pull`, …). Pipeline-style updates use `f.stage.set(...)` inside an aggregation chain, then `.updateMany()` with no callback.
|
|
206
206
|
|
|
207
|
-
**Plans vs ORM.** The ORM's `.create` / `.update` / `.all` issue queries directly. Don't pass ORM collections to `runtime.execute` —
|
|
207
|
+
**Plans vs ORM.** The ORM's `.create` / `.update` / `.all` issue queries directly. Don't pass ORM collections to `runtime.query` / `runtime.execute` — those entry points are for `db.query` plans (and migration/runtime internals).
|
|
208
208
|
|
|
209
209
|
## Common Pitfalls (Mongo)
|
|
210
210
|
|
|
@@ -215,7 +215,7 @@ Update callbacks return arrays of field operations (`.set`, `.inc`, `.push`, `.p
|
|
|
215
215
|
5. **Expecting Postgres-style lambda `.where((u) => u.email.eq(...))` on ORM.** Prefer object equality `.where({ email: '...' })`; richer operators need `MongoFilterExpr` helpers (façade gap today).
|
|
216
216
|
6. **Expecting `db.transaction(...)`.** The Mongo façade does not expose it today. Multi-document atomicity requires MongoDB transactions on a replica set via the driver — not yet wrapped in the Prisma 8 façade. Route to *What Prisma 8 doesn't do yet* / `references/feedback.md` if the user needs this.
|
|
217
217
|
7. **Trying to use `db.sql`.** There is no `db.sql` on Mongo.
|
|
218
|
-
8. **Trying to `db.execute(plan)` directly
|
|
218
|
+
8. **Trying to `db.execute(plan)` directly, or reading documents with `execute`.** Run query-builder plans via `(await db.runtime()).query(plan)`. `execute(plan)` resolves statistics only and throws `RUNTIME.MONGO_STATISTICS_UNSUPPORTED` for a find or aggregate.
|
|
219
219
|
9. **Expecting ORM `.aggregate(...)` / `.groupBy(...)`.** Use `db.query.from(...).group(...).build()` instead.
|
|
220
220
|
|
|
221
221
|
## Reference Files
|
|
@@ -230,7 +230,7 @@ Update callbacks return arrays of field operations (`.set`, `.inc`, `.push`, `.p
|
|
|
230
230
|
- [ ] Used lowercased plural ORM roots (`db.orm.users`, not `db.orm.User`).
|
|
231
231
|
- [ ] Chose the right lane (ORM by default; `db.query` for shapes the ORM doesn't express).
|
|
232
232
|
- [ ] Used `.where({ ... }).first()` for single-row reads — not `.all()`.
|
|
233
|
-
- [ ]
|
|
233
|
+
- [ ] Ran query-builder plans via `(await db.runtime()).query(plan)`; used `execute(plan)` only for an affected count on a write.
|
|
234
234
|
- [ ] For aggregations, used `db.query.from(...).group(...)` rather than a non-existent ORM `.aggregate(...)`.
|
|
235
235
|
- [ ] Did NOT confabulate `db.transaction`, `db.sql`, or ORM `.aggregate(...)` — routed to *What Prisma 8 doesn't do yet* / `references/feedback.md` instead.
|
|
236
236
|
- [ ] Did NOT use the lower-level builder for something the ORM cleanly expresses.
|