@prisma/orm-sqlite 8.0.0-rc.11-dev.55 → 8.0.0-rc.12-dev.1
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 +1 -1
- package/skills/prisma-8/references/migration-model.md +1 -1
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md +639 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/scripts/multifile-psl/add-use-prisma-8-directive.mjs +60 -0
- package/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/scripts/psl-verbatim-table-names/add-model-map.mjs +256 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md +857 -0
- package/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/scripts/psl-verbatim-table-names/add-model-map.mjs +256 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
---
|
|
2
|
+
from: "8.0.0-rc.11"
|
|
3
|
+
to: "8.0.0-rc.12"
|
|
4
|
+
# The Prisma 7 contract source adds `prisma7Schema` and the `examples/prisma7-adoption` example.
|
|
5
|
+
# The surface itself is new, so there is nothing to translate for it; the entries below cover the
|
|
6
|
+
# changes it made to paths every Postgres project already uses.
|
|
7
|
+
# contract.d.ts now orders every collection the way contract.json does; a re-emit reorders, nothing else.
|
|
8
|
+
changes:
|
|
9
|
+
- id: define-config-becomes-define-prisma-config
|
|
10
|
+
summary: |
|
|
11
|
+
Rename the engine config marker import in `prisma.config.ts` from `defineConfig` to
|
|
12
|
+
`definePrismaConfig`. Required: `@prisma/cli-engine@0.6.1` no longer exports the deprecated
|
|
13
|
+
`defineConfig` alias.
|
|
14
|
+
detection:
|
|
15
|
+
glob: "**/prisma.config.ts"
|
|
16
|
+
contains:
|
|
17
|
+
- "import { defineConfig } from '@prisma/cli-engine'"
|
|
18
|
+
- id: engine-pin-moves-to-0-6-1
|
|
19
|
+
summary: |
|
|
20
|
+
The toolchain now requires `@prisma/cli-engine@0.6.1` (up from 0.4.0). A project that pins `@prisma/cli-engine` itself must move the pin to `0.6.1`. The engine no longer exports the deprecated `defineConfig` alias, so `prisma.config.ts` must import `definePrismaConfig`.
|
|
21
|
+
detection:
|
|
22
|
+
glob: "**/package.json"
|
|
23
|
+
contains:
|
|
24
|
+
- '"@prisma/cli-engine": "0.4.0"'
|
|
25
|
+
- id: config-paths-resolve-from-declaring-file
|
|
26
|
+
summary: |
|
|
27
|
+
A relative path in `prisma.config.ts` now resolves from the directory of the config file that wrote it, not from the working directory. Commands run from the config file's directory behave as before. The engine also discovers `prisma.config.ts` files up to the repository root and merges them, with the nearest file's values winning.
|
|
28
|
+
- id: psl-schema-requires-use-prisma-8-directive
|
|
29
|
+
summary: |
|
|
30
|
+
`prisma contract emit` now refuses to load a PSL schema file that does not carry
|
|
31
|
+
`// use prisma-8` as its first line, failing with `PSL_NO_OPTED_IN_SCHEMA_FILES`. Add the
|
|
32
|
+
directive to every PSL schema file your `contract.source.inputs` matches.
|
|
33
|
+
detection:
|
|
34
|
+
glob: "**/*.prisma"
|
|
35
|
+
regex:
|
|
36
|
+
- '^(?!\s*// *use +(?:prisma-8|prisma-next)(?: *)(?!\S))'
|
|
37
|
+
script: ./scripts/multifile-psl/add-use-prisma-8-directive.mjs
|
|
38
|
+
- id: psl-model-names-table-verbatim
|
|
39
|
+
summary: |
|
|
40
|
+
A PSL `model` with no `@@map` now names its table (or Mongo collection) after the model
|
|
41
|
+
verbatim: `model UserProfile` maps to `"UserProfile"`, where it used to map to `"userProfile"`.
|
|
42
|
+
Run the colocated codemod over every `.prisma` file, including the `contract.prisma` copies
|
|
43
|
+
under `migrations/`, so each unmapped model gets `@@map("<current table name>")` and keeps the
|
|
44
|
+
table it already has. Emitted contracts, storage hashes, and migration history are unchanged
|
|
45
|
+
after the codemod. Planning without it fails with `MIGRATION.TABLE_NAME_CASE_CHANGED`
|
|
46
|
+
instead of dropping and recreating the table.
|
|
47
|
+
detection:
|
|
48
|
+
glob: "**/*.prisma"
|
|
49
|
+
regex:
|
|
50
|
+
- '\bmodel\s+[A-Za-z_][A-Za-z0-9_]*\s*\{'
|
|
51
|
+
script: ./scripts/psl-verbatim-table-names/add-model-map.mjs
|
|
52
|
+
- id: contract-infer-prints-emittable-defaults
|
|
53
|
+
summary: |
|
|
54
|
+
`contract infer` now prints each column default in the form `contract emit` accepts: an unquoted number with every digit for `Decimal` and `Numeric`, plain digits for a large `BigInt`, and a `sql` tagged literal for a list default holding a `NULL` element.
|
|
55
|
+
- id: infer-prints-a-literal-where-it-printed-dbgenerated
|
|
56
|
+
summary: |
|
|
57
|
+
`prisma contract infer` now prints a default as a literal wherever it can read the literal
|
|
58
|
+
back as the stored value, including forms it used to print as `dbgenerated("...")`. Re-running
|
|
59
|
+
infer produces different schema text for the same database. Nothing to fix; review the diff.
|
|
60
|
+
detection:
|
|
61
|
+
glob: "**/*.prisma"
|
|
62
|
+
contains:
|
|
63
|
+
- "dbgenerated("
|
|
64
|
+
- id: dbgenerated-removed-from-psl
|
|
65
|
+
summary: |
|
|
66
|
+
`@default(dbgenerated("..."))` is removed from PSL. `contract emit` refuses it with
|
|
67
|
+
`PSL_UNKNOWN_DEFAULT_FUNCTION`. Write a raw SQL default as the `sql` tagged literal, and a
|
|
68
|
+
value the column's data type writes as that literal.
|
|
69
|
+
detection:
|
|
70
|
+
glob: "**/*.prisma"
|
|
71
|
+
contains:
|
|
72
|
+
- "dbgenerated("
|
|
73
|
+
- id: default-sql-method-deprecated
|
|
74
|
+
summary: |
|
|
75
|
+
`.defaultSql('...')` on the TypeScript contract builder is deprecated and is removed at
|
|
76
|
+
8.0.0. Rewrite each call to `.default(...)` with `now()`, `autoincrement()`, or the `sql`
|
|
77
|
+
template tag.
|
|
78
|
+
detection:
|
|
79
|
+
glob: "**/*.{ts,mts,cts}"
|
|
80
|
+
matches:
|
|
81
|
+
- '\.defaultSql\('
|
|
82
|
+
- id: a-json-default-is-a-json-tag
|
|
83
|
+
summary: |
|
|
84
|
+
A `Json` or `Jsonb` column's default is written ``@default(json`{ "a": 1 }`)``. A quoted
|
|
85
|
+
string is refused: `pg/jsonb` casts from `pg/json`, not from `pg/text`.
|
|
86
|
+
detection:
|
|
87
|
+
glob: "**/*.prisma"
|
|
88
|
+
matches:
|
|
89
|
+
- '\b(Jsonb|Json)(\[\])?\??([ \t]+@[\w.]+(\([^)\n]*\))?)*?[ \t]+@default\([\s\[]*"'
|
|
90
|
+
- id: a-decimal-default-is-written-unquoted
|
|
91
|
+
summary: |
|
|
92
|
+
A `Decimal` or `Numeric` column's default is written as a number, not as a quoted string:
|
|
93
|
+
`@default(1.50)`. Trailing zeros are kept.
|
|
94
|
+
detection:
|
|
95
|
+
glob: "**/*.prisma"
|
|
96
|
+
matches:
|
|
97
|
+
- '\b(Decimal|Numeric)(\([^)]*\))?(\[\])?\??([ \t]+@[\w.]+(\([^)\n]*\))?)*?[ \t]+@default\([\s\[]*"'
|
|
98
|
+
- id: a-float-non-finite-default-is-written-bare
|
|
99
|
+
summary: |
|
|
100
|
+
A `Float` or `Real` column's default is written as a number, and `NaN`, `Infinity` and
|
|
101
|
+
`-Infinity` are written bare: `@default(NaN)`, not `@default("NaN")`.
|
|
102
|
+
detection:
|
|
103
|
+
glob: "**/*.prisma"
|
|
104
|
+
matches:
|
|
105
|
+
- '\b(Float|Real)(\[\])?\??([ \t]+@[\w.]+(\([^)\n]*\))?)*?[ \t]+@default\([\s\[]*"'
|
|
106
|
+
- id: a-json-list-default-is-one-json-literal
|
|
107
|
+
summary: |
|
|
108
|
+
A written list on a `Json` or `Jsonb` column that holds one value is refused. A JSON list
|
|
109
|
+
default is one JSON document: ``@default(json`[1, 2]`)``.
|
|
110
|
+
detection:
|
|
111
|
+
glob: "**/*.prisma"
|
|
112
|
+
matches:
|
|
113
|
+
- '\b(Jsonb|Json)\??([ \t]+@[\w.]+(\([^)\n]*\))?)*?[ \t]+@default\([ \t]*\['
|
|
114
|
+
- id: psl-number-defaults-keep-digits
|
|
115
|
+
summary: |
|
|
116
|
+
A PSL number `@default` on a `Decimal` or `Numeric` column now emits as decimal text with every digit (`"10"`, `"1.50"`) instead of a JSON number. Re-emitting such a contract changes its storage hash, so re-sign databases signed with the old contract. `BigInt` and `UnboundedInt` defaults beyond 2^53 now emit, and `contract infer` prints such `BigInt` defaults as plain numbers.
|
|
117
|
+
detection:
|
|
118
|
+
glob: "**/*.prisma"
|
|
119
|
+
regex:
|
|
120
|
+
- '@default\(\[?-?(\d|NaN|Infinity)'
|
|
121
|
+
- id: number-valued-64-bit-columns-store-their-default-as-digit-text
|
|
122
|
+
summary: |
|
|
123
|
+
This flags every contract that holds a `pg/int8number@1` or `sqlite/bigintnumber@1` column.
|
|
124
|
+
Only those columns that carry a literal default change form: the default is digit text now,
|
|
125
|
+
where it was a JSON number. A column with no default, or with a function default, is
|
|
126
|
+
unaffected. For an affected contract, re-run `prisma contract emit`, then `prisma db sign`.
|
|
127
|
+
detection:
|
|
128
|
+
glob: "**/contract.json"
|
|
129
|
+
contains:
|
|
130
|
+
- '"codecId": "pg/int8number@1"'
|
|
131
|
+
- '"codecId": "sqlite/bigintnumber@1"'
|
|
132
|
+
- id: client-generated-created-at-presets
|
|
133
|
+
summary: Re-emit contracts using creation timestamp presets and migrate their removed database defaults.
|
|
134
|
+
- id: re-emit-the-contract-for-the-moved-query-operation-types
|
|
135
|
+
summary: |
|
|
136
|
+
Emitted Postgres `contract.d.ts` files import `QueryOperationTypes` from the target package
|
|
137
|
+
instead of the adapter. Run `prisma contract emit` once; an un-emitted contract names a
|
|
138
|
+
subpath that no longer exists and stops type-checking. Application source that imported that
|
|
139
|
+
subpath directly changes the same way.
|
|
140
|
+
detection:
|
|
141
|
+
# Covers the emitted `contract.d.ts` and hand-written source alike: both
|
|
142
|
+
# name the subpath, and both stop compiling until they are changed.
|
|
143
|
+
glob: "**/*.{ts,tsx,mts}"
|
|
144
|
+
contains:
|
|
145
|
+
- "/adapter/operation-types"
|
|
146
|
+
- id: native-enum-columns-have-no-text-operations
|
|
147
|
+
summary: |
|
|
148
|
+
A native Postgres enum column (`pg.enum(...)`) no longer offers `like`, `ilike`,
|
|
149
|
+
`fullTextMatches`, `fullTextRank` or `fullTextHeadline`, is no longer accepted as the text of
|
|
150
|
+
a tsquery parser, and cannot carry `@@fullTextIndex` or `fullTextIndex`. These calls used to
|
|
151
|
+
compile and then fail in Postgres. Now they fail to compile.
|
|
152
|
+
- id: re-emit-for-the-insert-conflict-skip-capabilities
|
|
153
|
+
summary: "The Postgres and SQLite adapters report two new capability keys, sql.insertOnConflictSkip and sql.insertOnConflictWithoutTarget, which the new createAll/createAndCount option { onConflict: 'skip' } requires; a contract emitted before this release does not carry them and the option is refused against it, so re-emit the contract before using it."
|
|
154
|
+
detection:
|
|
155
|
+
glob: "**/contract.json"
|
|
156
|
+
contains:
|
|
157
|
+
- '"defaultInInsert"'
|
|
158
|
+
- id: params-only-sql-facade-prepare
|
|
159
|
+
summary: Replace injected SQL-builder preparation callbacks with params-only callbacks and lexical facade SQL access.
|
|
160
|
+
- id: postgres-target-owned-list-framing
|
|
161
|
+
summary: |
|
|
162
|
+
PostgreSQL list result decoding is target-owned; direct driver reads now expose raw array literals, and fixed-scale numeric arrays return database-normalized decimal text such as `"1.5000000000"`.
|
|
163
|
+
- id: postgres-verify-reads-more-default-spellings
|
|
164
|
+
summary: |
|
|
165
|
+
`db verify` now reads negative and cast numerals, enum literals cast to a type in another schema, zoneless `timestamp` literals, and `ARRAY[...]` list defaults as the values they are. Columns previously reported as drift verify clean; no contract or database change is needed.
|
|
166
|
+
- id: postgres-introspection-pinned-session
|
|
167
|
+
summary: |
|
|
168
|
+
Introspection now reads defaults, check constraints, index predicates, and policy text with `TimeZone = UTC`, `DateStyle = ISO, MDY`, and `IntervalStyle = postgres`, restoring the caller's settings. A contract inferred earlier from a server outside UTC that holds a `timestamptz` constant in such text shows that text once as a difference.
|
|
169
|
+
- id: source-load-failure-carries-diagnostics
|
|
170
|
+
summary: |
|
|
171
|
+
A contract source that fails to load now reports each finding in a `diagnostics` array on `CONTRACT.SOURCE_LOAD_FAILED`, alongside the existing `meta.diagnostics` and `meta.issues`.
|
|
172
|
+
- id: migration-new-defaults-to-the-db-ref
|
|
173
|
+
summary: |
|
|
174
|
+
Without `--from`, `migration new` now starts from the `db` ref, or from an empty database when there are no migrations, instead of from the newest migration. With migrations on disk and no `db` ref it refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN`. Scripts that relied on the old default must pass `--from`.
|
|
175
|
+
detection:
|
|
176
|
+
glob: "**/{package.json,*.sh,*.yml,*.yaml}"
|
|
177
|
+
contains:
|
|
178
|
+
- "migration new"
|
|
179
|
+
- id: migration-tip-error-codes-removed
|
|
180
|
+
summary: |
|
|
181
|
+
`MIGRATION.AMBIGUOUS_TARGET`, `MIGRATION.NO_TARGET` and `MIGRATION.NO_INITIAL_MIGRATION` are removed, and `graphTip` / `graphTipHash` are gone from error `meta`. A migration history with two branches now reports the real error, such as `MIGRATION.HASH_NOT_IN_GRAPH`.
|
|
182
|
+
detection:
|
|
183
|
+
glob: "**/*.{ts,tsx,js,mjs,cjs}"
|
|
184
|
+
matches:
|
|
185
|
+
- 'AMBIGUOUS_TARGET|NO_TARGET|NO_INITIAL_MIGRATION|graphTip'
|
|
186
|
+
- id: contract-artifacts-restamp
|
|
187
|
+
summary: |
|
|
188
|
+
The emitted `contract.json` / `contract.d.ts` embed the toolchain version, which moves
|
|
189
|
+
to 8.0.0-rc.12. Run `contract emit` once after upgrading so the emitted artifacts match
|
|
190
|
+
the installed toolchain.
|
|
191
|
+
detection:
|
|
192
|
+
glob: "**/contract.json"
|
|
193
|
+
contains:
|
|
194
|
+
- '"version": "8.0.0-rc.11"'
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
# 8.0.0-rc.11 → 8.0.0-rc.12 — User upgrade instructions
|
|
198
|
+
|
|
199
|
+
## `define-config-becomes-define-prisma-config`
|
|
200
|
+
|
|
201
|
+
For every `prisma.config.ts` matched by `detection`, rename the engine import and its call site from `defineConfig` to `definePrismaConfig`:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
// before
|
|
205
|
+
import { defineConfig } from '@prisma/cli-engine';
|
|
206
|
+
export default defineConfig({ ... });
|
|
207
|
+
|
|
208
|
+
// after
|
|
209
|
+
import { definePrismaConfig } from '@prisma/cli-engine';
|
|
210
|
+
export default definePrismaConfig({ ... });
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`definePrismaConfig` has been the engine's name for the marker since `@prisma/cli-engine@0.2.0`. `defineConfig` was a deprecated alias, and `@prisma/cli-engine@0.6.1` removes it, so this rename is required before the engine pin moves (`engine-pin-moves-to-0-6-1`, next). Leave any `defineConfig` imported from a product package (for example `@prisma/orm-postgres/config`) untouched — those helpers keep their name. The engine function's behaviour is identical; only the name changes.
|
|
214
|
+
|
|
215
|
+
## `engine-pin-moves-to-0-6-1`
|
|
216
|
+
|
|
217
|
+
For every `package.json` matched by `detection`, change the `@prisma/cli-engine` version from `0.4.0` to `0.6.1` and reinstall. Projects assembled by the `prisma` CLI resolve the engine automatically.
|
|
218
|
+
|
|
219
|
+
The engine removed the deprecated `defineConfig` alias. If `prisma.config.ts` still imports `defineConfig` from `@prisma/cli-engine`, apply `define-config-becomes-define-prisma-config` (the previous entry) in the same upgrade.
|
|
220
|
+
|
|
221
|
+
## `config-paths-resolve-from-declaring-file`
|
|
222
|
+
|
|
223
|
+
No change is needed for a project that runs commands from the directory holding its `prisma.config.ts`. A script that runs `prisma ... --config <path>` from another directory and relied on relative paths resolving from the working directory must drop that workaround: `contract`, `contract.output`, and `migrations.dir` now resolve from the config file's own directory.
|
|
224
|
+
|
|
225
|
+
A project inside a repository whose root, or any directory between the root and the project, holds another `prisma.config.ts` now inherits that file's values for any key the project's own config does not set. If the project must not inherit from a parent config, add `parent: false` to the project's own `prisma.config.ts`, which stops the search at that file, or remove the stray parent config.
|
|
226
|
+
|
|
227
|
+
## `psl-schema-requires-use-prisma-8-directive`
|
|
228
|
+
|
|
229
|
+
Every PSL schema file `prisma contract emit` reads must now carry `// use prisma-8` as its literal first line (before any other comment or whitespace beyond leading blank lines). A file a configured glob matches but that lacks the directive is silently excluded from the emitted contract; if excluding it would leave zero opted-in files, emission fails outright:
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
CONTRACT.SOURCE_LOAD_FAILED
|
|
233
|
+
why: No schema file carries the "// use prisma-8" directive
|
|
234
|
+
PSL_NO_OPTED_IN_SCHEMA_FILES: None of the matched files carry the "// use prisma-8" directive: <path>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
From the project root, run the codemod that sits next to this guide over every PSL schema file `contract.source.inputs` names (a plain path or a glob). `<skill>` is the directory of the synced `prisma-8` skill:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
node <skill>/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/scripts/multifile-psl/add-use-prisma-8-directive.mjs 'prisma/**/*.prisma'
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
It inserts `// use prisma-8` followed by a blank line at the top of every matched file that lacks the directive (or its earlier `// use prisma-next` spelling); a file that already carries either form is left untouched, so running it twice is a no-op. This is the same directive `orm init` already scaffolds into a fresh project.
|
|
244
|
+
|
|
245
|
+
The directive is content, not configuration: it does not change `contract.source.inputs`, the emitted `contract.json`, or any migration history. Re-run `prisma contract emit` afterward to confirm the schema loads.
|
|
246
|
+
|
|
247
|
+
## `psl-model-names-table-verbatim`
|
|
248
|
+
|
|
249
|
+
A PSL `model` with no `@@map` used to name its table, or its Mongo collection, after the model with the first letter lowered: `model UserProfile` read and wrote `"userProfile"`. It now uses the model name verbatim, `"UserProfile"`, the same rule every other Prisma 8 authoring surface already followed. Every model without `@@map` therefore points at a table that does not exist yet, so the schema must say which table it means.
|
|
250
|
+
|
|
251
|
+
From the project root, run the codemod that sits next to this guide once over every schema file, including the `contract.prisma` copy inside each migration directory. `<skill>` is the directory of the synced `prisma-8` skill. The script exits with an error if no file matches:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
node <skill>/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/scripts/psl-verbatim-table-names/add-model-map.mjs '**/*.prisma'
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
It adds `@@map("<model name with its first letter lowered>")` as the last line of every `model` block that has no `@@map`, keeps the file's indentation and line endings, leaves models that already have `@@map` alone, and leaves a variant with `@@base(...)` and no `@@map` alone because it shares its base's table. It never descends into `node_modules` or `dist`, prints every model it mapped as `<file>: model <Name> -> @@map("<name>")`, and is idempotent. If a `model` block is written in a shape it cannot read it prints `<file>:<line>: model block not understood` and exits 1; add the `@@map` to that block by hand.
|
|
258
|
+
|
|
259
|
+
The codemod cannot see storage. It is for schemas written against the previous release only, where every unmapped model's table was created with its first letter lowered. Run it once, before you re-run `contract infer`, and never on a schema that was inferred or written after upgrading: such a schema already names its tables verbatim, and the codemod would point each unmapped model at a lowercase table that does not exist. Read the printed list and remove the `@@map` from any model whose table already has the verbatim name.
|
|
260
|
+
|
|
261
|
+
Then run the project's emit command (`prisma contract emit`, or its `contract:emit` script) and check that `contract.json` did not change; `prisma db verify --schema-only` against the database must also be clean. An unchanged `contract.json` proves the codemod was run on the right schema: if it changed, or verify reports the lowercase tables as missing, the schema was already verbatim, so revert the codemod's edits. Storage hashes, migration history, and refs are unchanged after a correct run, so no `db sign`, migration, or data move is needed.
|
|
262
|
+
|
|
263
|
+
In this upgrade, other entries in this guide also change `contract.json` (the `version` stamp, and the defaults some later entries name). Emit also fails while the schema still holds a form this release refuses, such as `dbgenerated(...)`, until a later entry rewrites it. In either case, run this check after those entries, and compare the table names in `contract.json` rather than the whole file.
|
|
264
|
+
|
|
265
|
+
If you plan a migration (`prisma migration plan`, `prisma db update`, `prisma migrate`) without running the codemod, planning fails instead of dropping the table:
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
✘ [MIGRATION.PLANNING_FAILED] Migration planning failed
|
|
269
|
+
why: MIGRATION.TABLE_NAME_CASE_CHANGED: table "UserProfile" would be created and table "userProfile" dropped. Prisma 8 changed the default table name: a model with no @@map now names its table verbatim, so model UserProfile points at "UserProfile" instead of "userProfile".
|
|
270
|
+
→ To keep table "userProfile" and its rows, add @@map("userProfile") to model UserProfile (or run the add-model-map codemod over the schema) and plan again. Prisma 8 has no rename-table operation, so a deliberate rename is done by hand: run ALTER TABLE "userProfile" RENAME TO "UserProfile" (schema-qualified where applicable), after which the plan is empty.
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The conflict fires for each pair where the table to drop equals the table to create with its first letter lowered, in the same namespace, whatever the columns. It does not fire on an empty database or on tables the contract's control policy marks `external` or `observed`. Mongo has no planner and gives no error: an unmapped model silently reads and writes an empty `UserProfile` collection while the documents stay in `userProfile`, so run the codemod before deploying.
|
|
274
|
+
|
|
275
|
+
To adopt the verbatim names on purpose instead of mapping, rename the storage by hand and skip the codemod for those models. Postgres and SQLite: `ALTER TABLE "userProfile" RENAME TO "UserProfile"` (`ALTER TABLE "auth"."userProfile" RENAME TO "UserProfile"` inside a schema). Mongo: `db.userProfile.renameCollection("UserProfile")`. After the rename the migration plan is empty and `db verify` is clean.
|
|
276
|
+
|
|
277
|
+
`contract infer` follows the same rule: a table whose name already equals the model name (`"UserProfile"`, `"User"`) infers to a model with no `@@map` and verifies clean, where the previous output pointed the model at a lowercase table that did not exist. A snake_case table still infers with `@@map("user_profile")`. There is nothing to detect for this: the inferred text for such a table is the same as before, it is now correct.
|
|
278
|
+
|
|
279
|
+
## `contract-infer-prints-emittable-defaults`
|
|
280
|
+
|
|
281
|
+
No edit to an existing contract. This changes what `prisma contract infer` writes the next time you run it.
|
|
282
|
+
|
|
283
|
+
Each default is now printed in the form `contract emit` reads back, so an inferred `contract.prisma` emits without hand-editing:
|
|
284
|
+
|
|
285
|
+
- a `Decimal` or `Numeric` default is printed as an unquoted number that keeps every digit (`@default(1.50)`); a quoted form is refused, as `a-decimal-default-is-written-unquoted` describes;
|
|
286
|
+
- a `BigInt` default beyond ±(2^53 − 1) is printed as its digits instead of `dbgenerated(...)`;
|
|
287
|
+
- `NaN`, `Infinity` and `-Infinity` are printed bare (`@default(NaN)`), as `a-float-non-finite-default-is-written-bare` describes;
|
|
288
|
+
- a list default holding a `NULL` element is printed as `` @default(sql`<expression>`) `` (`dbgenerated(...)` is removed in this release; see `dbgenerated-removed-from-psl`), because no PSL list literal spells a null element. Earlier the default was dropped in silence and the column was emitted without it. `contract emit` stops at such a field with a diagnostic; edit the field or drop the default from the inferred file.
|
|
289
|
+
|
|
290
|
+
If you keep an inferred contract in version control, re-run `contract infer`, review the diff for these spellings, and re-emit. The stored defaults in the database do not change.
|
|
291
|
+
|
|
292
|
+
## `infer-prints-a-literal-where-it-printed-dbgenerated`
|
|
293
|
+
|
|
294
|
+
`prisma contract infer` classifies a stored default with the same rules a written value uses, prints it with the same authoring entry, and reads the text straight back to prove it returns the stored value. A default it can read back is now printed as a literal, including forms it used to print as `dbgenerated("...")` or as a quoted string:
|
|
295
|
+
|
|
296
|
+
| Column in the database | Before | After |
|
|
297
|
+
| --- | --- | --- |
|
|
298
|
+
| `jsonb NOT NULL DEFAULT '{}'::jsonb` | `@default(dbgenerated("'{}'::jsonb"))` | ``@default(json`{}`)`` |
|
|
299
|
+
| `jsonb DEFAULT 'null'::jsonb` | `@default(dbgenerated("'null'::jsonb"))` | ``@default(json`null`)`` |
|
|
300
|
+
| `timestamp(3) NOT NULL DEFAULT '2024-01-01 00:00:00'` | `@default(dbgenerated("'2024-01-01 00:00:00'::timestamp without time zone"))` | `@default("2024-01-01 00:00:00")` |
|
|
301
|
+
| `numeric(65,30) DEFAULT -0.5` | `@default("-0.5")` | `@default(-0.5)` |
|
|
302
|
+
| `numeric(10,2) NOT NULL DEFAULT 1.50` | `@default("1.50")` | `@default(1.50)` |
|
|
303
|
+
| `float8 DEFAULT 'NaN'` | `@default("NaN")` | `@default(NaN)` |
|
|
304
|
+
| `timestamp(3)[] DEFAULT ARRAY['2024-01-01 00:00:00'::timestamp(3)]` | `@default(dbgenerated("ARRAY[...]"))` | `@default(["2024-01-01 00:00:00"])` |
|
|
305
|
+
|
|
306
|
+
This is not a break to fix. The contract is the same; only the schema text differs. Re-run `prisma contract infer`, read the diff, and commit the new text. A default whose value the codec cannot read back, such as `NULL::character varying`, prints as `` @default(sql`<expression>`) `` (`dbgenerated(...)` is removed in this release; see `dbgenerated-removed-from-psl`), so infer never prints a schema that emit cannot read.
|
|
307
|
+
|
|
308
|
+
## `dbgenerated-removed-from-psl`
|
|
309
|
+
|
|
310
|
+
`@default(dbgenerated("<expression>"))` no longer parses. Every use is reported at its span as `PSL_UNKNOWN_DEFAULT_FUNCTION` with the message `` Default function "dbgenerated" was removed. Write the SQL as a tagged literal: @default(sql`<expression>`). Supported functions: ... ``. `prisma contract infer` no longer prints it either: a raw expression prints as a `sql` tagged literal, and a value the column's data type writes prints as that literal.
|
|
311
|
+
|
|
312
|
+
In PSL, a raw SQL column default is written as a tagged literal, ``@default(sql`...`)`` or `@default(sql"...")`. Rewrite each use by what the expression is:
|
|
313
|
+
|
|
314
|
+
| You wrote | Write instead |
|
|
315
|
+
| --- | --- |
|
|
316
|
+
| `@default(dbgenerated("gen_random_uuid()"))` | `` @default(sql`gen_random_uuid()`) `` |
|
|
317
|
+
| `@default(dbgenerated("now()"))`, `@default(dbgenerated("CURRENT_TIMESTAMP"))` on Postgres | `@default(now())` |
|
|
318
|
+
| `@default(dbgenerated("autoincrement()"))`, `@default(dbgenerated("nextval('<seq>'::regclass)"))` on a serial column | `@default(autoincrement())` |
|
|
319
|
+
| `@default(dbgenerated("'<json>'::jsonb"))` on a `Json` or `Jsonb` column | `` @default(json`<json>`) `` |
|
|
320
|
+
| `@default(dbgenerated("'<member>'::<enum type>"))` on a column typed by that enum | `@default("<member>")` |
|
|
321
|
+
| `@default(dbgenerated("'<text>'::text"))` on a text column | `@default("<text>")` |
|
|
322
|
+
| `@default(dbgenerated("<anything else>"))` | `` @default(sql`<anything else>`) `` |
|
|
323
|
+
|
|
324
|
+
The `now()` and `autoincrement()` rows are required, not a matter of style: `` sql`now()` `` and `` sql`autoincrement()` `` are refused with `PSL_INVALID_DEFAULT_SQL`, because Prisma reads those two expressions as its own default functions. Every other expression, including `NOW()` written in capitals, is used exactly as written.
|
|
325
|
+
|
|
326
|
+
The mechanical rewrite for the last row is `@default(sql"<expression>")` with the argument text copied unchanged: the double-quote fence uses the same escapes as the `dbgenerated("...")` argument, so the contract cannot change. The backtick fence reads better for SQL; to use it, undo the quoted string's escaping (`\"` becomes `"`, `\n` becomes a line break), then write each backtick in the body as `` \` ``. Inside backticks every other backslash is kept as written, `\$` included, because PSL has no escape for `$`, and `${` is ordinary text. A body that contains a backtick is easiest to keep in the double-quote fence.
|
|
327
|
+
|
|
328
|
+
The JSON and enum rows change the emitted contract: the default becomes `{ kind: 'literal', value }` instead of `{ kind: 'function', expression }`, so the storage hash moves. Run `prisma contract emit`, then `prisma db verify`: the literal compares equal to the live default, so verify passes and no migration is needed. Every other row emits the same contract as before; the storage hash does not move.
|
|
329
|
+
|
|
330
|
+
Find the uses with `grep -rn "dbgenerated(" prisma/` (or wherever the schema lives).
|
|
331
|
+
|
|
332
|
+
## `default-sql-method-deprecated`
|
|
333
|
+
|
|
334
|
+
Rewrite every `.defaultSql('<expression>')` call by its expression:
|
|
335
|
+
|
|
336
|
+
| Call | Replacement | Import |
|
|
337
|
+
| --- | --- | --- |
|
|
338
|
+
| `.defaultSql('now()')` | `.default(now())` | `now` from the contract builder |
|
|
339
|
+
| `.defaultSql('autoincrement()')` | `.default(autoincrement())` | `autoincrement` from the contract builder |
|
|
340
|
+
| `.defaultSql('gen_random_uuid()')` | `` .default(sql`gen_random_uuid()`) `` | `sql` from the contract builder |
|
|
341
|
+
| `.defaultSql('<anything else>')` | `` .default(sql`<anything else>`) `` | `sql` from the contract builder |
|
|
342
|
+
|
|
343
|
+
There is no named helper for other database functions, so they use the `sql` tag, as `gen_random_uuid()` does above. `` sql`now()` `` and `` sql`autoincrement()` `` are refused in the TypeScript `sql` tag as they are in PSL, so those two must use the named form.
|
|
344
|
+
|
|
345
|
+
Copy the expression's value, not its source string: first undo the TypeScript string's own escaping, so `.defaultSql('it\'s')` contributes `it's`. Then write each backtick as `` \` ``. Write a backslash that precedes a dollar sign as `\\$`, because the tag reads `\$` as the escape for `$`. Every other backslash can be written as it is, or doubled; both give one backslash. An expression that contains `${` is written `\${` inside the `sql` tag, which resolves it back to the two characters; in PSL it is written as it is.
|
|
346
|
+
|
|
347
|
+
Import the helpers from the module the code already imports `defineContract`, `field`, and `model` from (`@prisma/orm-postgres/contract-builder`, `@prisma/orm-sqlite/contract-builder`, or the internal `@internal/sql-contract-ts/contract-builder`). Every form lowers to the same `{ kind: 'function', expression }` default, so the emitted contract does not change: re-run `prisma contract emit` and confirm the column defaults in `contract.json` are unchanged.
|
|
348
|
+
|
|
349
|
+
Find the uses with `grep -rn "defaultSql(" src/` (or wherever the contract is defined).
|
|
350
|
+
|
|
351
|
+
## `a-json-default-is-a-json-tag`
|
|
352
|
+
|
|
353
|
+
Every value written in PSL now has a data type of its own, decided by what is written rather than by the column. A quoted string is text, and a JSON column's type does not cast from text, so a quoted JSON default is refused with `PSL_DEFAULT_TYPE_INCOMPATIBLE`:
|
|
354
|
+
|
|
355
|
+
```text
|
|
356
|
+
Field "Account.meta": pg/jsonb has no cast from pg/text; it casts from pg/json
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The `json` tag reads its body as a JSON document, which is what `pg/json` holds, and `pg/jsonb` casts from `pg/json`:
|
|
360
|
+
|
|
361
|
+
| Before | After |
|
|
362
|
+
| --- | --- |
|
|
363
|
+
| `meta Jsonb @default("{}")` | ``meta Jsonb @default(json`{}`)`` |
|
|
364
|
+
| `meta Jsonb @default("{\"plan\":\"free\"}")` | ``meta Jsonb @default(json`{ "plan": "free" }`)`` |
|
|
365
|
+
| `docs Jsonb[] @default(["{}"])` | ``docs Jsonb[] @default([json`{}`])`` |
|
|
366
|
+
| `meta Jsonb? @default("null")` | ``meta Jsonb? @default(json`null`)`` |
|
|
367
|
+
|
|
368
|
+
The body inside the tag is the JSON document itself, so it needs none of the escaping a PSL string needed. The backtick fence resolves `` \` `` and `\\` and nothing else, so `` json`{ "plan": "free" }` `` needs no escaping at all.
|
|
369
|
+
|
|
370
|
+
A backslash has to survive the fence and then JSON, so a JSON string that holds one backslash is written with four:
|
|
371
|
+
|
|
372
|
+
| In the schema | After the fence | JSON reads |
|
|
373
|
+
| --- | --- | --- |
|
|
374
|
+
| ``json`{ "re": "\\\\d+" }` `` | `{ "re": "\\d+" }` | the string `\d+` |
|
|
375
|
+
|
|
376
|
+
Two backslashes are not enough: the fence turns them into one, and `\d` is not a JSON escape, so the body is refused with `PSL_INVALID_JSON_LITERAL` — as is any other body that is not a JSON document.
|
|
377
|
+
|
|
378
|
+
## `a-decimal-default-is-written-unquoted`
|
|
379
|
+
|
|
380
|
+
A written number's data type comes from its own size and precision. Quoted digits are text, and `pg/numeric` does not cast from text:
|
|
381
|
+
|
|
382
|
+
```text
|
|
383
|
+
Field "Account.price": pg/numeric has no cast from pg/text; it casts from pg/int2, pg/int4, pg/int8
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
| Before | After |
|
|
387
|
+
| --- | --- |
|
|
388
|
+
| `price Decimal @default("1.50")` | `price Decimal @default(1.50)` |
|
|
389
|
+
| `price Numeric(10, 2) @default("-1.25")` | `price Numeric(10, 2) @default(-1.25)` |
|
|
390
|
+
| `prices Numeric(65, 30)[] @default(["-1.5", "2"])` | `prices Numeric(65, 30)[] @default([-1.5, 2])` |
|
|
391
|
+
|
|
392
|
+
The stored value does not change. Trailing zeros are kept (`1.50` stays `1.50`), leading zeros are dropped (`007.50` is `7.50`), and `-0.0` is `0.0` — the values these defaults always had.
|
|
393
|
+
|
|
394
|
+
## `a-float-non-finite-default-is-written-bare`
|
|
395
|
+
|
|
396
|
+
`NaN`, `Infinity` and `-Infinity` are number tokens in PSL, not identifiers and not text. A `Float` or `Real` column's type casts from the number types, not from text, so the quoted forms are refused with `PSL_DEFAULT_TYPE_INCOMPATIBLE`.
|
|
397
|
+
|
|
398
|
+
| Before | After |
|
|
399
|
+
| --- | --- |
|
|
400
|
+
| `ratio Float @default("NaN")` | `ratio Float @default(NaN)` |
|
|
401
|
+
| `ratio Float @default("-Infinity")` | `ratio Float @default(-Infinity)` |
|
|
402
|
+
| `ratio Real @default("NaN")` | `ratio Real @default(NaN)` |
|
|
403
|
+
| `ratios Float[] @default(["-1.5", "2"])` | `ratios Float[] @default([-1.5, 2])` |
|
|
404
|
+
|
|
405
|
+
## `a-json-list-default-is-one-json-literal`
|
|
406
|
+
|
|
407
|
+
A written list is several values, and the column takes it only when the column is a list or when the column's data type declares a list cast. `pg/json` and `pg/jsonb` declare none, so a written list on a column that holds one JSON value is refused:
|
|
408
|
+
|
|
409
|
+
```text
|
|
410
|
+
Field "Account.meta": pg/jsonb has no cast from a list; it casts from pg/json
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
A JSON list default is one JSON document, written inside the tag:
|
|
414
|
+
|
|
415
|
+
| Before | After |
|
|
416
|
+
| --- | --- |
|
|
417
|
+
| `meta Jsonb @default([1, 2])` | ``meta Jsonb @default(json`[1, 2]`)`` |
|
|
418
|
+
| `meta Jsonb @default([])` | ``meta Jsonb @default(json`[]`)`` |
|
|
419
|
+
|
|
420
|
+
A `Jsonb[]` column is unaffected: it is a list of JSON columns, and each element is written as its own `json` tag — ``docs Jsonb[] @default([json`{}`, json`[]`])``.
|
|
421
|
+
|
|
422
|
+
## `psl-number-defaults-keep-digits`
|
|
423
|
+
|
|
424
|
+
In every PSL schema matched by `detection`, look for number defaults on fields typed `Decimal`, `Numeric`, `Numeric(...)`, or a `types {}` alias of one of them, including list fields. If there are none, this entry changes nothing. The schema itself needs no edit.
|
|
425
|
+
|
|
426
|
+
Before you re-emit: if a database was signed with a contract holding such a default with more digits than a JavaScript number holds (for example `12345678901234567890.123456789`), `db verify` and `db sign` report a default mismatch on that column.
|
|
427
|
+
|
|
428
|
+
Run the project's emit command (`prisma contract emit`, or the project's `contract:emit` script). In `contract.json`, each such default becomes decimal text: `@default(10)` becomes `"10"`, `@default(1.50)` becomes `"1.50"`, `@default(007)` becomes `"7"`, `@default(-0)` becomes `"0"`, a long value keeps every digit, and `@default(NaN)` becomes `"NaN"` instead of `null`. The storage hash changes. The column default in the database does not, so no migration is needed.
|
|
429
|
+
|
|
430
|
+
Then run `prisma db sign` against the regenerated contract for every database signed with the old one. Until you do, `db verify` reports a hash mismatch and the application logs `CONTRACT.MARKER_MISMATCH`.
|
|
431
|
+
|
|
432
|
+
`db init` can now create these defaults; before, it failed with `pg/numeric@1 database JSON value must be a decimal string`. `BigInt` and `UnboundedInt` defaults within ±(2^53 − 1) emit exactly as before. Larger ones now emit instead of failing, and `contract infer` prints such `BigInt` defaults as plain numbers instead of `dbgenerated(...)`.
|
|
433
|
+
|
|
434
|
+
## `number-valued-64-bit-columns-store-their-default-as-digit-text`
|
|
435
|
+
|
|
436
|
+
Every codec of one data type now stores and reads that type's one canonical form. `pg/int8` stores digit text, so `pg/int8number@1` — the codec behind `BigIntNumber`, which reads a 64-bit integer as a JavaScript `number` — stores digit text too, where it used to store a JSON number. `sqlite/bigintnumber@1` changed the same way.
|
|
437
|
+
|
|
438
|
+
The detection flags every contract holding such a column, because a JSON file gives no reliable way to ask for the two facts together. A column is affected only when both are true: its codec is `pg/int8number@1` or `sqlite/bigintnumber@1`, **and** it carries a literal default. A column with no default, or with a function default, is unaffected — read the flagged file and check. In `contract.json` an affected column reads:
|
|
439
|
+
|
|
440
|
+
```json
|
|
441
|
+
"viewCount": {
|
|
442
|
+
"codecId": "pg/int8number@1",
|
|
443
|
+
"default": { "kind": "literal", "value": 10 },
|
|
444
|
+
"nativeType": "int8",
|
|
445
|
+
"nullable": false
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
and becomes:
|
|
450
|
+
|
|
451
|
+
```json
|
|
452
|
+
"viewCount": {
|
|
453
|
+
"codecId": "pg/int8number@1",
|
|
454
|
+
"default": { "kind": "literal", "value": "10" },
|
|
455
|
+
"nativeType": "int8",
|
|
456
|
+
"nullable": false
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Re-run `prisma contract emit` to rewrite `contract.json`, then `prisma db sign` so the signature matches the new contract. Nothing in the schema changes, and nothing in the database changes.
|
|
461
|
+
|
|
462
|
+
No example in this repository has such a column, so a project is affected only if its own contract holds one.
|
|
463
|
+
|
|
464
|
+
## `client-generated-created-at-presets`
|
|
465
|
+
|
|
466
|
+
Creation timestamp presets now use the client clock. For schemas using `temporal.createdAt()`, `temporal.createdAtString()`, or `temporal.createdAtJsDate()` (including the matching `field.temporal.*` TypeScript helpers), re-emit the contract with `prisma contract emit`. Review and apply a migration that removes the corresponding database defaults so the database matches the new storage contract. The creation timestamp now uses the same client-side generator as its matching update preset; it is still set only on create.
|
|
467
|
+
|
|
468
|
+
Update direct SQL writers to supply these required timestamps once their database defaults are removed. If database-generated time is intentional, replace the convenience preset with an explicit timestamp type and `@default(now())` in PSL, or an explicit column with `.default(now())` in TypeScript (`.defaultSql('now()')` is deprecated; see `default-sql-method-deprecated`). Preserve the original native type, precision, and codec representation. Explicit database defaults retain their existing behavior.
|
|
469
|
+
|
|
470
|
+
For Temporal-backed creation presets, provide a global `Temporal` implementation before writes as well as reads, for example `import 'temporal-polyfill/full/global'` when the runtime lacks native support. String and JavaScript Date presets do not require Temporal for their clock.
|
|
471
|
+
|
|
472
|
+
## `re-emit-the-contract-for-the-moved-query-operation-types`
|
|
473
|
+
|
|
474
|
+
The built-in Postgres query operations (`ilike`, and the new `fullTextMatches`, `fullTextRank` and `fullTextHeadline`) are contributed by the Postgres target rather than the Postgres adapter. Emitted contract types follow: the generated line
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma/orm-postgres/adapter/operation-types';
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
becomes
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types';
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Run `prisma contract emit` and commit the regenerated `contract.d.ts`. Nothing else in the file changes, `contract.json` does not change, and no contract hash moves. Do not hand-edit the generated file.
|
|
487
|
+
|
|
488
|
+
Application code that imported `@prisma/orm-postgres/adapter/operation-types` directly imports `@prisma/orm-postgres/target/operation-types` instead. That subpath no longer exists; there is no compatibility re-export.
|
|
489
|
+
|
|
490
|
+
To use the new operations, index the column with `@@fullTextIndex`, which renders the `to_tsvector` expression the predicate needs:
|
|
491
|
+
|
|
492
|
+
```prisma
|
|
493
|
+
model Message {
|
|
494
|
+
id Int @id
|
|
495
|
+
text String
|
|
496
|
+
|
|
497
|
+
@@fullTextIndex([text], name: "message_text_search")
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
In a TypeScript contract, use the matching helper inside the model's `sql({ indexes: [...] })`:
|
|
502
|
+
|
|
503
|
+
```ts
|
|
504
|
+
import { fullTextIndex } from '@prisma/orm-postgres/contract-builder';
|
|
505
|
+
|
|
506
|
+
model('Message', { fields: { id, text } }).sql(({ cols }) => ({
|
|
507
|
+
indexes: [
|
|
508
|
+
fullTextIndex(cols.text, { name: 'message_text_search' }),
|
|
509
|
+
fullTextIndex(cols.text, { where: 'archived_at IS NULL', name: 'message_text_search_live' }),
|
|
510
|
+
],
|
|
511
|
+
}));
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
The first argument of each operation is a `tsquery`, built with a helper from `@prisma/orm-postgres/target/full-text`. A bare string is a type error. Use `websearchToTsquery(query)` for search-box text; `plaintoTsquery` requires every word and `phrasetoTsquery` the words in order. In the SQL builder these four parsers are also `fns` members. For user input inside `tsquery` operator syntax, such as a typeahead prefix match, use the `tsquery` tag: `` tsquery`${term}:*` `` quotes each interpolated value as one term, so user input cannot add operators or break the syntax, and Postgres then lowercases and stems the words. A value with several words becomes a phrase: `` tsquery`${'new y'}:*` `` gives `'new':* <-> 'y':*`, so the words must be adjacent and in order, and `:*` applies to each word. Do not put quotes around the interpolation yourself: `` tsquery`'${term}':*` `` is a syntax error for every input. `toTsquery` takes operator syntax the application writes in full; never pass user input to it.
|
|
515
|
+
|
|
516
|
+
The operations also take an options object as their second argument — `language` for all three, plus `normalization` and `coverDensity` on `fullTextRank` and the `ts_headline` options (`startSel`, `stopSel`, `maxWords`, `minWords`, `highlightAll`) on `fullTextHeadline`:
|
|
517
|
+
|
|
518
|
+
```ts
|
|
519
|
+
import { tsquery, websearchToTsquery } from '@prisma/orm-postgres/target/full-text';
|
|
520
|
+
|
|
521
|
+
const q = websearchToTsquery(query);
|
|
522
|
+
await db.orm.public.Message.select('id', 'text')
|
|
523
|
+
.where((m) => m.text.fullTextMatches(q))
|
|
524
|
+
.orderBy((m) => m.text.fullTextRank(q, { normalization: 32 }).desc())
|
|
525
|
+
.all();
|
|
526
|
+
|
|
527
|
+
const suggestions = await db.orm.public.Message.select('id', 'text')
|
|
528
|
+
.where((m) => m.text.fullTextMatches(tsquery`${term}:*`))
|
|
529
|
+
.all();
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
The operation's `language` configures only the searched column. The parser or tag takes its own `language` for the query; pass the same value to both.
|
|
533
|
+
|
|
534
|
+
Postgres only uses a full-text index whose expression is the same `to_tsvector` over the same configuration literal and the same column as the query, so prefer these over writing `@@index(expression: "to_tsvector(…)", type: "gin", …)` by hand. Pass the same `language` to the index and to the operation: a mismatch is silent, and the query falls back to a sequential scan.
|
|
535
|
+
|
|
536
|
+
## `native-enum-columns-have-no-text-operations`
|
|
537
|
+
|
|
538
|
+
Postgres has no `LIKE`, `ILIKE` or `to_tsvector` for an enum type. Every one of these calls on a native enum column failed when the query ran, with `operator does not exist` or `function ... does not exist`. A `@@fullTextIndex` on a native enum column failed when the migration ran. So code that uses them never worked. It is now a type error, and `@@fullTextIndex` / `fullTextIndex` on a native enum column is refused when the contract is built (`PSL_FULL_TEXT_INDEX_TEXT_FIELD` in PSL, `CONTRACT.INDEX_INVALID` in TypeScript).
|
|
539
|
+
|
|
540
|
+
Replace a pattern match on an enum with an equality check on its members:
|
|
541
|
+
|
|
542
|
+
```ts
|
|
543
|
+
// before: failed at runtime
|
|
544
|
+
db.orm.public.Ticket.where((t) => t.status.ilike('open%'));
|
|
545
|
+
// after
|
|
546
|
+
db.orm.public.Ticket.where((t) => t.status.in(['open', 'reopened']));
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Remove any `@@fullTextIndex` or `fullTextIndex` on a native enum column. If you really need a pattern search over the enum labels, write that query through the raw SQL lane (``db.raw.sql`…` ``) and cast the column to text there, for example `status::text ILIKE $1`.
|
|
550
|
+
|
|
551
|
+
Nothing else about native enum columns changes: `eq`, `in`, ordering, and `min`/`max` work as before. A PSL `enum` block stored as text (`@@type("pg/text@1")`) is a text column and keeps every text operation.
|
|
552
|
+
|
|
553
|
+
## `re-emit-for-the-insert-conflict-skip-capabilities`
|
|
554
|
+
|
|
555
|
+
`createAll` and `createAndCount` on the SQL ORM client take a new option in second position that asks the database to skip rows colliding with a unique constraint:
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
const inserted = await db.orm.User.createAll(rows, { onConflict: 'skip' });
|
|
559
|
+
const added = await db.orm.User.createAndCount(rows, {
|
|
560
|
+
onConflict: 'skip',
|
|
561
|
+
conflictOn: ['email'],
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
The option requires two capability keys that the Postgres and SQLite adapters now report: `sql.insertOnConflictSkip`, and `sql.insertOnConflictWithoutTarget` for the untargeted form. A `contract.json` emitted before this release carries neither, so the ORM refuses the option with `ORM.CAPABILITY_MISSING` before running any statement.
|
|
566
|
+
|
|
567
|
+
Re-emit your contract to pick up the keys:
|
|
568
|
+
|
|
569
|
+
```console
|
|
570
|
+
prisma contract emit
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Nothing else changes. The keys are additive, the storage hash does not move, and every existing call — including `createAll(rows, configure)` with the annotation callback in second position — behaves exactly as before. You only need to re-emit if you want to use the new option.
|
|
574
|
+
|
|
575
|
+
## `params-only-sql-facade-prepare`
|
|
576
|
+
|
|
577
|
+
Find calls to `prepare(declaration, callback)` on clients created by the Postgres or SQLite facade (`@prisma/orm-postgres/runtime`, `@prisma/orm-sqlite/runtime`, or their `@internal/postgres/runtime` and `@internal/sqlite/runtime` counterparts). Resolve the receiver and callback rather than rewriting every method named `prepare`: native SQLite `database.prepare(sql)` and SQL runtime's existing params-only preparation are different APIs and must remain unchanged.
|
|
578
|
+
|
|
579
|
+
Change callbacks from `(sql, params) => ...` to `(params) => ...`. Replace references bound to the removed `sql` callback argument with the same facade receiver's lexical `.sql` property. Preserve the params argument's name, declaration, SQL chain, row selection, filters and invocation target/options. For extracted callbacks, capture the same client in the enclosing scope; do not capture an invocation target or evaluate the callback twice. Update explicit callback type annotations to accept only the placeholder-params argument.
|
|
580
|
+
|
|
581
|
+
```ts
|
|
582
|
+
// Before
|
|
583
|
+
const query = await db.prepare({ id: 'pg/int4@1' }, (sql, params) =>
|
|
584
|
+
sql.public.users.select('id').where((f, fns) => fns.eq(f.id, params.id)).build(),
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
// After
|
|
588
|
+
const query = await db.prepare({ id: 'pg/int4@1' }, (params) =>
|
|
589
|
+
db.sql.public.users.select('id').where((f, fns) => fns.eq(f.id, params.id)).build(),
|
|
590
|
+
);
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
Apply the same translation to SQLite's flat SQL facade (`sql.users` becomes `db.sql.users`), retaining its existing codec ids. Keep `.query(target, params, options?)` and SQL statistics `.execute(target, params, options?)` calls unchanged. Do not rewrite historical release notes, applied upgrade recipes, generated contracts or tests as part of this source translation.
|
|
594
|
+
|
|
595
|
+
## `postgres-target-owned-list-framing`
|
|
596
|
+
|
|
597
|
+
Review application code and snapshots that assert exact PostgreSQL list result spellings. Ordinary Prisma 8 runtime reads still return JavaScript arrays, and builtin and enum lists now use the same raw-text-to-element-codec path. If you assert `Decimal[]` / `numeric[]` strings for fixed-scale columns, update those expectations to PostgreSQL's database-normalized scale: a `numeric(30,10)[]` element inserted as `1.5` reads as `"1.5000000000"`; scalar numeric decoding already follows this text-preserving policy. If you use lower-level Postgres driver direct-query rows, parse raw PostgreSQL array literal strings such as `'{a,b}'` instead of expecting registered builtin arrays to arrive as JavaScript arrays. Do not re-emit contracts solely for this change: codec ids, `typeParams`, and `CodecRef.many` stay unchanged.
|
|
598
|
+
|
|
599
|
+
## `postgres-verify-reads-more-default-spellings`
|
|
600
|
+
|
|
601
|
+
No code or schema edit. Run `prisma db verify` once against each Postgres database and read the result.
|
|
602
|
+
|
|
603
|
+
Postgres prints a column default in spellings the reader did not all recognise. It now reads a negative or cast numeral (`'-1'::integer`, `(5)::smallint`) as the number, an enum literal cast to a type in another schema (`'confidential'::auth.oauth_client_type`) as the enum value, a zoneless `timestamp` literal as that timestamp, and an `ARRAY[...]` default of text, boolean, integer, bigint, float, decimal, timestamp, or enum elements — with the casts Postgres prints, and an empty `VARCHAR(n)[]` — as the list. An element that is an expression or a function call still stays a raw expression.
|
|
604
|
+
|
|
605
|
+
The effect is one-way: columns that were reported as drift on these spellings now report nothing. No column that verified before starts failing. If a test pins the exact findings `db verify` returns for such a column, remove that expectation rather than adjusting it.
|
|
606
|
+
|
|
607
|
+
The same rendering is now used when planning a migration, so a list literal default is written with its cast (`ARRAY['1', '-2']::int8[]`). Update snapshots of planned DDL that hold the uncast form.
|
|
608
|
+
|
|
609
|
+
## `postgres-introspection-pinned-session`
|
|
610
|
+
|
|
611
|
+
No code or schema edit. This matters only if you have a committed contract that was inferred from a server whose session time zone was not UTC.
|
|
612
|
+
|
|
613
|
+
Every introspection read now runs with `TimeZone = UTC`, `DateStyle = ISO, MDY`, and `IntervalStyle = postgres`, and the caller's settings are restored afterwards, so the text read back no longer depends on the server, the role, or the caller. Postgres prints a `timestamptz` constant in the session time zone, so a check constraint or index predicate whose text contains one was recorded in that server's zone.
|
|
614
|
+
|
|
615
|
+
Run `prisma db verify`. If a check constraint or an index predicate is reported as different and the only difference is the time zone written into a `timestamptz` constant, that is this change. Re-emit and re-sign once; the new text is stable from then on. Column defaults are compared as instants where they can be, so most of them are unaffected.
|
|
616
|
+
|
|
617
|
+
## `source-load-failure-carries-diagnostics`
|
|
618
|
+
|
|
619
|
+
Only for code or agents that read the CLI's `--json` output.
|
|
620
|
+
|
|
621
|
+
When a contract source fails to load, `CONTRACT.SOURCE_LOAD_FAILED` now carries a `diagnostics` array: one entry per finding, each with `code`, `summary`, `severity`, and, where the source gave a position, `where.path` and `where.line`. A finding whose source code is dotted (`PSL.PRISMA7_VIEW_UNSUPPORTED`) carries that code directly. A finding whose source code is an undotted legacy code (`PSL_INVALID_DECLARATION`) carries `CONTRACT.SOURCE_DIAGNOSTIC` with the original code in `meta.code` and at the start of the summary.
|
|
622
|
+
|
|
623
|
+
Nothing is removed: `meta.diagnostics` and `meta.issues` carry what they carried before. Read `diagnostics` in new code, and leave existing readers alone until you want the codes.
|
|
624
|
+
|
|
625
|
+
## `migration-new-defaults-to-the-db-ref`
|
|
626
|
+
|
|
627
|
+
`migration new` now picks its starting point the same way `migration plan` does. For every file matched by `detection` that runs `migration new` without `--from`:
|
|
628
|
+
|
|
629
|
+
- If the project keeps a `db` ref that points at a migration, nothing changes in practice: the command starts there.
|
|
630
|
+
- If the project has migrations on disk but no `db` ref, the command now refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN`. Add `--from <contract hash or ref>` naming the migration to build on; `prisma migration list` shows the hashes.
|
|
631
|
+
- If the migration history is empty and a `db` ref exists, the command refuses and points at `migration plan`, which writes the baseline. Run `migration plan` first.
|
|
632
|
+
|
|
633
|
+
## `migration-tip-error-codes-removed`
|
|
634
|
+
|
|
635
|
+
The CLI no longer looks for a single newest migration, so the errors that came from that lookup are gone. For every file matched by `detection`, remove handling of `MIGRATION.AMBIGUOUS_TARGET`, `MIGRATION.NO_TARGET` and `MIGRATION.NO_INITIAL_MIGRATION`, and stop reading `graphTip` or `graphTipHash` from an error's `meta`. A command that used to fail with one of those codes on a migration history with two branches now fails with the error that describes the actual problem, for example `MIGRATION.HASH_NOT_IN_GRAPH` for a hash that is not in the history, or `MIGRATION.MARKER_MISMATCH` for a database marker outside it.
|
|
636
|
+
|
|
637
|
+
## `contract-artifacts-restamp`
|
|
638
|
+
|
|
639
|
+
For every `contract.json` matched by `detection`, run the project's emit command (`prisma contract emit`, or the project's `contract:emit` script) once after upgrading. This entry accounts for the embedded `version` moving to `8.0.0-rc.12`; any other difference in the emitted files comes from an earlier entry in this guide.
|