turbine-orm 0.81.0 → 0.90.0
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/README.md +26 -25
- package/dist/checkout.d.ts +27 -2
- package/dist/checkout.js +75 -4
- package/dist/cjs/checkout.d.ts +27 -2
- package/dist/cjs/checkout.js +76 -3
- package/dist/cjs/cli/code-first-tags.d.ts +62 -0
- package/dist/cjs/cli/code-first-tags.js +124 -0
- package/dist/cjs/cli/compile-query.js +9 -0
- package/dist/cjs/cli/config.d.ts +35 -6
- package/dist/cjs/cli/config.js +17 -1
- package/dist/cjs/cli/destructive.d.ts +7 -4
- package/dist/cjs/cli/destructive.js +224 -20
- package/dist/cjs/cli/error-catalog.js +36 -0
- package/dist/cjs/cli/error-text.d.ts +15 -0
- package/dist/cjs/cli/error-text.js +40 -0
- package/dist/cjs/cli/index.d.ts +62 -4
- package/dist/cjs/cli/index.js +568 -108
- package/dist/cjs/cli/local-server-guard.d.ts +53 -0
- package/dist/cjs/cli/local-server-guard.js +119 -0
- package/dist/cjs/cli/local-server-output.d.ts +41 -0
- package/dist/cjs/cli/local-server-output.js +103 -0
- package/dist/cjs/cli/mcp.d.ts +8 -0
- package/dist/cjs/cli/mcp.js +121 -32
- package/dist/cjs/cli/migrate.d.ts +113 -5
- package/dist/cjs/cli/migrate.js +270 -21
- package/dist/cjs/cli/observe.d.ts +6 -1
- package/dist/cjs/cli/observe.js +85 -55
- package/dist/cjs/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cjs/cli/pii-predicate-guard.js +98 -37
- package/dist/cjs/cli/prisma-resolve.js +17 -13
- package/dist/cjs/cli/prisma-schema.js +12 -4
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.d.ts +14 -2
- package/dist/cjs/cli/studio.js +244 -69
- package/dist/cjs/cli/ui.d.ts +1 -1
- package/dist/cjs/client.d.ts +558 -85
- package/dist/cjs/client.js +1270 -395
- package/dist/cjs/connection-guard.d.ts +13 -0
- package/dist/cjs/connection-guard.js +28 -0
- package/dist/cjs/connection-url.d.ts +51 -16
- package/dist/cjs/connection-url.js +67 -34
- package/dist/cjs/diagnostics.d.ts +81 -0
- package/dist/cjs/diagnostics.js +147 -0
- package/dist/cjs/dialect.d.ts +449 -20
- package/dist/cjs/dialect.js +117 -76
- package/dist/cjs/engine-config.d.ts +7 -6
- package/dist/cjs/errors.d.ts +126 -6
- package/dist/cjs/errors.js +256 -13
- package/dist/cjs/generate.d.ts +57 -7
- package/dist/cjs/generate.js +635 -279
- package/dist/cjs/global-filters-config.d.ts +59 -0
- package/dist/cjs/global-filters-config.js +367 -0
- package/dist/cjs/index.d.ts +9 -4
- package/dist/cjs/index.js +10 -2
- package/dist/cjs/introspect.d.ts +31 -6
- package/dist/cjs/introspect.js +108 -50
- package/dist/cjs/log-codes.d.ts +12 -0
- package/dist/cjs/log-codes.js +77 -0
- package/dist/cjs/logger.d.ts +87 -0
- package/dist/cjs/logger.js +98 -0
- package/dist/cjs/mssql.d.ts +8 -5
- package/dist/cjs/mssql.js +352 -65
- package/dist/cjs/mysql.d.ts +3 -5
- package/dist/cjs/mysql.js +420 -70
- package/dist/cjs/nested-write.d.ts +3 -0
- package/dist/cjs/nested-write.js +179 -114
- package/dist/cjs/pipeline-submittable.d.ts +12 -0
- package/dist/cjs/pipeline-submittable.js +118 -44
- package/dist/cjs/pipeline.js +21 -26
- package/dist/cjs/powdb-introspect.d.ts +5 -2
- package/dist/cjs/powdb-introspect.js +20 -15
- package/dist/cjs/powdb-shared.d.ts +36 -5
- package/dist/cjs/powdb-shared.js +67 -8
- package/dist/cjs/powdb.d.ts +4 -0
- package/dist/cjs/powdb.js +183 -51
- package/dist/cjs/powql.d.ts +394 -51
- package/dist/cjs/powql.js +1829 -284
- package/dist/cjs/prisma-compat.d.ts +23 -15
- package/dist/cjs/prisma-compat.js +507 -372
- package/dist/cjs/query/aggregates.d.ts +17 -1
- package/dist/cjs/query/aggregates.js +261 -29
- package/dist/cjs/query/batched-loader.d.ts +25 -6
- package/dist/cjs/query/batched-loader.js +103 -20
- package/dist/cjs/query/builder.d.ts +325 -184
- package/dist/cjs/query/builder.js +1452 -490
- package/dist/cjs/query/column-ref.d.ts +61 -0
- package/dist/cjs/query/column-ref.js +73 -0
- package/dist/cjs/query/compound-unique.d.ts +21 -0
- package/dist/cjs/query/compound-unique.js +98 -18
- package/dist/cjs/query/deferred.d.ts +53 -16
- package/dist/cjs/query/expr.d.ts +217 -0
- package/dist/cjs/query/expr.js +859 -0
- package/dist/cjs/query/filters.d.ts +155 -53
- package/dist/cjs/query/filters.js +613 -76
- package/dist/cjs/query/index.d.ts +2 -0
- package/dist/cjs/query/index.js +7 -1
- package/dist/cjs/query/json-value.d.ts +43 -0
- package/dist/cjs/query/json-value.js +59 -0
- package/dist/cjs/query/normalize-args.d.ts +87 -0
- package/dist/cjs/query/normalize-args.js +527 -0
- package/dist/cjs/query/option-surface.d.ts +16 -4
- package/dist/cjs/query/option-surface.js +57 -14
- package/dist/cjs/query/relation-names.d.ts +14 -0
- package/dist/cjs/query/relation-names.js +77 -18
- package/dist/cjs/query/relations.d.ts +30 -3
- package/dist/cjs/query/relations.js +219 -63
- package/dist/cjs/query/row-lock.d.ts +49 -0
- package/dist/cjs/query/row-lock.js +138 -0
- package/dist/cjs/query/sql-comment.d.ts +161 -0
- package/dist/cjs/query/sql-comment.js +381 -0
- package/dist/cjs/query/stream-idle.d.ts +132 -0
- package/dist/cjs/query/stream-idle.js +212 -0
- package/dist/cjs/query/table-client.d.ts +101 -0
- package/dist/cjs/query/table-client.js +74 -0
- package/dist/cjs/query/types.d.ts +677 -298
- package/dist/cjs/query/utils.d.ts +105 -6
- package/dist/cjs/query/utils.js +426 -19
- package/dist/cjs/query/warn-registry.d.ts +18 -0
- package/dist/cjs/query/warn-registry.js +18 -0
- package/dist/cjs/query/where-compile.d.ts +138 -14
- package/dist/cjs/query/where-compile.js +513 -29
- package/dist/cjs/query/where.d.ts +159 -59
- package/dist/cjs/query/where.js +716 -246
- package/dist/cjs/query/writes.d.ts +112 -50
- package/dist/cjs/query/writes.js +1498 -200
- package/dist/cjs/query-events.d.ts +4 -2
- package/dist/cjs/query-events.js +3 -1
- package/dist/cjs/realtime.d.ts +7 -3
- package/dist/cjs/realtime.js +13 -2
- package/dist/cjs/schema-builder.d.ts +55 -4
- package/dist/cjs/schema-builder.js +382 -18
- package/dist/cjs/schema-metadata.d.ts +21 -1
- package/dist/cjs/schema-metadata.js +29 -7
- package/dist/cjs/schema-sql.d.ts +75 -5
- package/dist/cjs/schema-sql.js +497 -45
- package/dist/cjs/schema.d.ts +26 -5
- package/dist/cjs/schema.js +110 -13
- package/dist/cjs/sql-placeholders.d.ts +56 -0
- package/dist/cjs/sql-placeholders.js +204 -0
- package/dist/cjs/sqlite.d.ts +75 -20
- package/dist/cjs/sqlite.js +593 -95
- package/dist/cjs/supplied-pool-types.d.ts +48 -0
- package/dist/cjs/supplied-pool-types.js +256 -0
- package/dist/cjs/transaction-control.d.ts +51 -0
- package/dist/cjs/transaction-control.js +316 -0
- package/dist/cjs/transaction-state.d.ts +342 -0
- package/dist/cjs/transaction-state.js +707 -0
- package/dist/cjs/typed-sql.d.ts +37 -3
- package/dist/cjs/typed-sql.js +102 -8
- package/dist/cli/code-first-tags.d.ts +62 -0
- package/dist/cli/code-first-tags.js +120 -0
- package/dist/cli/compile-query.js +9 -0
- package/dist/cli/config.d.ts +35 -6
- package/dist/cli/config.js +16 -1
- package/dist/cli/destructive.d.ts +7 -4
- package/dist/cli/destructive.js +224 -20
- package/dist/cli/error-catalog.js +36 -0
- package/dist/cli/error-text.d.ts +15 -0
- package/dist/cli/error-text.js +37 -0
- package/dist/cli/index.d.ts +62 -4
- package/dist/cli/index.js +569 -112
- package/dist/cli/local-server-guard.d.ts +53 -0
- package/dist/cli/local-server-guard.js +113 -0
- package/dist/cli/local-server-output.d.ts +41 -0
- package/dist/cli/local-server-output.js +98 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +121 -34
- package/dist/cli/migrate.d.ts +113 -5
- package/dist/cli/migrate.js +261 -21
- package/dist/cli/observe.d.ts +6 -1
- package/dist/cli/observe.js +84 -54
- package/dist/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cli/pii-predicate-guard.js +100 -35
- package/dist/cli/prisma-resolve.js +15 -11
- package/dist/cli/prisma-schema.js +12 -4
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +14 -2
- package/dist/cli/studio.js +246 -71
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.d.ts +558 -85
- package/dist/client.js +1275 -400
- package/dist/connection-guard.d.ts +13 -0
- package/dist/connection-guard.js +27 -0
- package/dist/connection-url.d.ts +51 -16
- package/dist/connection-url.js +63 -34
- package/dist/diagnostics.d.ts +81 -0
- package/dist/diagnostics.js +144 -0
- package/dist/dialect.d.ts +449 -20
- package/dist/dialect.js +115 -43
- package/dist/engine-config.d.ts +7 -6
- package/dist/errors.d.ts +126 -6
- package/dist/errors.js +252 -12
- package/dist/generate.d.ts +57 -7
- package/dist/generate.js +635 -281
- package/dist/global-filters-config.d.ts +59 -0
- package/dist/global-filters-config.js +361 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +4 -2
- package/dist/introspect.d.ts +31 -6
- package/dist/introspect.js +106 -50
- package/dist/log-codes.d.ts +12 -0
- package/dist/log-codes.js +74 -0
- package/dist/logger.d.ts +87 -0
- package/dist/logger.js +93 -0
- package/dist/mssql.d.ts +8 -5
- package/dist/mssql.js +354 -67
- package/dist/mysql.d.ts +3 -5
- package/dist/mysql.js +423 -73
- package/dist/nested-write.d.ts +3 -0
- package/dist/nested-write.js +177 -112
- package/dist/pipeline-submittable.d.ts +12 -0
- package/dist/pipeline-submittable.js +117 -44
- package/dist/pipeline.js +21 -26
- package/dist/powdb-introspect.d.ts +5 -2
- package/dist/powdb-introspect.js +21 -16
- package/dist/powdb-shared.d.ts +36 -5
- package/dist/powdb-shared.js +65 -8
- package/dist/powdb.d.ts +4 -0
- package/dist/powdb.js +183 -51
- package/dist/powql.d.ts +394 -51
- package/dist/powql.js +1836 -291
- package/dist/prisma-compat.d.ts +23 -15
- package/dist/prisma-compat.js +508 -373
- package/dist/query/aggregates.d.ts +17 -1
- package/dist/query/aggregates.js +263 -32
- package/dist/query/batched-loader.d.ts +25 -6
- package/dist/query/batched-loader.js +101 -21
- package/dist/query/builder.d.ts +325 -184
- package/dist/query/builder.js +1458 -495
- package/dist/query/column-ref.d.ts +61 -0
- package/dist/query/column-ref.js +66 -0
- package/dist/query/compound-unique.d.ts +21 -0
- package/dist/query/compound-unique.js +97 -19
- package/dist/query/deferred.d.ts +53 -16
- package/dist/query/expr.d.ts +217 -0
- package/dist/query/expr.js +848 -0
- package/dist/query/filters.d.ts +155 -53
- package/dist/query/filters.js +597 -72
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +4 -0
- package/dist/query/json-value.d.ts +43 -0
- package/dist/query/json-value.js +54 -0
- package/dist/query/normalize-args.d.ts +87 -0
- package/dist/query/normalize-args.js +517 -0
- package/dist/query/option-surface.d.ts +16 -4
- package/dist/query/option-surface.js +56 -13
- package/dist/query/relation-names.d.ts +14 -0
- package/dist/query/relation-names.js +77 -19
- package/dist/query/relations.d.ts +30 -3
- package/dist/query/relations.js +220 -67
- package/dist/query/row-lock.d.ts +49 -0
- package/dist/query/row-lock.js +131 -0
- package/dist/query/sql-comment.d.ts +161 -0
- package/dist/query/sql-comment.js +374 -0
- package/dist/query/stream-idle.d.ts +132 -0
- package/dist/query/stream-idle.js +203 -0
- package/dist/query/table-client.d.ts +101 -0
- package/dist/query/table-client.js +70 -0
- package/dist/query/types.d.ts +677 -298
- package/dist/query/utils.d.ts +105 -6
- package/dist/query/utils.js +414 -18
- package/dist/query/warn-registry.d.ts +18 -0
- package/dist/query/warn-registry.js +18 -0
- package/dist/query/where-compile.d.ts +138 -14
- package/dist/query/where-compile.js +508 -31
- package/dist/query/where.d.ts +159 -59
- package/dist/query/where.js +711 -248
- package/dist/query/writes.d.ts +112 -50
- package/dist/query/writes.js +1496 -202
- package/dist/query-events.d.ts +4 -2
- package/dist/query-events.js +3 -1
- package/dist/realtime.d.ts +7 -3
- package/dist/realtime.js +13 -2
- package/dist/schema-builder.d.ts +55 -4
- package/dist/schema-builder.js +381 -18
- package/dist/schema-metadata.d.ts +21 -1
- package/dist/schema-metadata.js +28 -7
- package/dist/schema-sql.d.ts +75 -5
- package/dist/schema-sql.js +494 -44
- package/dist/schema.d.ts +26 -5
- package/dist/schema.js +109 -13
- package/dist/sql-placeholders.d.ts +56 -0
- package/dist/sql-placeholders.js +200 -0
- package/dist/sqlite.d.ts +75 -20
- package/dist/sqlite.js +597 -99
- package/dist/supplied-pool-types.d.ts +48 -0
- package/dist/supplied-pool-types.js +247 -0
- package/dist/transaction-control.d.ts +51 -0
- package/dist/transaction-control.js +311 -0
- package/dist/transaction-state.d.ts +342 -0
- package/dist/transaction-state.js +696 -0
- package/dist/typed-sql.d.ts +37 -3
- package/dist/typed-sql.js +100 -8
- package/package.json +7 -4
- package/skills/turbine-orm/SKILL.md +56 -6
package/dist/cjs/nested-write.js
CHANGED
|
@@ -21,6 +21,8 @@ exports.executeNestedCreate = executeNestedCreate;
|
|
|
21
21
|
exports.executeNestedUpdate = executeNestedUpdate;
|
|
22
22
|
const errors_js_1 = require("./errors.js");
|
|
23
23
|
const compound_unique_js_1 = require("./query/compound-unique.js");
|
|
24
|
+
const expr_js_1 = require("./query/expr.js");
|
|
25
|
+
const filters_js_1 = require("./query/filters.js");
|
|
24
26
|
const utils_js_1 = require("./query/utils.js");
|
|
25
27
|
const schema_js_1 = require("./schema.js");
|
|
26
28
|
const MAX_DEPTH = 10;
|
|
@@ -50,7 +52,7 @@ function extractRelationFields(data, tableMeta) {
|
|
|
50
52
|
relations[key] = value;
|
|
51
53
|
}
|
|
52
54
|
else {
|
|
53
|
-
scalars
|
|
55
|
+
(0, filters_js_1.setOwn)(scalars, key, value);
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
return { scalars, relations };
|
|
@@ -216,32 +218,73 @@ function toArray(value) {
|
|
|
216
218
|
function keyOf(value) {
|
|
217
219
|
return String(value);
|
|
218
220
|
}
|
|
221
|
+
/** The operations that point a to-one relation's foreign key at a row: at most one per write. */
|
|
222
|
+
const BELONGS_TO_POINTER_OPS = ['create', 'connect', 'connectOrCreate', 'disconnect', 'delete'];
|
|
219
223
|
/**
|
|
220
224
|
* Validate that all operation keys in a nested write are recognized and
|
|
221
|
-
* allowed for the current context (create vs update).
|
|
225
|
+
* allowed for the current context (create vs update) and relation.
|
|
226
|
+
*
|
|
227
|
+
* A belongsTo relation is one row the parent points at, so it takes Prisma's
|
|
228
|
+
* to-one operations: no `set` (a to-many operation), and at most one of the
|
|
229
|
+
* operations that decide what the parent points at, since one write cannot
|
|
230
|
+
* honor two. An operation whose value is `undefined` is absent, as everywhere
|
|
231
|
+
* else in a query; only its name is still checked, so a misspelled operation
|
|
232
|
+
* is reported whatever it holds.
|
|
222
233
|
*/
|
|
223
|
-
function validateOps(relationName, ops, isUpdate) {
|
|
234
|
+
function validateOps(relationName, ops, isUpdate, rel) {
|
|
235
|
+
const belongsTo = rel?.type === 'belongsTo';
|
|
224
236
|
for (const opName of Object.keys(ops)) {
|
|
237
|
+
const absent = ops[opName] === undefined;
|
|
225
238
|
if (!CREATE_ONLY_OPS.has(opName) && !UPDATE_ONLY_OPS.has(opName)) {
|
|
239
|
+
const updateOps = belongsTo
|
|
240
|
+
? ', disconnect, delete, update, upsert'
|
|
241
|
+
: ', disconnect, set, delete, update, upsert';
|
|
226
242
|
throw new errors_js_1.ValidationError(`Unknown nested write operation "${opName}" on relation "${relationName}". ` +
|
|
227
|
-
`Valid operations: create, connect, connectOrCreate${isUpdate ?
|
|
243
|
+
`Valid operations: create, connect, connectOrCreate${isUpdate ? updateOps : ''}.`);
|
|
228
244
|
}
|
|
245
|
+
if (absent)
|
|
246
|
+
continue;
|
|
229
247
|
if (!isUpdate && UPDATE_ONLY_OPS.has(opName)) {
|
|
230
248
|
throw new errors_js_1.ValidationError(`Operation "${opName}" on relation "${relationName}" is only valid inside update(), not create().`);
|
|
231
249
|
}
|
|
250
|
+
if (belongsTo && opName === 'set') {
|
|
251
|
+
throw new errors_js_1.ValidationError(`Nested set on "${relationName}" is not supported: "${relationName}" is a single "${rel.to}" row this ` +
|
|
252
|
+
`record points at, and set replaces a to-many list. Use connect to point at a different "${rel.to}", ` +
|
|
253
|
+
'or disconnect to clear it.');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (belongsTo) {
|
|
257
|
+
const given = BELONGS_TO_POINTER_OPS.filter((op) => ops[op] !== undefined && ops[op] !== false);
|
|
258
|
+
if (given.length > 1) {
|
|
259
|
+
throw new errors_js_1.ValidationError(`Nested write on "${relationName}" combines ${given.join(' and ')}. "${relationName}" is a single ` +
|
|
260
|
+
`"${rel.to}" row, so one write can create, connect, connectOrCreate, disconnect or delete it, not ` +
|
|
261
|
+
'several. Split the write in two if both are intended.');
|
|
262
|
+
}
|
|
232
263
|
}
|
|
233
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* The single item a to-one operation names. A one-element array is accepted
|
|
267
|
+
* for compatibility; an empty or longer one is refused, since only one row can
|
|
268
|
+
* be pointed at and keeping one of several would drop the rest silently.
|
|
269
|
+
*/
|
|
270
|
+
function singleToOneItem(value, op, relName, rel) {
|
|
271
|
+
if (!Array.isArray(value))
|
|
272
|
+
return value;
|
|
273
|
+
if (value.length === 1)
|
|
274
|
+
return value[0];
|
|
275
|
+
throw new errors_js_1.ValidationError(`Nested ${op} on "${relName}" received ${value.length} items, but "${relName}" is a single "${rel.to}" ` +
|
|
276
|
+
`row. Pass one object, for example \`${op}: { ... }\`.`);
|
|
277
|
+
}
|
|
234
278
|
/**
|
|
235
279
|
* The `with` clause that reads back the tree a nested write just wrote.
|
|
236
280
|
*
|
|
237
281
|
* Built from the DATA, recursively: a relation whose payload itself contains
|
|
238
282
|
* relation writes is requested as `{ rel: { with: { ... } } }` rather than
|
|
239
|
-
* `{ rel: true }`.
|
|
240
|
-
*
|
|
241
|
-
* missing entirely, which a caller reads as "there are none".
|
|
283
|
+
* `{ rel: true }`. Requesting the top-level keys alone would return a depth-3
|
|
284
|
+
* create without its grandchildren, which a caller reads as "there are none".
|
|
242
285
|
*
|
|
243
286
|
* Only relations that were actually WRITTEN are requested, so a create with no
|
|
244
|
-
* nested data
|
|
287
|
+
* nested data reads back just the row. The walk is capped
|
|
245
288
|
* at {@link MAX_DEPTH}, the cap the write walk itself uses, and the builder's
|
|
246
289
|
* own relation-depth guard is the backstop below that.
|
|
247
290
|
*/
|
|
@@ -266,11 +309,11 @@ function readBackWith(schema, tableName, relations, depth = 0) {
|
|
|
266
309
|
for (const item of toArray(payload)) {
|
|
267
310
|
// `{ where, data }` (nested update) and `{ where, create, update }`
|
|
268
311
|
// (nested upsert) carry their written fields one level in.
|
|
269
|
-
const bodies = isPlainRecord(item) && ('data' in item || 'create' in item || 'update' in item)
|
|
312
|
+
const bodies = (0, filters_js_1.isPlainRecord)(item) && ('data' in item || 'create' in item || 'update' in item)
|
|
270
313
|
? [item.data, item.create, item.update]
|
|
271
314
|
: [item];
|
|
272
315
|
for (const body of bodies) {
|
|
273
|
-
if (!isPlainRecord(body))
|
|
316
|
+
if (!(0, filters_js_1.isPlainRecord)(body))
|
|
274
317
|
continue;
|
|
275
318
|
const child = schema.tables[rel.to];
|
|
276
319
|
if (!child)
|
|
@@ -286,10 +329,6 @@ function readBackWith(schema, tableName, relations, depth = 0) {
|
|
|
286
329
|
}
|
|
287
330
|
return Object.keys(clause).length > 0 ? clause : undefined;
|
|
288
331
|
}
|
|
289
|
-
/** A plain object (not an array, Date or null), the shape nested payloads take. */
|
|
290
|
-
function isPlainRecord(value) {
|
|
291
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
292
|
-
}
|
|
293
332
|
/**
|
|
294
333
|
* Build a PK-based where clause from a parent row and its table metadata.
|
|
295
334
|
*/
|
|
@@ -821,6 +860,17 @@ function notRelatedToParent(op, relName, rel, target) {
|
|
|
821
860
|
`or it belongs to a different parent (a nested ${op} can only touch this parent's rows).`,
|
|
822
861
|
});
|
|
823
862
|
}
|
|
863
|
+
/** A belongsTo `delete` (or a `disconnect` naming fields) on a parent whose foreign key is NULL. */
|
|
864
|
+
function nothingConnected(op, relName, rel, target) {
|
|
865
|
+
return new errors_js_1.NotFoundError({
|
|
866
|
+
table: rel.to,
|
|
867
|
+
where: target,
|
|
868
|
+
operation: `nested ${op}`,
|
|
869
|
+
message: `Nested ${op} on relation "${relName}": nothing is connected (this record's foreign key to "${rel.to}" ` +
|
|
870
|
+
`is NULL), so there is no "${rel.to}" row to ${op}.` +
|
|
871
|
+
(op === 'disconnect' ? ' Use `disconnect: true` to clear the relation whatever it points at.' : ''),
|
|
872
|
+
});
|
|
873
|
+
}
|
|
824
874
|
/** Re-tag a NotFoundError from a scoped child write as a relation-scoped miss. */
|
|
825
875
|
function rethrowAsNotRelated(err, op, relName, rel, target) {
|
|
826
876
|
if (err instanceof errors_js_1.NotFoundError) {
|
|
@@ -852,7 +902,7 @@ async function executeNestedCreate(ctx, tableName, data, depth = 0, path = [], r
|
|
|
852
902
|
throw new errors_js_1.RelationError(`Unknown relation "${relName}" on table "${tableName}". ` +
|
|
853
903
|
`Available relations: ${Object.keys(tableMeta.relations).join(', ') || '(none)'}.`);
|
|
854
904
|
}
|
|
855
|
-
validateOps(relName, ops, false);
|
|
905
|
+
validateOps(relName, ops, false, rel);
|
|
856
906
|
}
|
|
857
907
|
// belongsTo relations put the foreign key on the PARENT row, so they must be
|
|
858
908
|
// resolved BEFORE the parent is inserted, otherwise a NOT NULL FK column
|
|
@@ -930,7 +980,7 @@ async function executeNestedUpdate(ctx, tableName, where, data, depth = 0, path
|
|
|
930
980
|
throw new errors_js_1.RelationError(`Unknown relation "${relName}" on table "${tableName}". ` +
|
|
931
981
|
`Available relations: ${Object.keys(tableMeta.relations).join(', ') || '(none)'}.`);
|
|
932
982
|
}
|
|
933
|
-
validateOps(relName, ops, true);
|
|
983
|
+
validateOps(relName, ops, true, rel);
|
|
934
984
|
}
|
|
935
985
|
// Update parent row with scalar data (may be empty if only relation ops)
|
|
936
986
|
let parentRow;
|
|
@@ -980,25 +1030,11 @@ async function executeNestedUpdate(ctx, tableName, where, data, depth = 0, path
|
|
|
980
1030
|
if (ops.upsert !== undefined) {
|
|
981
1031
|
await processBelongsToUpsert(ctx, rel, ops.upsert, parentRow, tableName);
|
|
982
1032
|
}
|
|
983
|
-
if (ops.disconnect !== undefined) {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
return col?.nullable ?? false;
|
|
989
|
-
});
|
|
990
|
-
if (!nullable) {
|
|
991
|
-
throw new errors_js_1.ValidationError(`Cannot disconnect "${relName}": foreign key column(s) ${fks.join(', ')} are NOT NULL. Use delete instead.`);
|
|
992
|
-
}
|
|
993
|
-
const updateData = {};
|
|
994
|
-
for (const fk of fks) {
|
|
995
|
-
const field = tableMeta.reverseColumnMap[fk] ?? fk;
|
|
996
|
-
updateData[field] = null;
|
|
997
|
-
}
|
|
998
|
-
await ctx.tx.table(tableName).update({
|
|
999
|
-
where: pkWhere(tableMeta, parentRow),
|
|
1000
|
-
data: updateData,
|
|
1001
|
-
});
|
|
1033
|
+
if (ops.disconnect !== undefined && ops.disconnect !== false) {
|
|
1034
|
+
await processBelongsToDisconnect(ctx, rel, ops.disconnect, relName, parentRow, tableName, 'disconnect');
|
|
1035
|
+
}
|
|
1036
|
+
if (ops.delete !== undefined && ops.delete !== false) {
|
|
1037
|
+
await processBelongsToDisconnect(ctx, rel, ops.delete, relName, parentRow, tableName, 'delete');
|
|
1002
1038
|
}
|
|
1003
1039
|
}
|
|
1004
1040
|
else {
|
|
@@ -1077,93 +1113,107 @@ async function processHasManyCreate(ctx, rel, ops, parentRow, depth, path, relNa
|
|
|
1077
1113
|
* which UPDATEs the FK after the parent already exists.)
|
|
1078
1114
|
*/
|
|
1079
1115
|
async function resolveBelongsToForCreate(ctx, rel, ops, parentTable, depth, path, relName) {
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1116
|
+
const relatedRow = await resolveBelongsToTarget(ctx, rel, ops, depth, path, relName);
|
|
1117
|
+
return relatedRow ? belongsToKeyData(ctx, rel, parentTable, relatedRow) : {};
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* The row a belongsTo create / connect / connectOrCreate points the parent at,
|
|
1121
|
+
* or null when the write names none of the three. Shared by the create path
|
|
1122
|
+
* (which folds the key into the parent INSERT) and the update path (which
|
|
1123
|
+
* UPDATEs the parent's key), so the two cannot disagree about any of them.
|
|
1124
|
+
*/
|
|
1125
|
+
async function resolveBelongsToTarget(ctx, rel, ops, depth, path, relName) {
|
|
1085
1126
|
if (ops.create !== undefined) {
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
relatedRow = (await executeNestedCreate(ctx, rel.to, items[0], depth + 1, [...path, relName]));
|
|
1089
|
-
}
|
|
1127
|
+
const item = singleToOneItem(ops.create, 'create', relName, rel);
|
|
1128
|
+
return (await executeNestedCreate(ctx, rel.to, item, depth + 1, [...path, relName]));
|
|
1090
1129
|
}
|
|
1091
|
-
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
if (!relatedRow) {
|
|
1097
|
-
throw new errors_js_1.ValidationError(`connect on "${relName}": no ${rel.to} row found matching ${(0, errors_js_1.describeTargetForMessage)(target)}.`);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
else if (ops.connectOrCreate !== undefined) {
|
|
1102
|
-
const items = toArray(ops.connectOrCreate);
|
|
1103
|
-
if (items.length > 0) {
|
|
1104
|
-
const op = items[0];
|
|
1105
|
-
relatedRow = (await ctx.tx.table(rel.to).findUnique({ where: op.where }));
|
|
1106
|
-
if (!relatedRow) {
|
|
1107
|
-
// For belongsTo the FK lives on the parent, so the related row is
|
|
1108
|
-
// created plainly (no FK injection) and we read its reference key.
|
|
1109
|
-
relatedRow = (await ctx.tx.table(rel.to).create({ data: op.create }));
|
|
1110
|
-
}
|
|
1130
|
+
if (ops.connect !== undefined) {
|
|
1131
|
+
const target = singleToOneItem(ops.connect, 'connect', relName, rel);
|
|
1132
|
+
const found = (await ctx.tx.table(rel.to).findUnique({ where: target }));
|
|
1133
|
+
if (!found) {
|
|
1134
|
+
throw new errors_js_1.ValidationError(`connect on "${relName}": no ${rel.to} row found matching ${(0, errors_js_1.describeTargetForMessage)(target)}.`);
|
|
1111
1135
|
}
|
|
1136
|
+
return found;
|
|
1112
1137
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
const refField = relatedTable?.reverseColumnMap[refs[i]] ?? refs[i];
|
|
1118
|
-
fkScalars[fkField] = relatedRow[refField];
|
|
1138
|
+
if (ops.connectOrCreate !== undefined) {
|
|
1139
|
+
const op = singleToOneItem(ops.connectOrCreate, 'connectOrCreate', relName, rel);
|
|
1140
|
+
if (!(0, filters_js_1.isPlainRecord)(op?.where) || !(0, filters_js_1.isPlainRecord)(op?.create)) {
|
|
1141
|
+
throw new errors_js_1.ValidationError(`Nested connectOrCreate on "${relName}" requires "where" and "create" fields.`);
|
|
1119
1142
|
}
|
|
1143
|
+
const found = (await ctx.tx.table(rel.to).findUnique({ where: op.where }));
|
|
1144
|
+
// For belongsTo the FK lives on the parent, so the related row is created
|
|
1145
|
+
// plainly (no FK injection) and its reference key is read back.
|
|
1146
|
+
return found ?? (await ctx.tx.table(rel.to).create({ data: op.create }));
|
|
1120
1147
|
}
|
|
1121
|
-
return
|
|
1148
|
+
return null;
|
|
1122
1149
|
}
|
|
1123
|
-
|
|
1150
|
+
/** The parent's own field values that point it at `relatedRow`. */
|
|
1151
|
+
function belongsToKeyData(ctx, rel, parentTable, relatedRow) {
|
|
1124
1152
|
const fks = (0, schema_js_1.normalizeKeyColumns)(rel.foreignKey);
|
|
1125
1153
|
const refs = (0, schema_js_1.normalizeKeyColumns)(rel.referenceKey);
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
for (let i = 0; i < fks.length; i++) {
|
|
1134
|
-
const fkField = ctx.schema.tables[parentTable]?.reverseColumnMap[fks[i]] ?? fks[i];
|
|
1135
|
-
const refField = relatedTable?.reverseColumnMap[refs[i]] ?? refs[i];
|
|
1136
|
-
updateData[fkField] = relatedRow[refField];
|
|
1137
|
-
}
|
|
1138
|
-
const parentMeta = ctx.schema.tables[parentTable];
|
|
1139
|
-
await ctx.tx.table(parentTable).update({
|
|
1140
|
-
where: pkWhere(parentMeta, parentRow),
|
|
1141
|
-
data: updateData,
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1154
|
+
const parentMeta = ctx.schema.tables[parentTable];
|
|
1155
|
+
const relatedTable = ctx.schema.tables[rel.to];
|
|
1156
|
+
const data = {};
|
|
1157
|
+
for (let i = 0; i < fks.length; i++) {
|
|
1158
|
+
const fkField = parentMeta?.reverseColumnMap[fks[i]] ?? fks[i];
|
|
1159
|
+
const refField = relatedTable?.reverseColumnMap[refs[i]] ?? refs[i];
|
|
1160
|
+
data[fkField] = relatedRow === null ? null : relatedRow[refField];
|
|
1144
1161
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1162
|
+
return data;
|
|
1163
|
+
}
|
|
1164
|
+
/** Update path: create / connect / connectOrCreate the related row, then point the parent at it. */
|
|
1165
|
+
async function processBelongsToCreate(ctx, rel, ops, parentRow, parentTable, depth, path, relName) {
|
|
1166
|
+
const relatedRow = await resolveBelongsToTarget(ctx, rel, ops, depth, path, relName);
|
|
1167
|
+
if (!relatedRow)
|
|
1168
|
+
return;
|
|
1169
|
+
await ctx.tx.table(parentTable).update({
|
|
1170
|
+
where: pkWhere(ctx.schema.tables[parentTable], parentRow),
|
|
1171
|
+
data: belongsToKeyData(ctx, rel, parentTable, relatedRow),
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* `disconnect` / `delete` on a belongsTo relation, with Prisma's semantics:
|
|
1176
|
+
* both clear the parent's foreign key, and `delete` then deletes the row it
|
|
1177
|
+
* pointed at. `true` (or `{}`) means the connected row; an object with fields
|
|
1178
|
+
* additionally requires the connected row to match it. Both need a nullable
|
|
1179
|
+
* foreign key: with a NOT NULL one the parent cannot exist pointing at nothing.
|
|
1180
|
+
*
|
|
1181
|
+
* `disconnect: true` when nothing is connected is a no-op: the parent already
|
|
1182
|
+
* points at nothing, which is what was asked. A `delete`, or a `disconnect`
|
|
1183
|
+
* that names fields, asks about a row that is not there, so it is E001.
|
|
1184
|
+
*/
|
|
1185
|
+
async function processBelongsToDisconnect(ctx, rel, arg, relName, parentRow, parentTable, op) {
|
|
1186
|
+
const parentMeta = ctx.schema.tables[parentTable];
|
|
1187
|
+
const fks = (0, schema_js_1.normalizeKeyColumns)(rel.foreignKey);
|
|
1188
|
+
const nullable = fks.every((fk) => parentMeta.columns.find((c) => c.name === fk)?.nullable ?? false);
|
|
1189
|
+
if (!nullable) {
|
|
1190
|
+
throw new errors_js_1.ValidationError(`Cannot ${op} "${relName}": foreign key column(s) ${fks.join(', ')} on "${parentTable}" are NOT NULL, so ` +
|
|
1191
|
+
`every "${parentTable}" row must point at a "${rel.to}". Connect it to a different "${rel.to}" instead` +
|
|
1192
|
+
`${op === 'delete' ? `, or delete the "${rel.to}" row directly once nothing points at it` : ''}.`);
|
|
1193
|
+
}
|
|
1194
|
+
const item = singleToOneItem(arg, op, relName, rel);
|
|
1195
|
+
// `{}` is the documented to-one spelling of `true` (the connected row). An
|
|
1196
|
+
// object whose keys are all undefined is still refused below: that is the
|
|
1197
|
+
// shape of a missing request field, not a choice.
|
|
1198
|
+
const target = (0, filters_js_1.isPlainRecord)(item) && Object.keys(item).length === 0 ? true : item;
|
|
1199
|
+
assertTargetSelectsSomething(target, op, relName, rel);
|
|
1200
|
+
const correlation = belongsToCorrelationWhere(ctx, rel, parentRow, parentTable);
|
|
1201
|
+
if (!correlation) {
|
|
1202
|
+
if (op === 'disconnect' && target === true)
|
|
1203
|
+
return;
|
|
1204
|
+
throw nothingConnected(op, relName, rel, target);
|
|
1205
|
+
}
|
|
1206
|
+
const related = (await ctx.tx.table(rel.to).findFirst({ where: scopeWhereToParent(target, correlation) }));
|
|
1207
|
+
if (!related)
|
|
1208
|
+
throw notRelatedToParent(op, relName, rel, target);
|
|
1209
|
+
await ctx.tx.table(parentTable).update({
|
|
1210
|
+
where: pkWhere(parentMeta, parentRow),
|
|
1211
|
+
data: belongsToKeyData(ctx, rel, parentTable, null),
|
|
1212
|
+
});
|
|
1213
|
+
if (op === 'delete') {
|
|
1214
|
+
// Any other row still pointing at it makes this a foreign-key violation
|
|
1215
|
+
// (E009), which rolls the whole write back.
|
|
1216
|
+
await ctx.tx.table(rel.to).delete({ where: pkWhere(ctx.schema.tables[rel.to], related) });
|
|
1167
1217
|
}
|
|
1168
1218
|
}
|
|
1169
1219
|
// ---------------------------------------------------------------------------
|
|
@@ -1374,11 +1424,12 @@ async function processNestedUpsert(ctx, rel, upsertArg, parentRow, relName) {
|
|
|
1374
1424
|
throw new errors_js_1.ValidationError(`Nested upsert on "${rel.name}" requires "where", "create", and "update" fields.`);
|
|
1375
1425
|
}
|
|
1376
1426
|
assertTargetSelectsSomething(item.where, 'upsert', relName, rel);
|
|
1427
|
+
const update = upsertUpdateData(ctx, rel, item);
|
|
1377
1428
|
const scoped = correlation ? scopeWhereToParent(item.where, correlation) : null;
|
|
1378
1429
|
const existing = scoped ? await ctx.tx.table(rel.to).findUnique({ where: scoped }) : null;
|
|
1379
1430
|
if (existing && scoped) {
|
|
1380
1431
|
try {
|
|
1381
|
-
await ctx.tx.table(rel.to).update({ where: scoped, data:
|
|
1432
|
+
await ctx.tx.table(rel.to).update({ where: scoped, data: update });
|
|
1382
1433
|
}
|
|
1383
1434
|
catch (err) {
|
|
1384
1435
|
rethrowAsNotRelated(err, 'upsert', relName, rel, item.where);
|
|
@@ -1390,6 +1441,19 @@ async function processNestedUpsert(ctx, rel, upsertArg, parentRow, relName) {
|
|
|
1390
1441
|
}
|
|
1391
1442
|
}
|
|
1392
1443
|
}
|
|
1444
|
+
/**
|
|
1445
|
+
* A nested upsert's `update`, with each `expr.excluded(f)` resolved to the
|
|
1446
|
+
* value its `create` supplies: the incoming row of a one-row upsert is
|
|
1447
|
+
* `create`, as it is for a top-level `upsert()`. Resolved before the lookup,
|
|
1448
|
+
* so a reference `create` cannot satisfy is refused whether or not the row
|
|
1449
|
+
* exists.
|
|
1450
|
+
*/
|
|
1451
|
+
function upsertUpdateData(ctx, rel, item) {
|
|
1452
|
+
const tableMeta = ctx.schema.tables[rel.to];
|
|
1453
|
+
if (!tableMeta)
|
|
1454
|
+
return item.update;
|
|
1455
|
+
return (0, expr_js_1.resolveExcludedFromCreate)({ table: rel.to, tableMeta }, item.update, item.create) ?? item.update;
|
|
1456
|
+
}
|
|
1393
1457
|
async function processBelongsToUpdate(ctx, rel, updateArg, parentRow, parentTable) {
|
|
1394
1458
|
const item = updateArg;
|
|
1395
1459
|
if (!item.data) {
|
|
@@ -1428,6 +1492,7 @@ async function processBelongsToUpsert(ctx, rel, upsertArg, parentRow, parentTabl
|
|
|
1428
1492
|
// hasMany/hasOne nested upsert makes: an upsert whose target is out of the
|
|
1429
1493
|
// relation creates a row owned by this parent rather than rewriting a
|
|
1430
1494
|
// stranger's.
|
|
1495
|
+
const update = upsertUpdateData(ctx, rel, item);
|
|
1431
1496
|
const correlation = belongsToCorrelationWhere(ctx, rel, parentRow, parentTable);
|
|
1432
1497
|
// findMany, not findUnique: the scoped where ANDs a non-unique-looking
|
|
1433
1498
|
// correlation onto the caller's selector. The correlation targets the
|
|
@@ -1442,7 +1507,7 @@ async function processBelongsToUpsert(ctx, rel, upsertArg, parentRow, parentTabl
|
|
|
1442
1507
|
: null;
|
|
1443
1508
|
if (existing) {
|
|
1444
1509
|
const relatedMeta = ctx.schema.tables[rel.to];
|
|
1445
|
-
await ctx.tx.table(rel.to).update({ where: pkWhere(relatedMeta, existing), data:
|
|
1510
|
+
await ctx.tx.table(rel.to).update({ where: pkWhere(relatedMeta, existing), data: update });
|
|
1446
1511
|
}
|
|
1447
1512
|
else {
|
|
1448
1513
|
// Create the related row, then update parent's FK to point at it
|
|
@@ -65,11 +65,23 @@ export interface PipelineRunOptions {
|
|
|
65
65
|
* Whether to wrap the pipeline in BEGIN/COMMIT (default: true).
|
|
66
66
|
* When true, all queries execute atomically. On error, ROLLBACK is sent.
|
|
67
67
|
* When false, each query gets its own Sync message for error isolation.
|
|
68
|
+
*
|
|
69
|
+
* A transactional batch that may write holds its COMMIT until every
|
|
70
|
+
* query's transform has run, so a transform that throws (a `...OrThrow`
|
|
71
|
+
* miss, an optimistic-lock conflict) rolls the writes back instead of
|
|
72
|
+
* rejecting after they committed. That costs a second round trip; a batch
|
|
73
|
+
* of plain SELECTs has nothing to undo and keeps the single one.
|
|
68
74
|
*/
|
|
69
75
|
transactional?: boolean;
|
|
70
76
|
/** Timeout in milliseconds. If exceeded, the connection is destroyed. */
|
|
71
77
|
timeout?: number;
|
|
72
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Whether a transactional batch must hold its COMMIT until the transforms have
|
|
81
|
+
* run. Anything that is not a plain SELECT counts as a write, so a statement
|
|
82
|
+
* this cannot classify costs a round trip rather than correctness.
|
|
83
|
+
*/
|
|
84
|
+
export declare function batchMayWrite(queries: readonly DeferredQuery<unknown>[]): boolean;
|
|
73
85
|
/**
|
|
74
86
|
* Execute multiple queries using the Postgres extended-query pipeline protocol.
|
|
75
87
|
*
|