@prisma/client-engine-runtime 7.10.0-dev.4 → 7.10.0-dev.41
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 +499 -150
- package/dist/index.mjs +499 -150
- 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 +10 -10
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,12 @@ import { Decimal as Decimal2 } from "@prisma/client-runtime-utils";
|
|
|
3
3
|
|
|
4
4
|
// src/utils.ts
|
|
5
5
|
import { Decimal } from "@prisma/client-runtime-utils";
|
|
6
|
+
function isUint8Array(value) {
|
|
7
|
+
return ArrayBuffer.isView(value) && Object.prototype.toString.call(value) === "[object Uint8Array]";
|
|
8
|
+
}
|
|
9
|
+
function isDate(value) {
|
|
10
|
+
return Object.prototype.toString.call(value) === "[object Date]";
|
|
11
|
+
}
|
|
6
12
|
function assertNever(_, message) {
|
|
7
13
|
throw new Error(message);
|
|
8
14
|
}
|
|
@@ -21,11 +27,11 @@ function doKeysMatch(lhs, rhs) {
|
|
|
21
27
|
const lhsDecimal = asDecimal(lhs[key]);
|
|
22
28
|
const rhsDecimal = asDecimal(rhs[key]);
|
|
23
29
|
return lhsDecimal && rhsDecimal && lhsDecimal.equals(rhsDecimal);
|
|
24
|
-
} else if (lhs[key]
|
|
30
|
+
} else if (isUint8Array(lhs[key]) || isUint8Array(rhs[key])) {
|
|
25
31
|
const lhsBuffer = asBuffer(lhs[key]);
|
|
26
32
|
const rhsBuffer = asBuffer(rhs[key]);
|
|
27
33
|
return lhsBuffer && rhsBuffer && lhsBuffer.equals(rhsBuffer);
|
|
28
|
-
} else if (lhs[key]
|
|
34
|
+
} else if (isDate(lhs[key]) || isDate(rhs[key])) {
|
|
29
35
|
return asDate(lhs[key])?.getTime() === asDate(rhs[key])?.getTime();
|
|
30
36
|
} else if (typeof lhs[key] === "bigint" || typeof rhs[key] === "bigint") {
|
|
31
37
|
return asBigInt(lhs[key]) === asBigInt(rhs[key]);
|
|
@@ -47,7 +53,7 @@ function asDecimal(value) {
|
|
|
47
53
|
function asBuffer(value) {
|
|
48
54
|
if (Buffer.isBuffer(value)) {
|
|
49
55
|
return value;
|
|
50
|
-
} else if (value
|
|
56
|
+
} else if (isUint8Array(value)) {
|
|
51
57
|
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
52
58
|
} else if (typeof value === "string") {
|
|
53
59
|
return Buffer.from(value, "base64");
|
|
@@ -56,7 +62,7 @@ function asBuffer(value) {
|
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
64
|
function asDate(value) {
|
|
59
|
-
if (value
|
|
65
|
+
if (isDate(value)) {
|
|
60
66
|
return value;
|
|
61
67
|
} else if (typeof value === "string" || typeof value === "number") {
|
|
62
68
|
return new Date(value);
|
|
@@ -92,6 +98,16 @@ function safeJsonStringify(obj) {
|
|
|
92
98
|
return val;
|
|
93
99
|
});
|
|
94
100
|
}
|
|
101
|
+
var MAX_PUSH_SPREAD_ARGS = 8192;
|
|
102
|
+
function appendToArray(target, source) {
|
|
103
|
+
if (source.length <= MAX_PUSH_SPREAD_ARGS) {
|
|
104
|
+
target.push(...source);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (let i = 0; i < source.length; i += MAX_PUSH_SPREAD_ARGS) {
|
|
108
|
+
target.push(...source.slice(i, i + MAX_PUSH_SPREAD_ARGS));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
95
111
|
|
|
96
112
|
// src/json-protocol.ts
|
|
97
113
|
function normalizeJsonProtocolValues(result) {
|
|
@@ -173,10 +189,8 @@ function deserializeTaggedValue({ $type, value }) {
|
|
|
173
189
|
switch ($type) {
|
|
174
190
|
case "BigInt":
|
|
175
191
|
return BigInt(value);
|
|
176
|
-
case "Bytes":
|
|
177
|
-
|
|
178
|
-
return new Uint8Array(buffer, byteOffset, byteLength);
|
|
179
|
-
}
|
|
192
|
+
case "Bytes":
|
|
193
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
180
194
|
case "DateTime":
|
|
181
195
|
return new Date(value);
|
|
182
196
|
case "Decimal":
|
|
@@ -290,6 +304,7 @@ function getErrorCode(err) {
|
|
|
290
304
|
case "UniqueConstraintViolation":
|
|
291
305
|
return "P2002";
|
|
292
306
|
case "ForeignKeyConstraintViolation":
|
|
307
|
+
case "RestrictViolation":
|
|
293
308
|
return "P2003";
|
|
294
309
|
case "InvalidInputValue":
|
|
295
310
|
return "P2007";
|
|
@@ -362,6 +377,7 @@ function renderErrorMessage(err) {
|
|
|
362
377
|
case "UniqueConstraintViolation":
|
|
363
378
|
return `Unique constraint failed on the ${renderConstraint(err.cause.constraint)}`;
|
|
364
379
|
case "ForeignKeyConstraintViolation":
|
|
380
|
+
case "RestrictViolation":
|
|
365
381
|
return `Foreign key constraint violated on the ${renderConstraint(err.cause.constraint)}`;
|
|
366
382
|
case "UnsupportedNativeDataType":
|
|
367
383
|
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\`.`;
|
|
@@ -638,7 +654,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
638
654
|
throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
|
|
639
655
|
}
|
|
640
656
|
}
|
|
641
|
-
if (Array.isArray(value) || value
|
|
657
|
+
if (Array.isArray(value) || isUint8Array(value)) {
|
|
642
658
|
for (const byte of value) {
|
|
643
659
|
if (byte !== 0) return true;
|
|
644
660
|
}
|
|
@@ -655,7 +671,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
655
671
|
if (typeof value === "string") {
|
|
656
672
|
return { $type: "DateTime", value: normalizeDateTime(value) };
|
|
657
673
|
}
|
|
658
|
-
if (typeof value === "number" || value
|
|
674
|
+
if (typeof value === "number" || isDate(value)) {
|
|
659
675
|
return { $type: "DateTime", value };
|
|
660
676
|
}
|
|
661
677
|
throw new DataMapperError(`Expected a date in column '${columnName}', got ${typeof value}: ${value}`);
|
|
@@ -686,7 +702,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
686
702
|
if (Array.isArray(value)) {
|
|
687
703
|
return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
|
|
688
704
|
}
|
|
689
|
-
if (value
|
|
705
|
+
if (isUint8Array(value)) {
|
|
690
706
|
return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
|
|
691
707
|
}
|
|
692
708
|
throw new DataMapperError(`Expected a byte array in column '${columnName}', got ${typeof value}: ${value}`);
|
|
@@ -734,6 +750,7 @@ function normalizeDateTime(dt) {
|
|
|
734
750
|
}
|
|
735
751
|
|
|
736
752
|
// src/interpreter/query-interpreter.ts
|
|
753
|
+
import { Debug } from "@prisma/debug";
|
|
737
754
|
import { klona as klona2 } from "klona";
|
|
738
755
|
|
|
739
756
|
// src/sql-commenter.ts
|
|
@@ -1095,8 +1112,9 @@ function renderTemplateSql(fragments, placeholderFormat, params, argTypes) {
|
|
|
1095
1112
|
if (fragment.type === "stringChunk") {
|
|
1096
1113
|
continue;
|
|
1097
1114
|
}
|
|
1098
|
-
const
|
|
1099
|
-
const added =
|
|
1115
|
+
const fragmentParams = Array.from(flattenedFragmentParams(fragment));
|
|
1116
|
+
const added = fragmentParams.length;
|
|
1117
|
+
appendToArray(flattenedParams, fragmentParams);
|
|
1100
1118
|
if (fragment.argType.arity === "tuple") {
|
|
1101
1119
|
if (added % fragment.argType.elements.length !== 0) {
|
|
1102
1120
|
throw new Error(
|
|
@@ -1525,6 +1543,7 @@ function getErrorCode2(error) {
|
|
|
1525
1543
|
}
|
|
1526
1544
|
|
|
1527
1545
|
// src/interpreter/query-interpreter.ts
|
|
1546
|
+
var debug = Debug("prisma:client:queryInterpreter");
|
|
1528
1547
|
var QueryInterpreter = class _QueryInterpreter {
|
|
1529
1548
|
#onQuery;
|
|
1530
1549
|
#generators = new GeneratorRegistry();
|
|
@@ -1559,17 +1578,27 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1559
1578
|
});
|
|
1560
1579
|
}
|
|
1561
1580
|
async run(queryPlan, options) {
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1581
|
+
const generators = this.#generators.snapshot();
|
|
1582
|
+
const context = { ...options, generators };
|
|
1583
|
+
const purified = purifyQueryPlan(queryPlan, (node) => this.interpretNode(node, context))?.catch(
|
|
1584
|
+
(e) => rethrowAsUserFacing(e)
|
|
1585
|
+
);
|
|
1586
|
+
if (purified) {
|
|
1587
|
+
try {
|
|
1588
|
+
return this.#interpretPureNode(await purified, context.scope, generators).value;
|
|
1589
|
+
} catch (e) {
|
|
1590
|
+
rethrowAsUserFacing(e);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
const { value } = await this.interpretNode(queryPlan, context).catch((e) => rethrowAsUserFacing(e));
|
|
1566
1594
|
return value;
|
|
1567
1595
|
}
|
|
1568
1596
|
async interpretNode(node, context) {
|
|
1569
1597
|
switch (node.type) {
|
|
1570
1598
|
case "value": {
|
|
1571
1599
|
return {
|
|
1572
|
-
value: evaluateArg(node.args, context.scope, context.generators)
|
|
1600
|
+
value: evaluateArg(node.args, context.scope, context.generators),
|
|
1601
|
+
lastInsertId: node.lastInsertId
|
|
1573
1602
|
};
|
|
1574
1603
|
}
|
|
1575
1604
|
case "seq": {
|
|
@@ -1579,9 +1608,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1579
1608
|
}
|
|
1580
1609
|
return result ?? { value: void 0 };
|
|
1581
1610
|
}
|
|
1582
|
-
case "get": {
|
|
1583
|
-
return { value: context.scope[node.args.name] };
|
|
1584
|
-
}
|
|
1585
1611
|
case "let": {
|
|
1586
1612
|
const nestedScope = Object.create(context.scope);
|
|
1587
1613
|
for (const binding of node.args.bindings) {
|
|
@@ -1590,15 +1616,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1590
1616
|
}
|
|
1591
1617
|
return this.interpretNode(node.args.expr, { ...context, scope: nestedScope });
|
|
1592
1618
|
}
|
|
1593
|
-
case "getFirstNonEmpty": {
|
|
1594
|
-
for (const name of node.args.names) {
|
|
1595
|
-
const value = context.scope[name];
|
|
1596
|
-
if (!isEmpty(value)) {
|
|
1597
|
-
return { value };
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
return { value: [] };
|
|
1601
|
-
}
|
|
1602
1619
|
case "concat": {
|
|
1603
1620
|
const parts = await Promise.all(
|
|
1604
1621
|
node.args.map((arg) => this.interpretNode(arg, context).then((res) => res.value))
|
|
@@ -1617,42 +1634,46 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1617
1634
|
}
|
|
1618
1635
|
case "execute": {
|
|
1619
1636
|
const queries = renderQuery(node.args, context.scope, context.generators, this.#maxChunkSize());
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
(
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1637
|
+
return this.#withChunkTransaction(queries.length, context, async (context2) => {
|
|
1638
|
+
let sum = 0;
|
|
1639
|
+
for (const query of queries) {
|
|
1640
|
+
const commentedQuery = applyComments(query, context2.sqlCommenter);
|
|
1641
|
+
sum += await this.#withQuerySpanAndEvent(
|
|
1642
|
+
commentedQuery,
|
|
1643
|
+
context2.queryable,
|
|
1644
|
+
() => context2.queryable.executeRaw(cloneObject(commentedQuery)).catch(
|
|
1645
|
+
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1646
|
+
)
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
return { value: sum };
|
|
1650
|
+
});
|
|
1632
1651
|
}
|
|
1633
1652
|
case "query": {
|
|
1634
1653
|
const queries = renderQuery(node.args, context.scope, context.generators, this.#maxChunkSize());
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
const
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
(
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
results
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1654
|
+
return this.#withChunkTransaction(queries.length, context, async (context2) => {
|
|
1655
|
+
let results;
|
|
1656
|
+
for (const query of queries) {
|
|
1657
|
+
const commentedQuery = applyComments(query, context2.sqlCommenter);
|
|
1658
|
+
const result = await this.#withQuerySpanAndEvent(
|
|
1659
|
+
commentedQuery,
|
|
1660
|
+
context2.queryable,
|
|
1661
|
+
() => context2.queryable.queryRaw(cloneObject(commentedQuery)).catch(
|
|
1662
|
+
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1663
|
+
)
|
|
1664
|
+
);
|
|
1665
|
+
if (results === void 0) {
|
|
1666
|
+
results = result;
|
|
1667
|
+
} else {
|
|
1668
|
+
appendToArray(results.rows, result.rows);
|
|
1669
|
+
results.lastInsertId = result.lastInsertId;
|
|
1670
|
+
}
|
|
1650
1671
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
};
|
|
1672
|
+
return {
|
|
1673
|
+
value: node.args.type === "rawSql" ? this.#rawSerializer(results) : this.#serializer(results),
|
|
1674
|
+
lastInsertId: results?.lastInsertId
|
|
1675
|
+
};
|
|
1676
|
+
});
|
|
1656
1677
|
}
|
|
1657
1678
|
case "reverse": {
|
|
1658
1679
|
const { value, lastInsertId } = await this.interpretNode(node.args, context);
|
|
@@ -1693,20 +1714,7 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1693
1714
|
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1694
1715
|
}
|
|
1695
1716
|
case "transaction": {
|
|
1696
|
-
|
|
1697
|
-
return this.interpretNode(node.args, context);
|
|
1698
|
-
}
|
|
1699
|
-
const transactionManager = context.transactionManager.manager;
|
|
1700
|
-
const transactionInfo = await transactionManager.startInternalTransaction();
|
|
1701
|
-
const transaction = await transactionManager.getTransaction(transactionInfo, "query");
|
|
1702
|
-
try {
|
|
1703
|
-
const value = await this.interpretNode(node.args, { ...context, queryable: transaction });
|
|
1704
|
-
await transactionManager.commitTransaction(transactionInfo.id);
|
|
1705
|
-
return value;
|
|
1706
|
-
} catch (e) {
|
|
1707
|
-
await transactionManager.rollbackTransaction(transactionInfo.id);
|
|
1708
|
-
throw e;
|
|
1709
|
-
}
|
|
1717
|
+
return this.#withInternalTransaction(context, (context2) => this.interpretNode(node.args, context2));
|
|
1710
1718
|
}
|
|
1711
1719
|
case "dataMap": {
|
|
1712
1720
|
const { value, lastInsertId } = await this.interpretNode(node.args.expr, context);
|
|
@@ -1725,9 +1733,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1725
1733
|
return await this.interpretNode(node.args.else, context);
|
|
1726
1734
|
}
|
|
1727
1735
|
}
|
|
1728
|
-
case "unit": {
|
|
1729
|
-
return { value: void 0 };
|
|
1730
|
-
}
|
|
1731
1736
|
case "diff": {
|
|
1732
1737
|
const { value: from } = await this.interpretNode(node.args.from, context);
|
|
1733
1738
|
const { value: to } = await this.interpretNode(node.args.to, context);
|
|
@@ -1757,10 +1762,185 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1757
1762
|
}
|
|
1758
1763
|
return { value: record, lastInsertId };
|
|
1759
1764
|
}
|
|
1765
|
+
default:
|
|
1766
|
+
return this.#interpretPureNode(node, context.scope, context.generators);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
#interpretPureNode(node, scope, generators) {
|
|
1770
|
+
switch (node.type) {
|
|
1771
|
+
case "value": {
|
|
1772
|
+
return { value: evaluateArg(node.args, scope, generators), lastInsertId: node.lastInsertId };
|
|
1773
|
+
}
|
|
1774
|
+
case "seq": {
|
|
1775
|
+
let result;
|
|
1776
|
+
for (const arg of node.args) {
|
|
1777
|
+
result = this.#interpretPureNode(arg, scope, generators);
|
|
1778
|
+
}
|
|
1779
|
+
return result ?? { value: void 0 };
|
|
1780
|
+
}
|
|
1781
|
+
case "get": {
|
|
1782
|
+
return { value: scope[node.args.name] };
|
|
1783
|
+
}
|
|
1784
|
+
case "let": {
|
|
1785
|
+
const nestedScope = Object.create(scope);
|
|
1786
|
+
for (const binding of node.args.bindings) {
|
|
1787
|
+
const { value } = this.#interpretPureNode(binding.expr, nestedScope, generators);
|
|
1788
|
+
nestedScope[binding.name] = value;
|
|
1789
|
+
}
|
|
1790
|
+
return this.#interpretPureNode(node.args.expr, nestedScope, generators);
|
|
1791
|
+
}
|
|
1792
|
+
case "getFirstNonEmpty": {
|
|
1793
|
+
for (const name of node.args.names) {
|
|
1794
|
+
const value = scope[name];
|
|
1795
|
+
if (!isEmpty(value)) {
|
|
1796
|
+
return { value };
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return { value: [] };
|
|
1800
|
+
}
|
|
1801
|
+
case "concat": {
|
|
1802
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1803
|
+
return {
|
|
1804
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : []
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
case "sum": {
|
|
1808
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1809
|
+
return {
|
|
1810
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => asNumber2(acc) + asNumber2(part)) : 0
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
case "reverse": {
|
|
1814
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1815
|
+
return { value: Array.isArray(value) ? value.reverse() : value, lastInsertId };
|
|
1816
|
+
}
|
|
1817
|
+
case "unique": {
|
|
1818
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1819
|
+
if (!Array.isArray(value)) {
|
|
1820
|
+
return { value, lastInsertId };
|
|
1821
|
+
}
|
|
1822
|
+
if (value.length > 1) {
|
|
1823
|
+
throw new Error(`Expected zero or one element, got ${value.length}`);
|
|
1824
|
+
}
|
|
1825
|
+
return { value: value[0] ?? null, lastInsertId };
|
|
1826
|
+
}
|
|
1827
|
+
case "required": {
|
|
1828
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1829
|
+
if (isEmpty(value)) {
|
|
1830
|
+
throw new Error("Required value is empty");
|
|
1831
|
+
}
|
|
1832
|
+
return { value, lastInsertId };
|
|
1833
|
+
}
|
|
1834
|
+
case "mapField": {
|
|
1835
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.records, scope, generators);
|
|
1836
|
+
return { value: mapField2(value, node.args.field), lastInsertId };
|
|
1837
|
+
}
|
|
1838
|
+
case "join": {
|
|
1839
|
+
const { value: parent, lastInsertId } = this.#interpretPureNode(node.args.parent, scope, generators);
|
|
1840
|
+
if (parent === null) {
|
|
1841
|
+
return { value: null, lastInsertId };
|
|
1842
|
+
}
|
|
1843
|
+
const children = node.args.children.map((joinExpr) => ({
|
|
1844
|
+
joinExpr,
|
|
1845
|
+
childRecords: this.#interpretPureNode(joinExpr.child, scope, generators).value
|
|
1846
|
+
}));
|
|
1847
|
+
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1848
|
+
}
|
|
1849
|
+
case "dataMap": {
|
|
1850
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1851
|
+
return { value: applyDataMap(value, node.args.structure, node.args.enums), lastInsertId };
|
|
1852
|
+
}
|
|
1853
|
+
case "validate": {
|
|
1854
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1855
|
+
performValidation(value, node.args.rules, node.args);
|
|
1856
|
+
return { value, lastInsertId };
|
|
1857
|
+
}
|
|
1858
|
+
case "if": {
|
|
1859
|
+
const { value } = this.#interpretPureNode(node.args.value, scope, generators);
|
|
1860
|
+
if (doesSatisfyRule(value, node.args.rule)) {
|
|
1861
|
+
return this.#interpretPureNode(node.args.then, scope, generators);
|
|
1862
|
+
} else {
|
|
1863
|
+
return this.#interpretPureNode(node.args.else, scope, generators);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
case "unit": {
|
|
1867
|
+
return { value: void 0 };
|
|
1868
|
+
}
|
|
1869
|
+
case "diff": {
|
|
1870
|
+
const { value: from } = this.#interpretPureNode(node.args.from, scope, generators);
|
|
1871
|
+
const { value: to } = this.#interpretPureNode(node.args.to, scope, generators);
|
|
1872
|
+
const keyGetter = (item) => item !== null ? getRecordKey(asRecord(item), node.args.fields) : null;
|
|
1873
|
+
const toSet = new Set(asList(to).map(keyGetter));
|
|
1874
|
+
return { value: asList(from).filter((item) => !toSet.has(keyGetter(item))) };
|
|
1875
|
+
}
|
|
1876
|
+
case "process": {
|
|
1877
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1878
|
+
const ops = cloneObject(node.args.operations);
|
|
1879
|
+
evaluateProcessingParameters(ops, scope, generators);
|
|
1880
|
+
return { value: processRecords(value, ops), lastInsertId };
|
|
1881
|
+
}
|
|
1882
|
+
case "initializeRecord": {
|
|
1883
|
+
const { lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1884
|
+
const record = {};
|
|
1885
|
+
for (const [key, initializer] of Object.entries(node.args.fields)) {
|
|
1886
|
+
record[key] = evalFieldInitializer(initializer, lastInsertId, scope, generators);
|
|
1887
|
+
}
|
|
1888
|
+
return { value: record, lastInsertId };
|
|
1889
|
+
}
|
|
1890
|
+
case "mapRecord": {
|
|
1891
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1892
|
+
const record = value === null ? {} : asRecord(value);
|
|
1893
|
+
for (const [key, entry] of Object.entries(node.args.fields)) {
|
|
1894
|
+
record[key] = evalFieldOperation(entry, record[key], scope, generators);
|
|
1895
|
+
}
|
|
1896
|
+
return { value: record, lastInsertId };
|
|
1897
|
+
}
|
|
1760
1898
|
default:
|
|
1761
1899
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
1762
1900
|
}
|
|
1763
1901
|
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Runs the statements of a `query` or `execute` node via `fn`, wrapping them in a
|
|
1904
|
+
* transaction when a chunkable statement was split into multiple queries at render time,
|
|
1905
|
+
* so that a partially applied write cannot be observed or left behind if a later chunk
|
|
1906
|
+
* fails. A single statement is atomic on its own, so it runs on the current context.
|
|
1907
|
+
*/
|
|
1908
|
+
#withChunkTransaction(statementCount, context, fn) {
|
|
1909
|
+
if (statementCount <= 1) {
|
|
1910
|
+
return fn(context);
|
|
1911
|
+
}
|
|
1912
|
+
return this.#withInternalTransaction(context, fn);
|
|
1913
|
+
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Runs `fn` with a context whose queryable is guaranteed to be a transaction, starting a
|
|
1916
|
+
* new internal transaction and committing or rolling it back around the call.
|
|
1917
|
+
*
|
|
1918
|
+
* A disabled transaction manager means the queryable already is a transaction: executors
|
|
1919
|
+
* pass `{ enabled: false }` when the plan runs inside an interactive transaction, and the
|
|
1920
|
+
* context handed to `fn` carries it for the duration of an internal transaction. In that
|
|
1921
|
+
* case `fn` runs on the current context, since the statements it issues are already
|
|
1922
|
+
* covered by the surrounding transaction.
|
|
1923
|
+
*/
|
|
1924
|
+
async #withInternalTransaction(context, fn) {
|
|
1925
|
+
if (!context.transactionManager.enabled) {
|
|
1926
|
+
return fn(context);
|
|
1927
|
+
}
|
|
1928
|
+
const transactionManager = context.transactionManager.manager;
|
|
1929
|
+
const transactionInfo = await transactionManager.startInternalTransaction();
|
|
1930
|
+
const transaction = await transactionManager.getTransaction(transactionInfo, "query");
|
|
1931
|
+
try {
|
|
1932
|
+
const result = await fn({ ...context, queryable: transaction, transactionManager: { enabled: false } });
|
|
1933
|
+
await transactionManager.commitTransaction(transactionInfo.id);
|
|
1934
|
+
return result;
|
|
1935
|
+
} catch (e) {
|
|
1936
|
+
try {
|
|
1937
|
+
await transactionManager.rollbackTransaction(transactionInfo.id);
|
|
1938
|
+
} catch (rollbackError) {
|
|
1939
|
+
debug("failed to roll back an internal transaction", rollbackError);
|
|
1940
|
+
}
|
|
1941
|
+
throw e;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1764
1944
|
#maxChunkSize() {
|
|
1765
1945
|
if (this.#connectionInfo?.maxBindValues !== void 0) {
|
|
1766
1946
|
return this.#connectionInfo.maxBindValues;
|
|
@@ -1934,6 +2114,112 @@ function evalFieldOperation(op, value, scope, generators) {
|
|
|
1934
2114
|
assertNever(op, `Unexpected field operation type: ${op["type"]}`);
|
|
1935
2115
|
}
|
|
1936
2116
|
}
|
|
2117
|
+
function purifyQueryPlan(node, evalNode) {
|
|
2118
|
+
const impureNode = findUniqueUnconditionalImpureNode(node);
|
|
2119
|
+
if (!impureNode) {
|
|
2120
|
+
return void 0;
|
|
2121
|
+
}
|
|
2122
|
+
return evalNode(impureNode).then((result) => {
|
|
2123
|
+
const evaluated = {
|
|
2124
|
+
type: "value",
|
|
2125
|
+
args: result.value,
|
|
2126
|
+
lastInsertId: result.lastInsertId
|
|
2127
|
+
};
|
|
2128
|
+
const purified = replaceImpureNode(node, impureNode, evaluated);
|
|
2129
|
+
if (!purified) {
|
|
2130
|
+
throw new Error("Could not substitute the evaluated impure node into the query plan");
|
|
2131
|
+
}
|
|
2132
|
+
return purified;
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
function replaceImpureNode(node, target, replacement) {
|
|
2136
|
+
if (node === target) {
|
|
2137
|
+
return replacement;
|
|
2138
|
+
}
|
|
2139
|
+
switch (node.type) {
|
|
2140
|
+
case "seq":
|
|
2141
|
+
case "sum":
|
|
2142
|
+
case "concat": {
|
|
2143
|
+
for (let i = 0; i < node.args.length; i++) {
|
|
2144
|
+
const child = replaceImpureNode(node.args[i], target, replacement);
|
|
2145
|
+
if (child) {
|
|
2146
|
+
return { ...node, args: node.args.map((arg, j) => j === i ? child : arg) };
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
return void 0;
|
|
2150
|
+
}
|
|
2151
|
+
case "dataMap":
|
|
2152
|
+
case "validate":
|
|
2153
|
+
case "initializeRecord":
|
|
2154
|
+
case "mapRecord":
|
|
2155
|
+
case "process": {
|
|
2156
|
+
const expr = replaceImpureNode(node.args.expr, target, replacement);
|
|
2157
|
+
return expr && { ...node, args: { ...node.args, expr } };
|
|
2158
|
+
}
|
|
2159
|
+
case "mapField": {
|
|
2160
|
+
const records = replaceImpureNode(node.args.records, target, replacement);
|
|
2161
|
+
return records && { ...node, args: { ...node.args, records } };
|
|
2162
|
+
}
|
|
2163
|
+
case "reverse":
|
|
2164
|
+
case "unique":
|
|
2165
|
+
case "required": {
|
|
2166
|
+
const args = replaceImpureNode(node.args, target, replacement);
|
|
2167
|
+
return args && { ...node, args };
|
|
2168
|
+
}
|
|
2169
|
+
default:
|
|
2170
|
+
return void 0;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
function findUniqueUnconditionalImpureNode(node) {
|
|
2174
|
+
switch (node.type) {
|
|
2175
|
+
case "query":
|
|
2176
|
+
case "execute":
|
|
2177
|
+
return node;
|
|
2178
|
+
case "seq":
|
|
2179
|
+
case "sum":
|
|
2180
|
+
case "concat": {
|
|
2181
|
+
let found = void 0;
|
|
2182
|
+
for (const child of node.args) {
|
|
2183
|
+
const childFound = findUniqueUnconditionalImpureNode(child);
|
|
2184
|
+
if (childFound === null) {
|
|
2185
|
+
return null;
|
|
2186
|
+
}
|
|
2187
|
+
if (childFound) {
|
|
2188
|
+
if (found) {
|
|
2189
|
+
return null;
|
|
2190
|
+
}
|
|
2191
|
+
found = childFound;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
return found;
|
|
2195
|
+
}
|
|
2196
|
+
case "dataMap":
|
|
2197
|
+
case "validate":
|
|
2198
|
+
case "initializeRecord":
|
|
2199
|
+
case "mapRecord":
|
|
2200
|
+
case "process":
|
|
2201
|
+
return findUniqueUnconditionalImpureNode(node.args.expr);
|
|
2202
|
+
case "mapField":
|
|
2203
|
+
return findUniqueUnconditionalImpureNode(node.args.records);
|
|
2204
|
+
case "reverse":
|
|
2205
|
+
case "unique":
|
|
2206
|
+
case "required":
|
|
2207
|
+
return findUniqueUnconditionalImpureNode(node.args);
|
|
2208
|
+
case "let":
|
|
2209
|
+
case "join":
|
|
2210
|
+
case "diff":
|
|
2211
|
+
case "if":
|
|
2212
|
+
case "transaction":
|
|
2213
|
+
return null;
|
|
2214
|
+
case "value":
|
|
2215
|
+
case "get":
|
|
2216
|
+
case "getFirstNonEmpty":
|
|
2217
|
+
case "unit":
|
|
2218
|
+
return void 0;
|
|
2219
|
+
default:
|
|
2220
|
+
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
1937
2223
|
function applyComments(query, sqlCommenter) {
|
|
1938
2224
|
if (!sqlCommenter || sqlCommenter.plugins.length === 0) {
|
|
1939
2225
|
return query;
|
|
@@ -2432,7 +2718,7 @@ function normalizeValue(type, value) {
|
|
|
2432
2718
|
}
|
|
2433
2719
|
|
|
2434
2720
|
// src/transaction-manager/transaction-manager.ts
|
|
2435
|
-
import { Debug } from "@prisma/debug";
|
|
2721
|
+
import { Debug as Debug2 } from "@prisma/debug";
|
|
2436
2722
|
|
|
2437
2723
|
// src/crypto.ts
|
|
2438
2724
|
async function getCrypto() {
|
|
@@ -2500,7 +2786,15 @@ var InvalidTransactionIsolationLevelError = class extends TransactionManagerErro
|
|
|
2500
2786
|
|
|
2501
2787
|
// src/transaction-manager/transaction-manager.ts
|
|
2502
2788
|
var MAX_CLOSED_TRANSACTIONS = 100;
|
|
2503
|
-
var
|
|
2789
|
+
var CANCEL_ROLLBACK_GRACE_MS = 2e3;
|
|
2790
|
+
function trackStartingTransaction() {
|
|
2791
|
+
let markSettled;
|
|
2792
|
+
const settled = new Promise((resolve) => {
|
|
2793
|
+
markSettled = resolve;
|
|
2794
|
+
});
|
|
2795
|
+
return { abortController: new AbortController(), settled, markSettled };
|
|
2796
|
+
}
|
|
2797
|
+
var debug2 = Debug2("prisma:client:transactionManager");
|
|
2504
2798
|
var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
|
|
2505
2799
|
var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
|
|
2506
2800
|
var PHANTOM_COMMIT_QUERY = () => ({
|
|
@@ -2519,6 +2813,9 @@ var TransactionManager = class {
|
|
|
2519
2813
|
// List of last closed transactions. Max MAX_CLOSED_TRANSACTIONS entries.
|
|
2520
2814
|
// Used to provide better error messages than a generic "transaction not found".
|
|
2521
2815
|
closedTransactions = [];
|
|
2816
|
+
// Transactions that are still being started. Tracked separately so that
|
|
2817
|
+
// `cancelAllTransactions` can reach them: they are not in `transactions` yet.
|
|
2818
|
+
#startingTransactions = /* @__PURE__ */ new Set();
|
|
2522
2819
|
driverAdapter;
|
|
2523
2820
|
transactionOptions;
|
|
2524
2821
|
tracingHelper;
|
|
@@ -2582,56 +2879,91 @@ var TransactionManager = class {
|
|
|
2582
2879
|
return { id: existing.id };
|
|
2583
2880
|
});
|
|
2584
2881
|
}
|
|
2585
|
-
const
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2882
|
+
const starting = trackStartingTransaction();
|
|
2883
|
+
const { abortController } = starting;
|
|
2884
|
+
this.#startingTransactions.add(starting);
|
|
2885
|
+
let discarding;
|
|
2886
|
+
try {
|
|
2887
|
+
const transaction = {
|
|
2888
|
+
id: await randomUUID(),
|
|
2889
|
+
status: "waiting",
|
|
2890
|
+
timer: void 0,
|
|
2891
|
+
timeout: options.timeout,
|
|
2892
|
+
startedAt: Date.now(),
|
|
2893
|
+
transaction: void 0,
|
|
2894
|
+
operationQueue: Promise.resolve(),
|
|
2895
|
+
depth: 1,
|
|
2896
|
+
savepoints: [],
|
|
2897
|
+
savepointCounter: 0
|
|
2898
|
+
};
|
|
2899
|
+
if (abortController.signal.aborted) {
|
|
2900
|
+
throw new TransactionStartTimeoutError();
|
|
2901
|
+
}
|
|
2902
|
+
const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
|
|
2903
|
+
startTimer?.unref?.();
|
|
2904
|
+
const startTransactionPromise = this.driverAdapter.startTransaction(options.isolationLevel).catch(rethrowAsUserFacing);
|
|
2905
|
+
transaction.transaction = await Promise.race([
|
|
2906
|
+
startTransactionPromise.finally(() => clearTimeout(startTimer)),
|
|
2907
|
+
once(abortController.signal, "abort").then(() => void 0)
|
|
2908
|
+
]);
|
|
2909
|
+
this.transactions.set(transaction.id, transaction);
|
|
2910
|
+
switch (transaction.status) {
|
|
2911
|
+
case "waiting":
|
|
2912
|
+
if (abortController.signal.aborted) {
|
|
2913
|
+
transaction.transaction = void 0;
|
|
2914
|
+
discarding = this.#discardStartedTransaction(startTransactionPromise);
|
|
2915
|
+
await this.#closeTransaction(transaction, "timed_out");
|
|
2916
|
+
throw new TransactionStartTimeoutError();
|
|
2917
|
+
}
|
|
2918
|
+
transaction.status = "running";
|
|
2919
|
+
transaction.startedAt = Date.now();
|
|
2920
|
+
transaction.timer = this.#startTransactionTimeout(transaction.id, options.timeout);
|
|
2921
|
+
return { id: transaction.id };
|
|
2922
|
+
case "timed_out":
|
|
2923
|
+
case "running":
|
|
2924
|
+
case "committed":
|
|
2925
|
+
case "rolled_back":
|
|
2926
|
+
throw new TransactionInternalConsistencyError(
|
|
2927
|
+
`Transaction in invalid state ${transaction.status} although it just finished startup.`
|
|
2928
|
+
);
|
|
2929
|
+
default:
|
|
2930
|
+
return assertNever(transaction["status"], "Unknown transaction status.");
|
|
2931
|
+
}
|
|
2932
|
+
} finally {
|
|
2933
|
+
this.#startingTransactions.delete(starting);
|
|
2934
|
+
if (discarding) {
|
|
2935
|
+
void discarding.finally(starting.markSettled);
|
|
2936
|
+
} else {
|
|
2937
|
+
starting.markSettled();
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Rolls back a transaction whose start was abandoned, and releases its connection.
|
|
2943
|
+
*
|
|
2944
|
+
* The `startTransaction` promise may still be running in the background. If it eventually
|
|
2945
|
+
* succeeds, we need to roll back and release the connection to avoid leaking it and
|
|
2946
|
+
* exhausting the connection pool. For adapters that don't use phantom queries (e.g. pg/neon),
|
|
2947
|
+
* `rollback()` only releases the connection without sending SQL, so we send an explicit
|
|
2948
|
+
* ROLLBACK first; otherwise the connection returns to the pool mid-transaction because
|
|
2949
|
+
* `BEGIN` already ran on the wire during startup.
|
|
2950
|
+
*
|
|
2951
|
+
* Errors are only logged: the caller has already reported the failure that led here.
|
|
2952
|
+
*/
|
|
2953
|
+
async #discardStartedTransaction(startTransactionPromise) {
|
|
2954
|
+
try {
|
|
2955
|
+
const tx = await startTransactionPromise;
|
|
2956
|
+
if (tx.options.usePhantomQuery) {
|
|
2957
|
+
await tx.rollback();
|
|
2958
|
+
} else {
|
|
2959
|
+
try {
|
|
2960
|
+
await tx.executeRaw(ROLLBACK_QUERY());
|
|
2961
|
+
} finally {
|
|
2962
|
+
await tx.rollback();
|
|
2622
2963
|
}
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
case "timed_out":
|
|
2627
|
-
case "running":
|
|
2628
|
-
case "committed":
|
|
2629
|
-
case "rolled_back":
|
|
2630
|
-
throw new TransactionInternalConsistencyError(
|
|
2631
|
-
`Transaction in invalid state ${transaction.status} although it just finished startup.`
|
|
2632
|
-
);
|
|
2633
|
-
default:
|
|
2634
|
-
assertNever(transaction["status"], "Unknown transaction status.");
|
|
2964
|
+
}
|
|
2965
|
+
} catch (e) {
|
|
2966
|
+
debug2("error in discarded transaction:", e);
|
|
2635
2967
|
}
|
|
2636
2968
|
}
|
|
2637
2969
|
async commitTransaction(transactionId) {
|
|
@@ -2695,7 +3027,7 @@ var TransactionManager = class {
|
|
|
2695
3027
|
if (!transaction) {
|
|
2696
3028
|
const closedTransaction = this.closedTransactions.find((tx) => tx.id === transactionId);
|
|
2697
3029
|
if (closedTransaction) {
|
|
2698
|
-
|
|
3030
|
+
debug2("Transaction already closed.", { transactionId, status: closedTransaction.status });
|
|
2699
3031
|
switch (closedTransaction.status) {
|
|
2700
3032
|
case "closing":
|
|
2701
3033
|
case "waiting":
|
|
@@ -2712,7 +3044,7 @@ var TransactionManager = class {
|
|
|
2712
3044
|
});
|
|
2713
3045
|
}
|
|
2714
3046
|
} else {
|
|
2715
|
-
|
|
3047
|
+
debug2(`Transaction not found.`, transactionId);
|
|
2716
3048
|
throw new TransactionNotFoundError();
|
|
2717
3049
|
}
|
|
2718
3050
|
}
|
|
@@ -2722,16 +3054,21 @@ var TransactionManager = class {
|
|
|
2722
3054
|
return transaction;
|
|
2723
3055
|
}
|
|
2724
3056
|
async cancelAllTransactions() {
|
|
2725
|
-
|
|
2726
|
-
|
|
3057
|
+
const starting = [...this.#startingTransactions];
|
|
3058
|
+
for (const { abortController } of starting) {
|
|
3059
|
+
abortController.abort();
|
|
3060
|
+
}
|
|
3061
|
+
await Promise.allSettled([
|
|
3062
|
+
...[...this.transactions.values()].map(
|
|
2727
3063
|
(tx) => this.#runSerialized(tx, async () => {
|
|
2728
3064
|
const current = this.transactions.get(tx.id);
|
|
2729
3065
|
if (current) {
|
|
2730
3066
|
await this.#closeTransaction(current, "rolled_back");
|
|
2731
3067
|
}
|
|
2732
3068
|
})
|
|
2733
|
-
)
|
|
2734
|
-
|
|
3069
|
+
),
|
|
3070
|
+
...starting.map(({ settled }) => settleWithin(settled, CANCEL_ROLLBACK_GRACE_MS))
|
|
3071
|
+
]);
|
|
2735
3072
|
}
|
|
2736
3073
|
#nextSavepointName(transaction) {
|
|
2737
3074
|
return `prisma_sp_${transaction.savepointCounter++}`;
|
|
@@ -2758,25 +3095,29 @@ var TransactionManager = class {
|
|
|
2758
3095
|
}
|
|
2759
3096
|
}
|
|
2760
3097
|
#debugTransactionAlreadyClosedOnTimeout(transactionId) {
|
|
2761
|
-
|
|
3098
|
+
debug2("Transaction already committed or rolled back when timeout happened.", transactionId);
|
|
2762
3099
|
}
|
|
2763
3100
|
#startTransactionTimeout(transactionId, timeout) {
|
|
2764
3101
|
const timeoutStartedAt = Date.now();
|
|
2765
3102
|
const timer = createTimeoutIfDefined(async () => {
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
return;
|
|
2771
|
-
}
|
|
2772
|
-
await this.#runSerialized(tx, async () => {
|
|
2773
|
-
const current = this.transactions.get(transactionId);
|
|
2774
|
-
if (current && ["running", "waiting"].includes(current.status)) {
|
|
2775
|
-
await this.#closeTransaction(current, "timed_out");
|
|
2776
|
-
} else {
|
|
3103
|
+
try {
|
|
3104
|
+
debug2("Transaction timed out.", { transactionId, timeoutStartedAt, timeout });
|
|
3105
|
+
const tx = this.transactions.get(transactionId);
|
|
3106
|
+
if (!tx) {
|
|
2777
3107
|
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
3108
|
+
return;
|
|
2778
3109
|
}
|
|
2779
|
-
|
|
3110
|
+
await this.#runSerialized(tx, async () => {
|
|
3111
|
+
const current = this.transactions.get(transactionId);
|
|
3112
|
+
if (current && ["running", "waiting"].includes(current.status)) {
|
|
3113
|
+
await this.#closeTransaction(current, "timed_out");
|
|
3114
|
+
} else {
|
|
3115
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
3116
|
+
}
|
|
3117
|
+
});
|
|
3118
|
+
} catch (error) {
|
|
3119
|
+
debug2("Error while closing timed-out transaction.", { transactionId, error });
|
|
3120
|
+
}
|
|
2780
3121
|
}, timeout);
|
|
2781
3122
|
timer?.unref?.();
|
|
2782
3123
|
return timer;
|
|
@@ -2808,7 +3149,7 @@ var TransactionManager = class {
|
|
|
2808
3149
|
}
|
|
2809
3150
|
async #closeTransaction(tx, status) {
|
|
2810
3151
|
const createClosingPromise = async () => {
|
|
2811
|
-
|
|
3152
|
+
debug2("Closing transaction.", { transactionId: tx.id, status });
|
|
2812
3153
|
try {
|
|
2813
3154
|
if (tx.transaction && status === "committed") {
|
|
2814
3155
|
if (tx.transaction.options.usePhantomQuery) {
|
|
@@ -2884,6 +3225,14 @@ var TransactionManager = class {
|
|
|
2884
3225
|
function createTimeoutIfDefined(cb, ms) {
|
|
2885
3226
|
return ms !== void 0 ? setTimeout(cb, ms) : void 0;
|
|
2886
3227
|
}
|
|
3228
|
+
function settleWithin(promise, timeout) {
|
|
3229
|
+
let timer;
|
|
3230
|
+
const deadline = new Promise((resolve) => {
|
|
3231
|
+
timer = setTimeout(resolve, timeout);
|
|
3232
|
+
timer?.unref?.();
|
|
3233
|
+
});
|
|
3234
|
+
return Promise.race([promise, deadline]).finally(() => clearTimeout(timer));
|
|
3235
|
+
}
|
|
2887
3236
|
export {
|
|
2888
3237
|
DataMapperError,
|
|
2889
3238
|
QueryInterpreter,
|