@prisma/client-engine-runtime 7.10.0-dev.8 → 7.10.0-integration-prisma7-project-closeout.8
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/index.d.mts +123 -91
- package/dist/index.d.ts +123 -91
- package/dist/index.js +504 -151
- package/dist/index.mjs +504 -151
- package/dist/interpreter/query-interpreter.d.ts +20 -1
- package/dist/interpreter/query-interpreter.test.d.ts +1 -0
- package/dist/query-plan.d.ts +61 -31
- package/dist/utils.d.ts +15 -0
- package/dist/utils.test.d.ts +1 -0
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -56,6 +56,12 @@ var import_client_runtime_utils2 = require("@prisma/client-runtime-utils");
|
|
|
56
56
|
|
|
57
57
|
// src/utils.ts
|
|
58
58
|
var import_client_runtime_utils = require("@prisma/client-runtime-utils");
|
|
59
|
+
function isUint8Array(value) {
|
|
60
|
+
return ArrayBuffer.isView(value) && Object.prototype.toString.call(value) === "[object Uint8Array]";
|
|
61
|
+
}
|
|
62
|
+
function isDate(value) {
|
|
63
|
+
return Object.prototype.toString.call(value) === "[object Date]";
|
|
64
|
+
}
|
|
59
65
|
function assertNever(_, message) {
|
|
60
66
|
throw new Error(message);
|
|
61
67
|
}
|
|
@@ -74,11 +80,11 @@ function doKeysMatch(lhs, rhs) {
|
|
|
74
80
|
const lhsDecimal = asDecimal(lhs[key]);
|
|
75
81
|
const rhsDecimal = asDecimal(rhs[key]);
|
|
76
82
|
return lhsDecimal && rhsDecimal && lhsDecimal.equals(rhsDecimal);
|
|
77
|
-
} else if (lhs[key]
|
|
83
|
+
} else if (isUint8Array(lhs[key]) || isUint8Array(rhs[key])) {
|
|
78
84
|
const lhsBuffer = asBuffer(lhs[key]);
|
|
79
85
|
const rhsBuffer = asBuffer(rhs[key]);
|
|
80
86
|
return lhsBuffer && rhsBuffer && lhsBuffer.equals(rhsBuffer);
|
|
81
|
-
} else if (lhs[key]
|
|
87
|
+
} else if (isDate(lhs[key]) || isDate(rhs[key])) {
|
|
82
88
|
return asDate(lhs[key])?.getTime() === asDate(rhs[key])?.getTime();
|
|
83
89
|
} else if (typeof lhs[key] === "bigint" || typeof rhs[key] === "bigint") {
|
|
84
90
|
return asBigInt(lhs[key]) === asBigInt(rhs[key]);
|
|
@@ -100,7 +106,7 @@ function asDecimal(value) {
|
|
|
100
106
|
function asBuffer(value) {
|
|
101
107
|
if (Buffer.isBuffer(value)) {
|
|
102
108
|
return value;
|
|
103
|
-
} else if (value
|
|
109
|
+
} else if (isUint8Array(value)) {
|
|
104
110
|
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
105
111
|
} else if (typeof value === "string") {
|
|
106
112
|
return Buffer.from(value, "base64");
|
|
@@ -109,7 +115,7 @@ function asBuffer(value) {
|
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
function asDate(value) {
|
|
112
|
-
if (value
|
|
118
|
+
if (isDate(value)) {
|
|
113
119
|
return value;
|
|
114
120
|
} else if (typeof value === "string" || typeof value === "number") {
|
|
115
121
|
return new Date(value);
|
|
@@ -145,6 +151,16 @@ function safeJsonStringify(obj) {
|
|
|
145
151
|
return val;
|
|
146
152
|
});
|
|
147
153
|
}
|
|
154
|
+
var MAX_PUSH_SPREAD_ARGS = 8192;
|
|
155
|
+
function appendToArray(target, source) {
|
|
156
|
+
if (source.length <= MAX_PUSH_SPREAD_ARGS) {
|
|
157
|
+
target.push(...source);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
for (let i = 0; i < source.length; i += MAX_PUSH_SPREAD_ARGS) {
|
|
161
|
+
target.push(...source.slice(i, i + MAX_PUSH_SPREAD_ARGS));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
148
164
|
|
|
149
165
|
// src/json-protocol.ts
|
|
150
166
|
function normalizeJsonProtocolValues(result) {
|
|
@@ -226,10 +242,8 @@ function deserializeTaggedValue({ $type, value }) {
|
|
|
226
242
|
switch ($type) {
|
|
227
243
|
case "BigInt":
|
|
228
244
|
return BigInt(value);
|
|
229
|
-
case "Bytes":
|
|
230
|
-
|
|
231
|
-
return new Uint8Array(buffer, byteOffset, byteLength);
|
|
232
|
-
}
|
|
245
|
+
case "Bytes":
|
|
246
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
233
247
|
case "DateTime":
|
|
234
248
|
return new Date(value);
|
|
235
249
|
case "Decimal":
|
|
@@ -277,7 +291,11 @@ function rethrowAsUserFacing(error) {
|
|
|
277
291
|
const code = getErrorCode(error);
|
|
278
292
|
const message = renderErrorMessage(error);
|
|
279
293
|
if (code !== void 0 && message !== void 0) {
|
|
280
|
-
|
|
294
|
+
const meta = { driverAdapterError: error };
|
|
295
|
+
if (error.cause.kind === "UniqueConstraintViolation" && error.cause.table) {
|
|
296
|
+
meta.table = error.cause.table;
|
|
297
|
+
}
|
|
298
|
+
throw new UserFacingError(message, code, meta);
|
|
281
299
|
}
|
|
282
300
|
if (isGenericDatabaseErrorKind(error.cause.kind)) {
|
|
283
301
|
throw buildUnmappedDatabaseUserFacingError(error);
|
|
@@ -343,6 +361,7 @@ function getErrorCode(err) {
|
|
|
343
361
|
case "UniqueConstraintViolation":
|
|
344
362
|
return "P2002";
|
|
345
363
|
case "ForeignKeyConstraintViolation":
|
|
364
|
+
case "RestrictViolation":
|
|
346
365
|
return "P2003";
|
|
347
366
|
case "InvalidInputValue":
|
|
348
367
|
return "P2007";
|
|
@@ -415,6 +434,7 @@ function renderErrorMessage(err) {
|
|
|
415
434
|
case "UniqueConstraintViolation":
|
|
416
435
|
return `Unique constraint failed on the ${renderConstraint(err.cause.constraint)}`;
|
|
417
436
|
case "ForeignKeyConstraintViolation":
|
|
437
|
+
case "RestrictViolation":
|
|
418
438
|
return `Foreign key constraint violated on the ${renderConstraint(err.cause.constraint)}`;
|
|
419
439
|
case "UnsupportedNativeDataType":
|
|
420
440
|
return `Failed to deserialize column of type '${err.cause.type}'. If you're using $queryRaw and this column is explicitly marked as \`Unsupported\` in your Prisma schema, try casting this column to any supported Prisma type such as \`String\`.`;
|
|
@@ -691,7 +711,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
691
711
|
throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
|
|
692
712
|
}
|
|
693
713
|
}
|
|
694
|
-
if (Array.isArray(value) || value
|
|
714
|
+
if (Array.isArray(value) || isUint8Array(value)) {
|
|
695
715
|
for (const byte of value) {
|
|
696
716
|
if (byte !== 0) return true;
|
|
697
717
|
}
|
|
@@ -708,7 +728,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
708
728
|
if (typeof value === "string") {
|
|
709
729
|
return { $type: "DateTime", value: normalizeDateTime(value) };
|
|
710
730
|
}
|
|
711
|
-
if (typeof value === "number" || value
|
|
731
|
+
if (typeof value === "number" || isDate(value)) {
|
|
712
732
|
return { $type: "DateTime", value };
|
|
713
733
|
}
|
|
714
734
|
throw new DataMapperError(`Expected a date in column '${columnName}', got ${typeof value}: ${value}`);
|
|
@@ -739,7 +759,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
739
759
|
if (Array.isArray(value)) {
|
|
740
760
|
return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
|
|
741
761
|
}
|
|
742
|
-
if (value
|
|
762
|
+
if (isUint8Array(value)) {
|
|
743
763
|
return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
|
|
744
764
|
}
|
|
745
765
|
throw new DataMapperError(`Expected a byte array in column '${columnName}', got ${typeof value}: ${value}`);
|
|
@@ -787,6 +807,7 @@ function normalizeDateTime(dt) {
|
|
|
787
807
|
}
|
|
788
808
|
|
|
789
809
|
// src/interpreter/query-interpreter.ts
|
|
810
|
+
var import_debug = require("@prisma/debug");
|
|
790
811
|
var import_klona2 = require("klona");
|
|
791
812
|
|
|
792
813
|
// src/sql-commenter.ts
|
|
@@ -1148,8 +1169,9 @@ function renderTemplateSql(fragments, placeholderFormat, params, argTypes) {
|
|
|
1148
1169
|
if (fragment.type === "stringChunk") {
|
|
1149
1170
|
continue;
|
|
1150
1171
|
}
|
|
1151
|
-
const
|
|
1152
|
-
const added =
|
|
1172
|
+
const fragmentParams = Array.from(flattenedFragmentParams(fragment));
|
|
1173
|
+
const added = fragmentParams.length;
|
|
1174
|
+
appendToArray(flattenedParams, fragmentParams);
|
|
1153
1175
|
if (fragment.argType.arity === "tuple") {
|
|
1154
1176
|
if (added % fragment.argType.elements.length !== 0) {
|
|
1155
1177
|
throw new Error(
|
|
@@ -1578,6 +1600,7 @@ function getErrorCode2(error) {
|
|
|
1578
1600
|
}
|
|
1579
1601
|
|
|
1580
1602
|
// src/interpreter/query-interpreter.ts
|
|
1603
|
+
var debug = (0, import_debug.Debug)("prisma:client:queryInterpreter");
|
|
1581
1604
|
var QueryInterpreter = class _QueryInterpreter {
|
|
1582
1605
|
#onQuery;
|
|
1583
1606
|
#generators = new GeneratorRegistry();
|
|
@@ -1612,17 +1635,27 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1612
1635
|
});
|
|
1613
1636
|
}
|
|
1614
1637
|
async run(queryPlan, options) {
|
|
1615
|
-
const
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1638
|
+
const generators = this.#generators.snapshot();
|
|
1639
|
+
const context = { ...options, generators };
|
|
1640
|
+
const purified = purifyQueryPlan(queryPlan, (node) => this.interpretNode(node, context))?.catch(
|
|
1641
|
+
(e) => rethrowAsUserFacing(e)
|
|
1642
|
+
);
|
|
1643
|
+
if (purified) {
|
|
1644
|
+
try {
|
|
1645
|
+
return this.#interpretPureNode(await purified, context.scope, generators).value;
|
|
1646
|
+
} catch (e) {
|
|
1647
|
+
rethrowAsUserFacing(e);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
const { value } = await this.interpretNode(queryPlan, context).catch((e) => rethrowAsUserFacing(e));
|
|
1619
1651
|
return value;
|
|
1620
1652
|
}
|
|
1621
1653
|
async interpretNode(node, context) {
|
|
1622
1654
|
switch (node.type) {
|
|
1623
1655
|
case "value": {
|
|
1624
1656
|
return {
|
|
1625
|
-
value: evaluateArg(node.args, context.scope, context.generators)
|
|
1657
|
+
value: evaluateArg(node.args, context.scope, context.generators),
|
|
1658
|
+
lastInsertId: node.lastInsertId
|
|
1626
1659
|
};
|
|
1627
1660
|
}
|
|
1628
1661
|
case "seq": {
|
|
@@ -1632,9 +1665,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1632
1665
|
}
|
|
1633
1666
|
return result ?? { value: void 0 };
|
|
1634
1667
|
}
|
|
1635
|
-
case "get": {
|
|
1636
|
-
return { value: context.scope[node.args.name] };
|
|
1637
|
-
}
|
|
1638
1668
|
case "let": {
|
|
1639
1669
|
const nestedScope = Object.create(context.scope);
|
|
1640
1670
|
for (const binding of node.args.bindings) {
|
|
@@ -1643,15 +1673,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1643
1673
|
}
|
|
1644
1674
|
return this.interpretNode(node.args.expr, { ...context, scope: nestedScope });
|
|
1645
1675
|
}
|
|
1646
|
-
case "getFirstNonEmpty": {
|
|
1647
|
-
for (const name of node.args.names) {
|
|
1648
|
-
const value = context.scope[name];
|
|
1649
|
-
if (!isEmpty(value)) {
|
|
1650
|
-
return { value };
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
return { value: [] };
|
|
1654
|
-
}
|
|
1655
1676
|
case "concat": {
|
|
1656
1677
|
const parts = await Promise.all(
|
|
1657
1678
|
node.args.map((arg) => this.interpretNode(arg, context).then((res) => res.value))
|
|
@@ -1670,42 +1691,46 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1670
1691
|
}
|
|
1671
1692
|
case "execute": {
|
|
1672
1693
|
const queries = renderQuery(node.args, context.scope, context.generators, this.#maxChunkSize());
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
const
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
(
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1694
|
+
return this.#withChunkTransaction(queries.length, context, async (context2) => {
|
|
1695
|
+
let sum = 0;
|
|
1696
|
+
for (const query of queries) {
|
|
1697
|
+
const commentedQuery = applyComments(query, context2.sqlCommenter);
|
|
1698
|
+
sum += await this.#withQuerySpanAndEvent(
|
|
1699
|
+
commentedQuery,
|
|
1700
|
+
context2.queryable,
|
|
1701
|
+
() => context2.queryable.executeRaw(cloneObject(commentedQuery)).catch(
|
|
1702
|
+
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1703
|
+
)
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
return { value: sum };
|
|
1707
|
+
});
|
|
1685
1708
|
}
|
|
1686
1709
|
case "query": {
|
|
1687
1710
|
const queries = renderQuery(node.args, context.scope, context.generators, this.#maxChunkSize());
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
(
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
results
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1711
|
+
return this.#withChunkTransaction(queries.length, context, async (context2) => {
|
|
1712
|
+
let results;
|
|
1713
|
+
for (const query of queries) {
|
|
1714
|
+
const commentedQuery = applyComments(query, context2.sqlCommenter);
|
|
1715
|
+
const result = await this.#withQuerySpanAndEvent(
|
|
1716
|
+
commentedQuery,
|
|
1717
|
+
context2.queryable,
|
|
1718
|
+
() => context2.queryable.queryRaw(cloneObject(commentedQuery)).catch(
|
|
1719
|
+
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1720
|
+
)
|
|
1721
|
+
);
|
|
1722
|
+
if (results === void 0) {
|
|
1723
|
+
results = result;
|
|
1724
|
+
} else {
|
|
1725
|
+
appendToArray(results.rows, result.rows);
|
|
1726
|
+
results.lastInsertId = result.lastInsertId;
|
|
1727
|
+
}
|
|
1703
1728
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
};
|
|
1729
|
+
return {
|
|
1730
|
+
value: node.args.type === "rawSql" ? this.#rawSerializer(results) : this.#serializer(results),
|
|
1731
|
+
lastInsertId: results?.lastInsertId
|
|
1732
|
+
};
|
|
1733
|
+
});
|
|
1709
1734
|
}
|
|
1710
1735
|
case "reverse": {
|
|
1711
1736
|
const { value, lastInsertId } = await this.interpretNode(node.args, context);
|
|
@@ -1746,20 +1771,7 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1746
1771
|
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1747
1772
|
}
|
|
1748
1773
|
case "transaction": {
|
|
1749
|
-
|
|
1750
|
-
return this.interpretNode(node.args, context);
|
|
1751
|
-
}
|
|
1752
|
-
const transactionManager = context.transactionManager.manager;
|
|
1753
|
-
const transactionInfo = await transactionManager.startInternalTransaction();
|
|
1754
|
-
const transaction = await transactionManager.getTransaction(transactionInfo, "query");
|
|
1755
|
-
try {
|
|
1756
|
-
const value = await this.interpretNode(node.args, { ...context, queryable: transaction });
|
|
1757
|
-
await transactionManager.commitTransaction(transactionInfo.id);
|
|
1758
|
-
return value;
|
|
1759
|
-
} catch (e) {
|
|
1760
|
-
await transactionManager.rollbackTransaction(transactionInfo.id);
|
|
1761
|
-
throw e;
|
|
1762
|
-
}
|
|
1774
|
+
return this.#withInternalTransaction(context, (context2) => this.interpretNode(node.args, context2));
|
|
1763
1775
|
}
|
|
1764
1776
|
case "dataMap": {
|
|
1765
1777
|
const { value, lastInsertId } = await this.interpretNode(node.args.expr, context);
|
|
@@ -1778,9 +1790,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1778
1790
|
return await this.interpretNode(node.args.else, context);
|
|
1779
1791
|
}
|
|
1780
1792
|
}
|
|
1781
|
-
case "unit": {
|
|
1782
|
-
return { value: void 0 };
|
|
1783
|
-
}
|
|
1784
1793
|
case "diff": {
|
|
1785
1794
|
const { value: from } = await this.interpretNode(node.args.from, context);
|
|
1786
1795
|
const { value: to } = await this.interpretNode(node.args.to, context);
|
|
@@ -1810,10 +1819,185 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1810
1819
|
}
|
|
1811
1820
|
return { value: record, lastInsertId };
|
|
1812
1821
|
}
|
|
1822
|
+
default:
|
|
1823
|
+
return this.#interpretPureNode(node, context.scope, context.generators);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
#interpretPureNode(node, scope, generators) {
|
|
1827
|
+
switch (node.type) {
|
|
1828
|
+
case "value": {
|
|
1829
|
+
return { value: evaluateArg(node.args, scope, generators), lastInsertId: node.lastInsertId };
|
|
1830
|
+
}
|
|
1831
|
+
case "seq": {
|
|
1832
|
+
let result;
|
|
1833
|
+
for (const arg of node.args) {
|
|
1834
|
+
result = this.#interpretPureNode(arg, scope, generators);
|
|
1835
|
+
}
|
|
1836
|
+
return result ?? { value: void 0 };
|
|
1837
|
+
}
|
|
1838
|
+
case "get": {
|
|
1839
|
+
return { value: scope[node.args.name] };
|
|
1840
|
+
}
|
|
1841
|
+
case "let": {
|
|
1842
|
+
const nestedScope = Object.create(scope);
|
|
1843
|
+
for (const binding of node.args.bindings) {
|
|
1844
|
+
const { value } = this.#interpretPureNode(binding.expr, nestedScope, generators);
|
|
1845
|
+
nestedScope[binding.name] = value;
|
|
1846
|
+
}
|
|
1847
|
+
return this.#interpretPureNode(node.args.expr, nestedScope, generators);
|
|
1848
|
+
}
|
|
1849
|
+
case "getFirstNonEmpty": {
|
|
1850
|
+
for (const name of node.args.names) {
|
|
1851
|
+
const value = scope[name];
|
|
1852
|
+
if (!isEmpty(value)) {
|
|
1853
|
+
return { value };
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
return { value: [] };
|
|
1857
|
+
}
|
|
1858
|
+
case "concat": {
|
|
1859
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1860
|
+
return {
|
|
1861
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : []
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
case "sum": {
|
|
1865
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1866
|
+
return {
|
|
1867
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => asNumber2(acc) + asNumber2(part)) : 0
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
case "reverse": {
|
|
1871
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1872
|
+
return { value: Array.isArray(value) ? value.reverse() : value, lastInsertId };
|
|
1873
|
+
}
|
|
1874
|
+
case "unique": {
|
|
1875
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1876
|
+
if (!Array.isArray(value)) {
|
|
1877
|
+
return { value, lastInsertId };
|
|
1878
|
+
}
|
|
1879
|
+
if (value.length > 1) {
|
|
1880
|
+
throw new Error(`Expected zero or one element, got ${value.length}`);
|
|
1881
|
+
}
|
|
1882
|
+
return { value: value[0] ?? null, lastInsertId };
|
|
1883
|
+
}
|
|
1884
|
+
case "required": {
|
|
1885
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1886
|
+
if (isEmpty(value)) {
|
|
1887
|
+
throw new Error("Required value is empty");
|
|
1888
|
+
}
|
|
1889
|
+
return { value, lastInsertId };
|
|
1890
|
+
}
|
|
1891
|
+
case "mapField": {
|
|
1892
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.records, scope, generators);
|
|
1893
|
+
return { value: mapField2(value, node.args.field), lastInsertId };
|
|
1894
|
+
}
|
|
1895
|
+
case "join": {
|
|
1896
|
+
const { value: parent, lastInsertId } = this.#interpretPureNode(node.args.parent, scope, generators);
|
|
1897
|
+
if (parent === null) {
|
|
1898
|
+
return { value: null, lastInsertId };
|
|
1899
|
+
}
|
|
1900
|
+
const children = node.args.children.map((joinExpr) => ({
|
|
1901
|
+
joinExpr,
|
|
1902
|
+
childRecords: this.#interpretPureNode(joinExpr.child, scope, generators).value
|
|
1903
|
+
}));
|
|
1904
|
+
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1905
|
+
}
|
|
1906
|
+
case "dataMap": {
|
|
1907
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1908
|
+
return { value: applyDataMap(value, node.args.structure, node.args.enums), lastInsertId };
|
|
1909
|
+
}
|
|
1910
|
+
case "validate": {
|
|
1911
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1912
|
+
performValidation(value, node.args.rules, node.args);
|
|
1913
|
+
return { value, lastInsertId };
|
|
1914
|
+
}
|
|
1915
|
+
case "if": {
|
|
1916
|
+
const { value } = this.#interpretPureNode(node.args.value, scope, generators);
|
|
1917
|
+
if (doesSatisfyRule(value, node.args.rule)) {
|
|
1918
|
+
return this.#interpretPureNode(node.args.then, scope, generators);
|
|
1919
|
+
} else {
|
|
1920
|
+
return this.#interpretPureNode(node.args.else, scope, generators);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
case "unit": {
|
|
1924
|
+
return { value: void 0 };
|
|
1925
|
+
}
|
|
1926
|
+
case "diff": {
|
|
1927
|
+
const { value: from } = this.#interpretPureNode(node.args.from, scope, generators);
|
|
1928
|
+
const { value: to } = this.#interpretPureNode(node.args.to, scope, generators);
|
|
1929
|
+
const keyGetter = (item) => item !== null ? getRecordKey(asRecord(item), node.args.fields) : null;
|
|
1930
|
+
const toSet = new Set(asList(to).map(keyGetter));
|
|
1931
|
+
return { value: asList(from).filter((item) => !toSet.has(keyGetter(item))) };
|
|
1932
|
+
}
|
|
1933
|
+
case "process": {
|
|
1934
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1935
|
+
const ops = cloneObject(node.args.operations);
|
|
1936
|
+
evaluateProcessingParameters(ops, scope, generators);
|
|
1937
|
+
return { value: processRecords(value, ops), lastInsertId };
|
|
1938
|
+
}
|
|
1939
|
+
case "initializeRecord": {
|
|
1940
|
+
const { lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1941
|
+
const record = {};
|
|
1942
|
+
for (const [key, initializer] of Object.entries(node.args.fields)) {
|
|
1943
|
+
record[key] = evalFieldInitializer(initializer, lastInsertId, scope, generators);
|
|
1944
|
+
}
|
|
1945
|
+
return { value: record, lastInsertId };
|
|
1946
|
+
}
|
|
1947
|
+
case "mapRecord": {
|
|
1948
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1949
|
+
const record = value === null ? {} : asRecord(value);
|
|
1950
|
+
for (const [key, entry] of Object.entries(node.args.fields)) {
|
|
1951
|
+
record[key] = evalFieldOperation(entry, record[key], scope, generators);
|
|
1952
|
+
}
|
|
1953
|
+
return { value: record, lastInsertId };
|
|
1954
|
+
}
|
|
1813
1955
|
default:
|
|
1814
1956
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
1815
1957
|
}
|
|
1816
1958
|
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Runs the statements of a `query` or `execute` node via `fn`, wrapping them in a
|
|
1961
|
+
* transaction when a chunkable statement was split into multiple queries at render time,
|
|
1962
|
+
* so that a partially applied write cannot be observed or left behind if a later chunk
|
|
1963
|
+
* fails. A single statement is atomic on its own, so it runs on the current context.
|
|
1964
|
+
*/
|
|
1965
|
+
#withChunkTransaction(statementCount, context, fn) {
|
|
1966
|
+
if (statementCount <= 1) {
|
|
1967
|
+
return fn(context);
|
|
1968
|
+
}
|
|
1969
|
+
return this.#withInternalTransaction(context, fn);
|
|
1970
|
+
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Runs `fn` with a context whose queryable is guaranteed to be a transaction, starting a
|
|
1973
|
+
* new internal transaction and committing or rolling it back around the call.
|
|
1974
|
+
*
|
|
1975
|
+
* A disabled transaction manager means the queryable already is a transaction: executors
|
|
1976
|
+
* pass `{ enabled: false }` when the plan runs inside an interactive transaction, and the
|
|
1977
|
+
* context handed to `fn` carries it for the duration of an internal transaction. In that
|
|
1978
|
+
* case `fn` runs on the current context, since the statements it issues are already
|
|
1979
|
+
* covered by the surrounding transaction.
|
|
1980
|
+
*/
|
|
1981
|
+
async #withInternalTransaction(context, fn) {
|
|
1982
|
+
if (!context.transactionManager.enabled) {
|
|
1983
|
+
return fn(context);
|
|
1984
|
+
}
|
|
1985
|
+
const transactionManager = context.transactionManager.manager;
|
|
1986
|
+
const transactionInfo = await transactionManager.startInternalTransaction();
|
|
1987
|
+
const transaction = await transactionManager.getTransaction(transactionInfo, "query");
|
|
1988
|
+
try {
|
|
1989
|
+
const result = await fn({ ...context, queryable: transaction, transactionManager: { enabled: false } });
|
|
1990
|
+
await transactionManager.commitTransaction(transactionInfo.id);
|
|
1991
|
+
return result;
|
|
1992
|
+
} catch (e) {
|
|
1993
|
+
try {
|
|
1994
|
+
await transactionManager.rollbackTransaction(transactionInfo.id);
|
|
1995
|
+
} catch (rollbackError) {
|
|
1996
|
+
debug("failed to roll back an internal transaction", rollbackError);
|
|
1997
|
+
}
|
|
1998
|
+
throw e;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
1817
2001
|
#maxChunkSize() {
|
|
1818
2002
|
if (this.#connectionInfo?.maxBindValues !== void 0) {
|
|
1819
2003
|
return this.#connectionInfo.maxBindValues;
|
|
@@ -1987,6 +2171,112 @@ function evalFieldOperation(op, value, scope, generators) {
|
|
|
1987
2171
|
assertNever(op, `Unexpected field operation type: ${op["type"]}`);
|
|
1988
2172
|
}
|
|
1989
2173
|
}
|
|
2174
|
+
function purifyQueryPlan(node, evalNode) {
|
|
2175
|
+
const impureNode = findUniqueUnconditionalImpureNode(node);
|
|
2176
|
+
if (!impureNode) {
|
|
2177
|
+
return void 0;
|
|
2178
|
+
}
|
|
2179
|
+
return evalNode(impureNode).then((result) => {
|
|
2180
|
+
const evaluated = {
|
|
2181
|
+
type: "value",
|
|
2182
|
+
args: result.value,
|
|
2183
|
+
lastInsertId: result.lastInsertId
|
|
2184
|
+
};
|
|
2185
|
+
const purified = replaceImpureNode(node, impureNode, evaluated);
|
|
2186
|
+
if (!purified) {
|
|
2187
|
+
throw new Error("Could not substitute the evaluated impure node into the query plan");
|
|
2188
|
+
}
|
|
2189
|
+
return purified;
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
function replaceImpureNode(node, target, replacement) {
|
|
2193
|
+
if (node === target) {
|
|
2194
|
+
return replacement;
|
|
2195
|
+
}
|
|
2196
|
+
switch (node.type) {
|
|
2197
|
+
case "seq":
|
|
2198
|
+
case "sum":
|
|
2199
|
+
case "concat": {
|
|
2200
|
+
for (let i = 0; i < node.args.length; i++) {
|
|
2201
|
+
const child = replaceImpureNode(node.args[i], target, replacement);
|
|
2202
|
+
if (child) {
|
|
2203
|
+
return { ...node, args: node.args.map((arg, j) => j === i ? child : arg) };
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
return void 0;
|
|
2207
|
+
}
|
|
2208
|
+
case "dataMap":
|
|
2209
|
+
case "validate":
|
|
2210
|
+
case "initializeRecord":
|
|
2211
|
+
case "mapRecord":
|
|
2212
|
+
case "process": {
|
|
2213
|
+
const expr = replaceImpureNode(node.args.expr, target, replacement);
|
|
2214
|
+
return expr && { ...node, args: { ...node.args, expr } };
|
|
2215
|
+
}
|
|
2216
|
+
case "mapField": {
|
|
2217
|
+
const records = replaceImpureNode(node.args.records, target, replacement);
|
|
2218
|
+
return records && { ...node, args: { ...node.args, records } };
|
|
2219
|
+
}
|
|
2220
|
+
case "reverse":
|
|
2221
|
+
case "unique":
|
|
2222
|
+
case "required": {
|
|
2223
|
+
const args = replaceImpureNode(node.args, target, replacement);
|
|
2224
|
+
return args && { ...node, args };
|
|
2225
|
+
}
|
|
2226
|
+
default:
|
|
2227
|
+
return void 0;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
function findUniqueUnconditionalImpureNode(node) {
|
|
2231
|
+
switch (node.type) {
|
|
2232
|
+
case "query":
|
|
2233
|
+
case "execute":
|
|
2234
|
+
return node;
|
|
2235
|
+
case "seq":
|
|
2236
|
+
case "sum":
|
|
2237
|
+
case "concat": {
|
|
2238
|
+
let found = void 0;
|
|
2239
|
+
for (const child of node.args) {
|
|
2240
|
+
const childFound = findUniqueUnconditionalImpureNode(child);
|
|
2241
|
+
if (childFound === null) {
|
|
2242
|
+
return null;
|
|
2243
|
+
}
|
|
2244
|
+
if (childFound) {
|
|
2245
|
+
if (found) {
|
|
2246
|
+
return null;
|
|
2247
|
+
}
|
|
2248
|
+
found = childFound;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
return found;
|
|
2252
|
+
}
|
|
2253
|
+
case "dataMap":
|
|
2254
|
+
case "validate":
|
|
2255
|
+
case "initializeRecord":
|
|
2256
|
+
case "mapRecord":
|
|
2257
|
+
case "process":
|
|
2258
|
+
return findUniqueUnconditionalImpureNode(node.args.expr);
|
|
2259
|
+
case "mapField":
|
|
2260
|
+
return findUniqueUnconditionalImpureNode(node.args.records);
|
|
2261
|
+
case "reverse":
|
|
2262
|
+
case "unique":
|
|
2263
|
+
case "required":
|
|
2264
|
+
return findUniqueUnconditionalImpureNode(node.args);
|
|
2265
|
+
case "let":
|
|
2266
|
+
case "join":
|
|
2267
|
+
case "diff":
|
|
2268
|
+
case "if":
|
|
2269
|
+
case "transaction":
|
|
2270
|
+
return null;
|
|
2271
|
+
case "value":
|
|
2272
|
+
case "get":
|
|
2273
|
+
case "getFirstNonEmpty":
|
|
2274
|
+
case "unit":
|
|
2275
|
+
return void 0;
|
|
2276
|
+
default:
|
|
2277
|
+
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
1990
2280
|
function applyComments(query, sqlCommenter) {
|
|
1991
2281
|
if (!sqlCommenter || sqlCommenter.plugins.length === 0) {
|
|
1992
2282
|
return query;
|
|
@@ -2485,7 +2775,7 @@ function normalizeValue(type, value) {
|
|
|
2485
2775
|
}
|
|
2486
2776
|
|
|
2487
2777
|
// src/transaction-manager/transaction-manager.ts
|
|
2488
|
-
var
|
|
2778
|
+
var import_debug2 = require("@prisma/debug");
|
|
2489
2779
|
|
|
2490
2780
|
// src/crypto.ts
|
|
2491
2781
|
async function getCrypto() {
|
|
@@ -2553,7 +2843,15 @@ var InvalidTransactionIsolationLevelError = class extends TransactionManagerErro
|
|
|
2553
2843
|
|
|
2554
2844
|
// src/transaction-manager/transaction-manager.ts
|
|
2555
2845
|
var MAX_CLOSED_TRANSACTIONS = 100;
|
|
2556
|
-
var
|
|
2846
|
+
var CANCEL_ROLLBACK_GRACE_MS = 2e3;
|
|
2847
|
+
function trackStartingTransaction() {
|
|
2848
|
+
let markSettled;
|
|
2849
|
+
const settled = new Promise((resolve) => {
|
|
2850
|
+
markSettled = resolve;
|
|
2851
|
+
});
|
|
2852
|
+
return { abortController: new AbortController(), settled, markSettled };
|
|
2853
|
+
}
|
|
2854
|
+
var debug2 = (0, import_debug2.Debug)("prisma:client:transactionManager");
|
|
2557
2855
|
var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
|
|
2558
2856
|
var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
|
|
2559
2857
|
var PHANTOM_COMMIT_QUERY = () => ({
|
|
@@ -2572,6 +2870,9 @@ var TransactionManager = class {
|
|
|
2572
2870
|
// List of last closed transactions. Max MAX_CLOSED_TRANSACTIONS entries.
|
|
2573
2871
|
// Used to provide better error messages than a generic "transaction not found".
|
|
2574
2872
|
closedTransactions = [];
|
|
2873
|
+
// Transactions that are still being started. Tracked separately so that
|
|
2874
|
+
// `cancelAllTransactions` can reach them: they are not in `transactions` yet.
|
|
2875
|
+
#startingTransactions = /* @__PURE__ */ new Set();
|
|
2575
2876
|
driverAdapter;
|
|
2576
2877
|
transactionOptions;
|
|
2577
2878
|
tracingHelper;
|
|
@@ -2635,56 +2936,91 @@ var TransactionManager = class {
|
|
|
2635
2936
|
return { id: existing.id };
|
|
2636
2937
|
});
|
|
2637
2938
|
}
|
|
2638
|
-
const
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2939
|
+
const starting = trackStartingTransaction();
|
|
2940
|
+
const { abortController } = starting;
|
|
2941
|
+
this.#startingTransactions.add(starting);
|
|
2942
|
+
let discarding;
|
|
2943
|
+
try {
|
|
2944
|
+
const transaction = {
|
|
2945
|
+
id: await randomUUID(),
|
|
2946
|
+
status: "waiting",
|
|
2947
|
+
timer: void 0,
|
|
2948
|
+
timeout: options.timeout,
|
|
2949
|
+
startedAt: Date.now(),
|
|
2950
|
+
transaction: void 0,
|
|
2951
|
+
operationQueue: Promise.resolve(),
|
|
2952
|
+
depth: 1,
|
|
2953
|
+
savepoints: [],
|
|
2954
|
+
savepointCounter: 0
|
|
2955
|
+
};
|
|
2956
|
+
if (abortController.signal.aborted) {
|
|
2957
|
+
throw new TransactionStartTimeoutError();
|
|
2958
|
+
}
|
|
2959
|
+
const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
|
|
2960
|
+
startTimer?.unref?.();
|
|
2961
|
+
const startTransactionPromise = this.driverAdapter.startTransaction(options.isolationLevel).catch(rethrowAsUserFacing);
|
|
2962
|
+
transaction.transaction = await Promise.race([
|
|
2963
|
+
startTransactionPromise.finally(() => clearTimeout(startTimer)),
|
|
2964
|
+
once(abortController.signal, "abort").then(() => void 0)
|
|
2965
|
+
]);
|
|
2966
|
+
this.transactions.set(transaction.id, transaction);
|
|
2967
|
+
switch (transaction.status) {
|
|
2968
|
+
case "waiting":
|
|
2969
|
+
if (abortController.signal.aborted) {
|
|
2970
|
+
transaction.transaction = void 0;
|
|
2971
|
+
discarding = this.#discardStartedTransaction(startTransactionPromise);
|
|
2972
|
+
await this.#closeTransaction(transaction, "timed_out");
|
|
2973
|
+
throw new TransactionStartTimeoutError();
|
|
2974
|
+
}
|
|
2975
|
+
transaction.status = "running";
|
|
2976
|
+
transaction.startedAt = Date.now();
|
|
2977
|
+
transaction.timer = this.#startTransactionTimeout(transaction.id, options.timeout);
|
|
2978
|
+
return { id: transaction.id };
|
|
2979
|
+
case "timed_out":
|
|
2980
|
+
case "running":
|
|
2981
|
+
case "committed":
|
|
2982
|
+
case "rolled_back":
|
|
2983
|
+
throw new TransactionInternalConsistencyError(
|
|
2984
|
+
`Transaction in invalid state ${transaction.status} although it just finished startup.`
|
|
2985
|
+
);
|
|
2986
|
+
default:
|
|
2987
|
+
return assertNever(transaction["status"], "Unknown transaction status.");
|
|
2988
|
+
}
|
|
2989
|
+
} finally {
|
|
2990
|
+
this.#startingTransactions.delete(starting);
|
|
2991
|
+
if (discarding) {
|
|
2992
|
+
void discarding.finally(starting.markSettled);
|
|
2993
|
+
} else {
|
|
2994
|
+
starting.markSettled();
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
/**
|
|
2999
|
+
* Rolls back a transaction whose start was abandoned, and releases its connection.
|
|
3000
|
+
*
|
|
3001
|
+
* The `startTransaction` promise may still be running in the background. If it eventually
|
|
3002
|
+
* succeeds, we need to roll back and release the connection to avoid leaking it and
|
|
3003
|
+
* exhausting the connection pool. For adapters that don't use phantom queries (e.g. pg/neon),
|
|
3004
|
+
* `rollback()` only releases the connection without sending SQL, so we send an explicit
|
|
3005
|
+
* ROLLBACK first; otherwise the connection returns to the pool mid-transaction because
|
|
3006
|
+
* `BEGIN` already ran on the wire during startup.
|
|
3007
|
+
*
|
|
3008
|
+
* Errors are only logged: the caller has already reported the failure that led here.
|
|
3009
|
+
*/
|
|
3010
|
+
async #discardStartedTransaction(startTransactionPromise) {
|
|
3011
|
+
try {
|
|
3012
|
+
const tx = await startTransactionPromise;
|
|
3013
|
+
if (tx.options.usePhantomQuery) {
|
|
3014
|
+
await tx.rollback();
|
|
3015
|
+
} else {
|
|
3016
|
+
try {
|
|
3017
|
+
await tx.executeRaw(ROLLBACK_QUERY());
|
|
3018
|
+
} finally {
|
|
3019
|
+
await tx.rollback();
|
|
2675
3020
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
case "timed_out":
|
|
2680
|
-
case "running":
|
|
2681
|
-
case "committed":
|
|
2682
|
-
case "rolled_back":
|
|
2683
|
-
throw new TransactionInternalConsistencyError(
|
|
2684
|
-
`Transaction in invalid state ${transaction.status} although it just finished startup.`
|
|
2685
|
-
);
|
|
2686
|
-
default:
|
|
2687
|
-
assertNever(transaction["status"], "Unknown transaction status.");
|
|
3021
|
+
}
|
|
3022
|
+
} catch (e) {
|
|
3023
|
+
debug2("error in discarded transaction:", e);
|
|
2688
3024
|
}
|
|
2689
3025
|
}
|
|
2690
3026
|
async commitTransaction(transactionId) {
|
|
@@ -2748,7 +3084,7 @@ var TransactionManager = class {
|
|
|
2748
3084
|
if (!transaction) {
|
|
2749
3085
|
const closedTransaction = this.closedTransactions.find((tx) => tx.id === transactionId);
|
|
2750
3086
|
if (closedTransaction) {
|
|
2751
|
-
|
|
3087
|
+
debug2("Transaction already closed.", { transactionId, status: closedTransaction.status });
|
|
2752
3088
|
switch (closedTransaction.status) {
|
|
2753
3089
|
case "closing":
|
|
2754
3090
|
case "waiting":
|
|
@@ -2765,7 +3101,7 @@ var TransactionManager = class {
|
|
|
2765
3101
|
});
|
|
2766
3102
|
}
|
|
2767
3103
|
} else {
|
|
2768
|
-
|
|
3104
|
+
debug2(`Transaction not found.`, transactionId);
|
|
2769
3105
|
throw new TransactionNotFoundError();
|
|
2770
3106
|
}
|
|
2771
3107
|
}
|
|
@@ -2775,16 +3111,21 @@ var TransactionManager = class {
|
|
|
2775
3111
|
return transaction;
|
|
2776
3112
|
}
|
|
2777
3113
|
async cancelAllTransactions() {
|
|
2778
|
-
|
|
2779
|
-
|
|
3114
|
+
const starting = [...this.#startingTransactions];
|
|
3115
|
+
for (const { abortController } of starting) {
|
|
3116
|
+
abortController.abort();
|
|
3117
|
+
}
|
|
3118
|
+
await Promise.allSettled([
|
|
3119
|
+
...[...this.transactions.values()].map(
|
|
2780
3120
|
(tx) => this.#runSerialized(tx, async () => {
|
|
2781
3121
|
const current = this.transactions.get(tx.id);
|
|
2782
3122
|
if (current) {
|
|
2783
3123
|
await this.#closeTransaction(current, "rolled_back");
|
|
2784
3124
|
}
|
|
2785
3125
|
})
|
|
2786
|
-
)
|
|
2787
|
-
|
|
3126
|
+
),
|
|
3127
|
+
...starting.map(({ settled }) => settleWithin(settled, CANCEL_ROLLBACK_GRACE_MS))
|
|
3128
|
+
]);
|
|
2788
3129
|
}
|
|
2789
3130
|
#nextSavepointName(transaction) {
|
|
2790
3131
|
return `prisma_sp_${transaction.savepointCounter++}`;
|
|
@@ -2811,25 +3152,29 @@ var TransactionManager = class {
|
|
|
2811
3152
|
}
|
|
2812
3153
|
}
|
|
2813
3154
|
#debugTransactionAlreadyClosedOnTimeout(transactionId) {
|
|
2814
|
-
|
|
3155
|
+
debug2("Transaction already committed or rolled back when timeout happened.", transactionId);
|
|
2815
3156
|
}
|
|
2816
3157
|
#startTransactionTimeout(transactionId, timeout) {
|
|
2817
3158
|
const timeoutStartedAt = Date.now();
|
|
2818
3159
|
const timer = createTimeoutIfDefined(async () => {
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
return;
|
|
2824
|
-
}
|
|
2825
|
-
await this.#runSerialized(tx, async () => {
|
|
2826
|
-
const current = this.transactions.get(transactionId);
|
|
2827
|
-
if (current && ["running", "waiting"].includes(current.status)) {
|
|
2828
|
-
await this.#closeTransaction(current, "timed_out");
|
|
2829
|
-
} else {
|
|
3160
|
+
try {
|
|
3161
|
+
debug2("Transaction timed out.", { transactionId, timeoutStartedAt, timeout });
|
|
3162
|
+
const tx = this.transactions.get(transactionId);
|
|
3163
|
+
if (!tx) {
|
|
2830
3164
|
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
3165
|
+
return;
|
|
2831
3166
|
}
|
|
2832
|
-
|
|
3167
|
+
await this.#runSerialized(tx, async () => {
|
|
3168
|
+
const current = this.transactions.get(transactionId);
|
|
3169
|
+
if (current && ["running", "waiting"].includes(current.status)) {
|
|
3170
|
+
await this.#closeTransaction(current, "timed_out");
|
|
3171
|
+
} else {
|
|
3172
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
3173
|
+
}
|
|
3174
|
+
});
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
debug2("Error while closing timed-out transaction.", { transactionId, error });
|
|
3177
|
+
}
|
|
2833
3178
|
}, timeout);
|
|
2834
3179
|
timer?.unref?.();
|
|
2835
3180
|
return timer;
|
|
@@ -2861,7 +3206,7 @@ var TransactionManager = class {
|
|
|
2861
3206
|
}
|
|
2862
3207
|
async #closeTransaction(tx, status) {
|
|
2863
3208
|
const createClosingPromise = async () => {
|
|
2864
|
-
|
|
3209
|
+
debug2("Closing transaction.", { transactionId: tx.id, status });
|
|
2865
3210
|
try {
|
|
2866
3211
|
if (tx.transaction && status === "committed") {
|
|
2867
3212
|
if (tx.transaction.options.usePhantomQuery) {
|
|
@@ -2937,6 +3282,14 @@ var TransactionManager = class {
|
|
|
2937
3282
|
function createTimeoutIfDefined(cb, ms) {
|
|
2938
3283
|
return ms !== void 0 ? setTimeout(cb, ms) : void 0;
|
|
2939
3284
|
}
|
|
3285
|
+
function settleWithin(promise, timeout) {
|
|
3286
|
+
let timer;
|
|
3287
|
+
const deadline = new Promise((resolve) => {
|
|
3288
|
+
timer = setTimeout(resolve, timeout);
|
|
3289
|
+
timer?.unref?.();
|
|
3290
|
+
});
|
|
3291
|
+
return Promise.race([promise, deadline]).finally(() => clearTimeout(timer));
|
|
3292
|
+
}
|
|
2940
3293
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2941
3294
|
0 && (module.exports = {
|
|
2942
3295
|
DataMapperError,
|