@prisma/client-engine-runtime 7.5.0-dev.5 → 7.5.0-dev.50
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/dist/events.d.ts +1 -1
- package/dist/index.d.mts +24 -10
- package/dist/index.d.ts +24 -10
- package/dist/index.js +232 -32
- package/dist/index.mjs +231 -31
- package/dist/interpreter/in-memory-processing.d.ts +1 -1
- package/dist/interpreter/query-interpreter.d.ts +2 -1
- package/dist/interpreter/render-query.d.ts +2 -1
- package/dist/interpreter/validation.d.ts +2 -1
- package/dist/json-protocol.d.ts +6 -2
- package/dist/query-plan.d.ts +10 -6
- package/dist/tracing.d.ts +2 -1
- package/dist/transaction-manager/transaction.d.ts +1 -0
- package/dist/utils.d.ts +6 -0
- package/package.json +7 -10
package/dist/index.mjs
CHANGED
|
@@ -119,7 +119,10 @@ function normalizeJsonProtocolValues(result) {
|
|
|
119
119
|
function isTaggedValue(value) {
|
|
120
120
|
return value !== null && typeof value == "object" && typeof value["$type"] === "string";
|
|
121
121
|
}
|
|
122
|
-
function normalizeTaggedValue({
|
|
122
|
+
function normalizeTaggedValue({
|
|
123
|
+
$type,
|
|
124
|
+
value
|
|
125
|
+
}) {
|
|
123
126
|
switch ($type) {
|
|
124
127
|
case "BigInt":
|
|
125
128
|
return { $type, value: String(value) };
|
|
@@ -131,6 +134,12 @@ function normalizeTaggedValue({ $type, value }) {
|
|
|
131
134
|
return { $type, value: String(new Decimal2(value)) };
|
|
132
135
|
case "Json":
|
|
133
136
|
return { $type, value: JSON.stringify(JSON.parse(value)) };
|
|
137
|
+
case "Raw":
|
|
138
|
+
return { $type, value };
|
|
139
|
+
case "FieldRef":
|
|
140
|
+
return { $type, value };
|
|
141
|
+
case "Enum":
|
|
142
|
+
return { $type, value };
|
|
134
143
|
default:
|
|
135
144
|
assertNever(value, "Unknown tagged value");
|
|
136
145
|
}
|
|
@@ -142,12 +151,12 @@ function mapObjectValues(object, mapper) {
|
|
|
142
151
|
}
|
|
143
152
|
return result;
|
|
144
153
|
}
|
|
145
|
-
function
|
|
154
|
+
function deserializeJsonObject(result) {
|
|
146
155
|
if (result === null) {
|
|
147
156
|
return result;
|
|
148
157
|
}
|
|
149
158
|
if (Array.isArray(result)) {
|
|
150
|
-
return result.map(
|
|
159
|
+
return result.map(deserializeJsonObject);
|
|
151
160
|
}
|
|
152
161
|
if (typeof result === "object") {
|
|
153
162
|
if (isTaggedValue(result)) {
|
|
@@ -156,7 +165,7 @@ function deserializeJsonResponse(result) {
|
|
|
156
165
|
if (result.constructor !== null && result.constructor.name !== "Object") {
|
|
157
166
|
return result;
|
|
158
167
|
}
|
|
159
|
-
return mapObjectValues(result,
|
|
168
|
+
return mapObjectValues(result, deserializeJsonObject);
|
|
160
169
|
}
|
|
161
170
|
return result;
|
|
162
171
|
}
|
|
@@ -174,6 +183,12 @@ function deserializeTaggedValue({ $type, value }) {
|
|
|
174
183
|
return new Decimal2(value);
|
|
175
184
|
case "Json":
|
|
176
185
|
return JSON.parse(value);
|
|
186
|
+
case "Raw":
|
|
187
|
+
return value;
|
|
188
|
+
case "FieldRef":
|
|
189
|
+
throw new Error("FieldRef tagged values cannot be deserialized to JavaScript values");
|
|
190
|
+
case "Enum":
|
|
191
|
+
return value;
|
|
177
192
|
default:
|
|
178
193
|
assertNever(value, "Unknown tagged value");
|
|
179
194
|
}
|
|
@@ -405,7 +420,7 @@ function resolveArgPlaceholders(args, placeholderValues) {
|
|
|
405
420
|
function convertCompactedRows(rows, compiledBatch, placeholderValues = {}) {
|
|
406
421
|
const keysPerRow = rows.map(
|
|
407
422
|
(item) => compiledBatch.keys.reduce((acc, key) => {
|
|
408
|
-
acc[key] =
|
|
423
|
+
acc[key] = deserializeJsonObject(item[key]);
|
|
409
424
|
return acc;
|
|
410
425
|
}, {})
|
|
411
426
|
);
|
|
@@ -691,6 +706,9 @@ function normalizeDateTime(dt) {
|
|
|
691
706
|
return dtWithTz;
|
|
692
707
|
}
|
|
693
708
|
|
|
709
|
+
// src/interpreter/query-interpreter.ts
|
|
710
|
+
import { klona as klona2 } from "klona";
|
|
711
|
+
|
|
694
712
|
// src/sql-commenter.ts
|
|
695
713
|
import { klona } from "klona";
|
|
696
714
|
function formatSqlComment(tags) {
|
|
@@ -974,8 +992,11 @@ function paginateSingleList(list, { cursor, skip, take }) {
|
|
|
974
992
|
const end = take !== null ? start + take : list.length;
|
|
975
993
|
return list.slice(start, end);
|
|
976
994
|
}
|
|
977
|
-
function getRecordKey(record, fields) {
|
|
978
|
-
|
|
995
|
+
function getRecordKey(record, fields, mappers) {
|
|
996
|
+
const array = fields.map(
|
|
997
|
+
(field, index) => mappers?.[index] ? record[field] !== null ? mappers[index](record[field]) : null : record[field]
|
|
998
|
+
);
|
|
999
|
+
return JSON.stringify(array);
|
|
979
1000
|
}
|
|
980
1001
|
|
|
981
1002
|
// src/query-plan.ts
|
|
@@ -1012,7 +1033,11 @@ function evaluateArg(arg, scope, generators) {
|
|
|
1012
1033
|
if (found === void 0) {
|
|
1013
1034
|
throw new Error(`Missing value for query variable ${arg.prisma__value.name}`);
|
|
1014
1035
|
}
|
|
1015
|
-
arg
|
|
1036
|
+
if (arg.prisma__value.type === "DateTime" && typeof found === "string") {
|
|
1037
|
+
arg = new Date(found);
|
|
1038
|
+
} else {
|
|
1039
|
+
arg = found;
|
|
1040
|
+
}
|
|
1016
1041
|
} else if (isPrismaValueGenerator(arg)) {
|
|
1017
1042
|
const { name, args } = arg.prisma__value;
|
|
1018
1043
|
const generator = generators[name];
|
|
@@ -1070,7 +1095,10 @@ function renderFragment(fragment, placeholderFormat, ctx) {
|
|
|
1070
1095
|
case "stringChunk":
|
|
1071
1096
|
return fragment.chunk;
|
|
1072
1097
|
case "parameterTuple": {
|
|
1073
|
-
const placeholders = fragment.value.length == 0 ? "NULL" : fragment.value.map(() =>
|
|
1098
|
+
const placeholders = fragment.value.length == 0 ? "NULL" : fragment.value.map(() => {
|
|
1099
|
+
const item = formatPlaceholder(placeholderFormat, ctx.placeholderNumber++);
|
|
1100
|
+
return `${fragment.itemPrefix}${item}${fragment.itemSuffix}`;
|
|
1101
|
+
}).join(fragment.itemSeparator);
|
|
1074
1102
|
return `(${placeholders})`;
|
|
1075
1103
|
}
|
|
1076
1104
|
case "parameterTupleList": {
|
|
@@ -1429,7 +1457,7 @@ function doesSatisfyRule(data, rule) {
|
|
|
1429
1457
|
}
|
|
1430
1458
|
}
|
|
1431
1459
|
function renderMessage(data, error) {
|
|
1432
|
-
switch (error.
|
|
1460
|
+
switch (error.errorIdentifier) {
|
|
1433
1461
|
case "RELATION_VIOLATION":
|
|
1434
1462
|
return `The change you are trying to make would violate the required relation '${error.context.relation}' between the \`${error.context.modelA}\` and \`${error.context.modelB}\` models.`;
|
|
1435
1463
|
case "MISSING_RECORD":
|
|
@@ -1449,7 +1477,7 @@ function renderMessage(data, error) {
|
|
|
1449
1477
|
}
|
|
1450
1478
|
}
|
|
1451
1479
|
function getErrorCode2(error) {
|
|
1452
|
-
switch (error.
|
|
1480
|
+
switch (error.errorIdentifier) {
|
|
1453
1481
|
case "RELATION_VIOLATION":
|
|
1454
1482
|
return "P2014";
|
|
1455
1483
|
case "RECORDS_NOT_CONNECTED":
|
|
@@ -1564,7 +1592,7 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1564
1592
|
sum += await this.#withQuerySpanAndEvent(
|
|
1565
1593
|
commentedQuery,
|
|
1566
1594
|
context.queryable,
|
|
1567
|
-
() => context.queryable.executeRaw(commentedQuery).catch(
|
|
1595
|
+
() => context.queryable.executeRaw(cloneObject(commentedQuery)).catch(
|
|
1568
1596
|
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1569
1597
|
)
|
|
1570
1598
|
);
|
|
@@ -1579,7 +1607,7 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1579
1607
|
const result = await this.#withQuerySpanAndEvent(
|
|
1580
1608
|
commentedQuery,
|
|
1581
1609
|
context.queryable,
|
|
1582
|
-
() => context.queryable.queryRaw(commentedQuery).catch(
|
|
1610
|
+
() => context.queryable.queryRaw(cloneObject(commentedQuery)).catch(
|
|
1583
1611
|
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1584
1612
|
)
|
|
1585
1613
|
);
|
|
@@ -1631,7 +1659,7 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1631
1659
|
childRecords: (await this.interpretNode(joinExpr.child, context)).value
|
|
1632
1660
|
}))
|
|
1633
1661
|
);
|
|
1634
|
-
return { value: attachChildrenToParents(parent, children), lastInsertId };
|
|
1662
|
+
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1635
1663
|
}
|
|
1636
1664
|
case "transaction": {
|
|
1637
1665
|
if (!context.transactionManager.enabled) {
|
|
@@ -1678,8 +1706,9 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1678
1706
|
}
|
|
1679
1707
|
case "process": {
|
|
1680
1708
|
const { value, lastInsertId } = await this.interpretNode(node.args.expr, context);
|
|
1681
|
-
|
|
1682
|
-
|
|
1709
|
+
const ops = cloneObject(node.args.operations);
|
|
1710
|
+
evaluateProcessingParameters(ops, context.scope, context.generators);
|
|
1711
|
+
return { value: processRecords(value, ops), lastInsertId };
|
|
1683
1712
|
}
|
|
1684
1713
|
case "initializeRecord": {
|
|
1685
1714
|
const { lastInsertId } = await this.interpretNode(node.args.expr, context);
|
|
@@ -1772,12 +1801,13 @@ function mapField2(value, field) {
|
|
|
1772
1801
|
}
|
|
1773
1802
|
return value;
|
|
1774
1803
|
}
|
|
1775
|
-
function attachChildrenToParents(parentRecords, children) {
|
|
1804
|
+
function attachChildrenToParents(parentRecords, children, canAssumeStrictEquality) {
|
|
1776
1805
|
for (const { joinExpr, childRecords } of children) {
|
|
1777
1806
|
const parentKeys = joinExpr.on.map(([k]) => k);
|
|
1778
1807
|
const childKeys = joinExpr.on.map(([, k]) => k);
|
|
1779
1808
|
const parentMap = {};
|
|
1780
|
-
|
|
1809
|
+
const parentArray = Array.isArray(parentRecords) ? parentRecords : [parentRecords];
|
|
1810
|
+
for (const parent of parentArray) {
|
|
1781
1811
|
const parentRecord = asRecord(parent);
|
|
1782
1812
|
const key = getRecordKey(parentRecord, parentKeys);
|
|
1783
1813
|
if (!parentMap[key]) {
|
|
@@ -1790,11 +1820,12 @@ function attachChildrenToParents(parentRecords, children) {
|
|
|
1790
1820
|
parentRecord[joinExpr.parentField] = [];
|
|
1791
1821
|
}
|
|
1792
1822
|
}
|
|
1823
|
+
const mappers = canAssumeStrictEquality ? void 0 : inferKeyCasts(parentArray, parentKeys);
|
|
1793
1824
|
for (const childRecord of Array.isArray(childRecords) ? childRecords : [childRecords]) {
|
|
1794
1825
|
if (childRecord === null) {
|
|
1795
1826
|
continue;
|
|
1796
1827
|
}
|
|
1797
|
-
const key = getRecordKey(asRecord(childRecord), childKeys);
|
|
1828
|
+
const key = getRecordKey(asRecord(childRecord), childKeys, mappers);
|
|
1798
1829
|
for (const parentRecord of parentMap[key] ?? []) {
|
|
1799
1830
|
if (joinExpr.isRelationUnique) {
|
|
1800
1831
|
parentRecord[joinExpr.parentField] = childRecord;
|
|
@@ -1806,6 +1837,40 @@ function attachChildrenToParents(parentRecords, children) {
|
|
|
1806
1837
|
}
|
|
1807
1838
|
return parentRecords;
|
|
1808
1839
|
}
|
|
1840
|
+
function inferKeyCasts(rows, keys) {
|
|
1841
|
+
function getKeyCast(type) {
|
|
1842
|
+
switch (type) {
|
|
1843
|
+
case "number":
|
|
1844
|
+
return Number;
|
|
1845
|
+
case "string":
|
|
1846
|
+
return String;
|
|
1847
|
+
case "boolean":
|
|
1848
|
+
return Boolean;
|
|
1849
|
+
case "bigint":
|
|
1850
|
+
return BigInt;
|
|
1851
|
+
default:
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
const keyCasts = Array.from({ length: keys.length });
|
|
1856
|
+
let keysFound = 0;
|
|
1857
|
+
for (const parent of rows) {
|
|
1858
|
+
const parentRecord = asRecord(parent);
|
|
1859
|
+
for (const [i, key] of keys.entries()) {
|
|
1860
|
+
if (parentRecord[key] !== null && keyCasts[i] === void 0) {
|
|
1861
|
+
const keyCast = getKeyCast(typeof parentRecord[key]);
|
|
1862
|
+
if (keyCast !== void 0) {
|
|
1863
|
+
keyCasts[i] = keyCast;
|
|
1864
|
+
}
|
|
1865
|
+
keysFound++;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
if (keysFound === keys.length) {
|
|
1869
|
+
break;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
return keyCasts;
|
|
1873
|
+
}
|
|
1809
1874
|
function evalFieldInitializer(initializer, lastInsertId, scope, generators) {
|
|
1810
1875
|
switch (initializer.type) {
|
|
1811
1876
|
case "value":
|
|
@@ -1865,6 +1930,9 @@ function evaluateProcessingParameters(ops, scope, generators) {
|
|
|
1865
1930
|
evaluateProcessingParameters(nested, scope, generators);
|
|
1866
1931
|
}
|
|
1867
1932
|
}
|
|
1933
|
+
function cloneObject(value) {
|
|
1934
|
+
return klona2(value);
|
|
1935
|
+
}
|
|
1868
1936
|
|
|
1869
1937
|
// src/raw-json-protocol.ts
|
|
1870
1938
|
import { Decimal as Decimal4 } from "@prisma/client-runtime-utils";
|
|
@@ -2021,13 +2089,42 @@ var TransactionManager = class {
|
|
|
2021
2089
|
);
|
|
2022
2090
|
}
|
|
2023
2091
|
async #startTransactionImpl(options) {
|
|
2092
|
+
if (options.newTxId) {
|
|
2093
|
+
return await this.#withActiveTransactionLock(options.newTxId, "start", async (existing) => {
|
|
2094
|
+
if (existing.status !== "running") {
|
|
2095
|
+
throw new TransactionInternalConsistencyError(
|
|
2096
|
+
`Transaction in invalid state ${existing.status} when starting a nested transaction.`
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
if (!existing.transaction) {
|
|
2100
|
+
throw new TransactionInternalConsistencyError(
|
|
2101
|
+
`Transaction missing underlying driver transaction when starting a nested transaction.`
|
|
2102
|
+
);
|
|
2103
|
+
}
|
|
2104
|
+
existing.depth += 1;
|
|
2105
|
+
const savepointName = this.#nextSavepointName(existing);
|
|
2106
|
+
existing.savepoints.push(savepointName);
|
|
2107
|
+
try {
|
|
2108
|
+
await this.#requiredCreateSavepoint(existing.transaction)(savepointName);
|
|
2109
|
+
} catch (e) {
|
|
2110
|
+
existing.depth -= 1;
|
|
2111
|
+
existing.savepoints.pop();
|
|
2112
|
+
throw e;
|
|
2113
|
+
}
|
|
2114
|
+
return { id: existing.id };
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2024
2117
|
const transaction = {
|
|
2025
2118
|
id: await randomUUID(),
|
|
2026
2119
|
status: "waiting",
|
|
2027
2120
|
timer: void 0,
|
|
2028
2121
|
timeout: options.timeout,
|
|
2029
2122
|
startedAt: Date.now(),
|
|
2030
|
-
transaction: void 0
|
|
2123
|
+
transaction: void 0,
|
|
2124
|
+
operationQueue: Promise.resolve(),
|
|
2125
|
+
depth: 1,
|
|
2126
|
+
savepoints: [],
|
|
2127
|
+
savepointCounter: 0
|
|
2031
2128
|
};
|
|
2032
2129
|
const abortController = new AbortController();
|
|
2033
2130
|
const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
|
|
@@ -2061,14 +2158,49 @@ var TransactionManager = class {
|
|
|
2061
2158
|
}
|
|
2062
2159
|
async commitTransaction(transactionId) {
|
|
2063
2160
|
return await this.tracingHelper.runInChildSpan("commit_transaction", async () => {
|
|
2064
|
-
|
|
2065
|
-
|
|
2161
|
+
await this.#withActiveTransactionLock(transactionId, "commit", async (txw) => {
|
|
2162
|
+
if (txw.depth > 1) {
|
|
2163
|
+
if (!txw.transaction) throw new TransactionNotFoundError();
|
|
2164
|
+
const savepointName = txw.savepoints.at(-1);
|
|
2165
|
+
if (!savepointName) {
|
|
2166
|
+
throw new TransactionInternalConsistencyError(
|
|
2167
|
+
`Missing savepoint for nested commit. Depth: ${txw.depth}, transactionId: ${txw.id}`
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
try {
|
|
2171
|
+
await this.#releaseSavepoint(txw.transaction, savepointName);
|
|
2172
|
+
} finally {
|
|
2173
|
+
txw.savepoints.pop();
|
|
2174
|
+
txw.depth -= 1;
|
|
2175
|
+
}
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
await this.#closeTransaction(txw, "committed");
|
|
2179
|
+
});
|
|
2066
2180
|
});
|
|
2067
2181
|
}
|
|
2068
2182
|
async rollbackTransaction(transactionId) {
|
|
2069
2183
|
return await this.tracingHelper.runInChildSpan("rollback_transaction", async () => {
|
|
2070
|
-
|
|
2071
|
-
|
|
2184
|
+
await this.#withActiveTransactionLock(transactionId, "rollback", async (txw) => {
|
|
2185
|
+
if (txw.depth > 1) {
|
|
2186
|
+
if (!txw.transaction) throw new TransactionNotFoundError();
|
|
2187
|
+
const savepointName = txw.savepoints.at(-1);
|
|
2188
|
+
if (!savepointName) {
|
|
2189
|
+
throw new TransactionInternalConsistencyError(
|
|
2190
|
+
`Missing savepoint for nested rollback. Depth: ${txw.depth}, transactionId: ${txw.id}`
|
|
2191
|
+
);
|
|
2192
|
+
}
|
|
2193
|
+
try {
|
|
2194
|
+
await this.#requiredRollbackToSavepoint(txw.transaction)(savepointName);
|
|
2195
|
+
await this.#releaseSavepoint(txw.transaction, savepointName);
|
|
2196
|
+
} finally {
|
|
2197
|
+
txw.savepoints.pop();
|
|
2198
|
+
txw.depth -= 1;
|
|
2199
|
+
}
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2202
|
+
await this.#closeTransaction(txw, "rolled_back");
|
|
2203
|
+
});
|
|
2072
2204
|
});
|
|
2073
2205
|
}
|
|
2074
2206
|
async getTransaction(txInfo, operation) {
|
|
@@ -2112,22 +2244,90 @@ var TransactionManager = class {
|
|
|
2112
2244
|
return transaction;
|
|
2113
2245
|
}
|
|
2114
2246
|
async cancelAllTransactions() {
|
|
2115
|
-
await Promise.allSettled(
|
|
2247
|
+
await Promise.allSettled(
|
|
2248
|
+
[...this.transactions.values()].map(
|
|
2249
|
+
(tx) => this.#runSerialized(tx, async () => {
|
|
2250
|
+
const current = this.transactions.get(tx.id);
|
|
2251
|
+
if (current) {
|
|
2252
|
+
await this.#closeTransaction(current, "rolled_back");
|
|
2253
|
+
}
|
|
2254
|
+
})
|
|
2255
|
+
)
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
#nextSavepointName(transaction) {
|
|
2259
|
+
return `prisma_sp_${transaction.savepointCounter++}`;
|
|
2260
|
+
}
|
|
2261
|
+
#requiredCreateSavepoint(transaction) {
|
|
2262
|
+
if (transaction.createSavepoint) {
|
|
2263
|
+
return transaction.createSavepoint.bind(transaction);
|
|
2264
|
+
}
|
|
2265
|
+
throw new TransactionManagerError(
|
|
2266
|
+
`Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): createSavepoint is not implemented.`
|
|
2267
|
+
);
|
|
2268
|
+
}
|
|
2269
|
+
#requiredRollbackToSavepoint(transaction) {
|
|
2270
|
+
if (transaction.rollbackToSavepoint) {
|
|
2271
|
+
return transaction.rollbackToSavepoint.bind(transaction);
|
|
2272
|
+
}
|
|
2273
|
+
throw new TransactionManagerError(
|
|
2274
|
+
`Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): rollbackToSavepoint is not implemented.`
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2277
|
+
async #releaseSavepoint(transaction, name) {
|
|
2278
|
+
if (transaction.releaseSavepoint) {
|
|
2279
|
+
await transaction.releaseSavepoint(name);
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
#debugTransactionAlreadyClosedOnTimeout(transactionId) {
|
|
2283
|
+
debug("Transaction already committed or rolled back when timeout happened.", transactionId);
|
|
2116
2284
|
}
|
|
2117
2285
|
#startTransactionTimeout(transactionId, timeout) {
|
|
2118
2286
|
const timeoutStartedAt = Date.now();
|
|
2119
2287
|
const timer = createTimeoutIfDefined(async () => {
|
|
2120
2288
|
debug("Transaction timed out.", { transactionId, timeoutStartedAt, timeout });
|
|
2121
2289
|
const tx = this.transactions.get(transactionId);
|
|
2122
|
-
if (tx
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2290
|
+
if (!tx) {
|
|
2291
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
await this.#runSerialized(tx, async () => {
|
|
2295
|
+
const current = this.transactions.get(transactionId);
|
|
2296
|
+
if (current && ["running", "waiting"].includes(current.status)) {
|
|
2297
|
+
await this.#closeTransaction(current, "timed_out");
|
|
2298
|
+
} else {
|
|
2299
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
2300
|
+
}
|
|
2301
|
+
});
|
|
2127
2302
|
}, timeout);
|
|
2128
2303
|
timer?.unref?.();
|
|
2129
2304
|
return timer;
|
|
2130
2305
|
}
|
|
2306
|
+
// Any operation that mutates or closes a transaction must run through this lock so
|
|
2307
|
+
// status/savepoint/depth checks and updates happen against a stable view of state.
|
|
2308
|
+
async #withActiveTransactionLock(transactionId, operation, callback) {
|
|
2309
|
+
const tx = this.#getActiveOrClosingTransaction(transactionId, operation);
|
|
2310
|
+
return await this.#runSerialized(tx, async () => {
|
|
2311
|
+
const current = this.#getActiveOrClosingTransaction(transactionId, operation);
|
|
2312
|
+
return await callback(current);
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
// Serializes operations per transaction id to prevent interleaving across awaits.
|
|
2316
|
+
// This avoids races where one operation mutates savepoint/depth state while another
|
|
2317
|
+
// operation is suspended, which could otherwise corrupt cleanup logic.
|
|
2318
|
+
async #runSerialized(tx, callback) {
|
|
2319
|
+
const previousOperation = tx.operationQueue;
|
|
2320
|
+
let releaseOperationLock;
|
|
2321
|
+
tx.operationQueue = new Promise((resolve) => {
|
|
2322
|
+
releaseOperationLock = resolve;
|
|
2323
|
+
});
|
|
2324
|
+
await previousOperation;
|
|
2325
|
+
try {
|
|
2326
|
+
return await callback();
|
|
2327
|
+
} finally {
|
|
2328
|
+
releaseOperationLock();
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2131
2331
|
async #closeTransaction(tx, status) {
|
|
2132
2332
|
const createClosingPromise = async () => {
|
|
2133
2333
|
debug("Closing transaction.", { transactionId: tx.id, status });
|
|
@@ -2214,7 +2414,7 @@ export {
|
|
|
2214
2414
|
UserFacingError,
|
|
2215
2415
|
applySqlCommenters,
|
|
2216
2416
|
convertCompactedRows,
|
|
2217
|
-
|
|
2417
|
+
deserializeJsonObject,
|
|
2218
2418
|
doKeysMatch,
|
|
2219
2419
|
isDeepStrictEqual,
|
|
2220
2420
|
isPrismaValueGenerator,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { InMemoryOps } from '../query-plan';
|
|
2
2
|
export declare function processRecords(value: unknown, ops: InMemoryOps): unknown;
|
|
3
|
-
export declare function getRecordKey(record: {}, fields: string[]): string;
|
|
3
|
+
export declare function getRecordKey(record: {}, fields: readonly string[], mappers?: ((value: unknown) => unknown)[]): string;
|
|
@@ -5,6 +5,7 @@ import { QueryPlanNode } from '../query-plan';
|
|
|
5
5
|
import { type SchemaProvider } from '../schema';
|
|
6
6
|
import { type TracingHelper } from '../tracing';
|
|
7
7
|
import { type TransactionManager } from '../transaction-manager/transaction-manager';
|
|
8
|
+
import { DeepReadonly } from '../utils';
|
|
8
9
|
import { Value } from './scope';
|
|
9
10
|
export type QueryInterpreterTransactionManager = {
|
|
10
11
|
enabled: true;
|
|
@@ -39,6 +40,6 @@ export declare class QueryInterpreter {
|
|
|
39
40
|
provider?: SchemaProvider;
|
|
40
41
|
connectionInfo?: ConnectionInfo;
|
|
41
42
|
}): QueryInterpreter;
|
|
42
|
-
run(queryPlan: QueryPlanNode
|
|
43
|
+
run(queryPlan: DeepReadonly<QueryPlanNode>, options: QueryRuntimeOptions): Promise<unknown>;
|
|
43
44
|
private interpretNode;
|
|
44
45
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SqlQuery } from '@prisma/driver-adapter-utils';
|
|
2
2
|
import { type QueryPlanDbQuery } from '../query-plan';
|
|
3
|
+
import { DeepReadonly } from '../utils';
|
|
3
4
|
import { GeneratorRegistrySnapshot } from './generators';
|
|
4
5
|
import { ScopeBindings } from './scope';
|
|
5
|
-
export declare function renderQuery(dbQuery: QueryPlanDbQuery
|
|
6
|
+
export declare function renderQuery(dbQuery: DeepReadonly<QueryPlanDbQuery>, scope: ScopeBindings, generators: GeneratorRegistrySnapshot, maxChunkSize?: number): DeepReadonly<SqlQuery>[];
|
|
6
7
|
export declare function evaluateArg(arg: unknown, scope: ScopeBindings, generators: GeneratorRegistrySnapshot): unknown;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { DataRule, ValidationError } from '../query-plan';
|
|
2
|
-
|
|
2
|
+
import { DeepReadonly } from '../utils';
|
|
3
|
+
export declare function performValidation(data: unknown, rules: DeepReadonly<DataRule[]>, error: ValidationError): void;
|
|
3
4
|
export declare function doesSatisfyRule(data: unknown, rule: DataRule): boolean;
|
package/dist/json-protocol.d.ts
CHANGED
|
@@ -29,10 +29,14 @@ export type JsonTaggedValue = {
|
|
|
29
29
|
$type: 'Json';
|
|
30
30
|
value: string;
|
|
31
31
|
};
|
|
32
|
-
export type
|
|
32
|
+
export type RawTaggedValue = {
|
|
33
|
+
$type: 'Raw';
|
|
34
|
+
value: unknown;
|
|
35
|
+
};
|
|
36
|
+
export type JsonInputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | FieldRefTaggedValue | JsonTaggedValue | EnumTaggedValue | RawTaggedValue;
|
|
33
37
|
export type JsonOutputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | JsonTaggedValue;
|
|
34
38
|
export type JsOutputValue = null | string | number | boolean | bigint | Uint8Array | Date | Decimal | JsOutputValue[] | {
|
|
35
39
|
[key: string]: JsOutputValue;
|
|
36
40
|
};
|
|
37
41
|
export declare function normalizeJsonProtocolValues(result: unknown): unknown;
|
|
38
|
-
export declare function
|
|
42
|
+
export declare function deserializeJsonObject(result: unknown): unknown;
|
package/dist/query-plan.d.ts
CHANGED
|
@@ -56,6 +56,9 @@ export type Fragment = {
|
|
|
56
56
|
type: 'parameter';
|
|
57
57
|
} | {
|
|
58
58
|
type: 'parameterTuple';
|
|
59
|
+
itemPrefix: string;
|
|
60
|
+
itemSeparator: string;
|
|
61
|
+
itemSuffix: string;
|
|
59
62
|
} | {
|
|
60
63
|
type: 'parameterTupleList';
|
|
61
64
|
itemPrefix: string;
|
|
@@ -121,6 +124,7 @@ export type QueryPlanNode = {
|
|
|
121
124
|
args: {
|
|
122
125
|
parent: QueryPlanNode;
|
|
123
126
|
children: JoinExpression[];
|
|
127
|
+
canAssumeStrictEquality: boolean;
|
|
124
128
|
};
|
|
125
129
|
} | {
|
|
126
130
|
type: 'mapField';
|
|
@@ -227,14 +231,14 @@ export type DataRule = {
|
|
|
227
231
|
type: 'never';
|
|
228
232
|
};
|
|
229
233
|
export type ValidationError = {
|
|
230
|
-
|
|
234
|
+
errorIdentifier: 'RELATION_VIOLATION';
|
|
231
235
|
context: {
|
|
232
236
|
relation: string;
|
|
233
237
|
modelA: string;
|
|
234
238
|
modelB: string;
|
|
235
239
|
};
|
|
236
240
|
} | {
|
|
237
|
-
|
|
241
|
+
errorIdentifier: 'MISSING_RELATED_RECORD';
|
|
238
242
|
context: {
|
|
239
243
|
model: string;
|
|
240
244
|
relation: string;
|
|
@@ -243,24 +247,24 @@ export type ValidationError = {
|
|
|
243
247
|
neededFor?: string;
|
|
244
248
|
};
|
|
245
249
|
} | {
|
|
246
|
-
|
|
250
|
+
errorIdentifier: 'MISSING_RECORD';
|
|
247
251
|
context: {
|
|
248
252
|
operation: string;
|
|
249
253
|
};
|
|
250
254
|
} | {
|
|
251
|
-
|
|
255
|
+
errorIdentifier: 'INCOMPLETE_CONNECT_INPUT';
|
|
252
256
|
context: {
|
|
253
257
|
expectedRows: number;
|
|
254
258
|
};
|
|
255
259
|
} | {
|
|
256
|
-
|
|
260
|
+
errorIdentifier: 'INCOMPLETE_CONNECT_OUTPUT';
|
|
257
261
|
context: {
|
|
258
262
|
expectedRows: number;
|
|
259
263
|
relation: string;
|
|
260
264
|
relationType: string;
|
|
261
265
|
};
|
|
262
266
|
} | {
|
|
263
|
-
|
|
267
|
+
errorIdentifier: 'RECORDS_NOT_CONNECTED';
|
|
264
268
|
context: {
|
|
265
269
|
relation: string;
|
|
266
270
|
parent: string;
|
package/dist/tracing.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type Context, type Span, type SpanOptions } from '@opentelemetry/api';
|
|
|
2
2
|
import type { SqlQuery } from '@prisma/driver-adapter-utils';
|
|
3
3
|
import { QueryEvent } from './events';
|
|
4
4
|
import type { SchemaProvider } from './schema';
|
|
5
|
+
import { DeepReadonly } from './utils';
|
|
5
6
|
export type SpanCallback<R> = (span?: Span, context?: Context) => R;
|
|
6
7
|
export type ExtendedSpanOptions = SpanOptions & {
|
|
7
8
|
name: string;
|
|
@@ -13,7 +14,7 @@ export interface TracingHelper {
|
|
|
13
14
|
export declare const noopTracingHelper: TracingHelper;
|
|
14
15
|
export declare function providerToOtelSystem(provider: SchemaProvider): string;
|
|
15
16
|
export declare function withQuerySpanAndEvent<T>({ query, tracingHelper, provider, onQuery, execute, }: {
|
|
16
|
-
query: SqlQuery
|
|
17
|
+
query: DeepReadonly<SqlQuery>;
|
|
17
18
|
tracingHelper: TracingHelper;
|
|
18
19
|
provider: SchemaProvider;
|
|
19
20
|
onQuery?: (event: QueryEvent) => void;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export type DeepReadonly<T> = T extends undefined | null | boolean | string | number | symbol | Function | Date ? T : T extends Array<infer U> ? ReadonlyArray<DeepReadonly<U>> : unknown extends T ? unknown : {
|
|
2
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
3
|
+
};
|
|
4
|
+
export type DeepUnreadonly<T> = T extends undefined | null | boolean | string | number | symbol | Function | Date ? T : T extends ReadonlyArray<infer U> ? Array<DeepUnreadonly<U>> : unknown extends T ? unknown : {
|
|
5
|
+
-readonly [K in keyof T]: DeepUnreadonly<T[K]>;
|
|
6
|
+
};
|
|
1
7
|
export declare function assertNever(_: never, message: string): never;
|
|
2
8
|
/**
|
|
3
9
|
* Checks if two objects are deeply equal, recursively checking all properties for strict equality.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/client-engine-runtime",
|
|
3
|
-
"version": "7.5.0-dev.
|
|
3
|
+
"version": "7.5.0-dev.50",
|
|
4
4
|
"description": "This package is intended for Prisma's internal use",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -31,19 +31,16 @@
|
|
|
31
31
|
"nanoid": "5.1.5",
|
|
32
32
|
"ulid": "3.0.0",
|
|
33
33
|
"uuid": "11.1.0",
|
|
34
|
-
"@prisma/client-runtime-utils": "7.5.0-dev.
|
|
35
|
-
"@prisma/
|
|
36
|
-
"@prisma/
|
|
37
|
-
"@prisma/
|
|
34
|
+
"@prisma/client-runtime-utils": "7.5.0-dev.50",
|
|
35
|
+
"@prisma/debug": "7.5.0-dev.50",
|
|
36
|
+
"@prisma/driver-adapter-utils": "7.5.0-dev.50",
|
|
37
|
+
"@prisma/sqlcommenter": "7.5.0-dev.50"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@codspeed/benchmark.js-plugin": "4.0.0",
|
|
41
41
|
"@types/benchmark": "2.1.5",
|
|
42
|
-
"@types/jest": "29.5.14",
|
|
43
42
|
"@types/node": "~20.19.24",
|
|
44
|
-
"benchmark": "2.1.4"
|
|
45
|
-
"jest": "29.7.0",
|
|
46
|
-
"jest-junit": "16.0.0"
|
|
43
|
+
"benchmark": "2.1.4"
|
|
47
44
|
},
|
|
48
45
|
"files": [
|
|
49
46
|
"dist"
|
|
@@ -52,6 +49,6 @@
|
|
|
52
49
|
"scripts": {
|
|
53
50
|
"dev": "DEV=true tsx helpers/build.ts",
|
|
54
51
|
"build": "tsx helpers/build.ts",
|
|
55
|
-
"test": "
|
|
52
|
+
"test": "vitest run"
|
|
56
53
|
}
|
|
57
54
|
}
|