@prisma/client-engine-runtime 7.10.0-dev.9 → 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 +393 -99
- package/dist/index.mjs +393 -99
- 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.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":
|
|
@@ -224,7 +238,11 @@ function rethrowAsUserFacing(error) {
|
|
|
224
238
|
const code = getErrorCode(error);
|
|
225
239
|
const message = renderErrorMessage(error);
|
|
226
240
|
if (code !== void 0 && message !== void 0) {
|
|
227
|
-
|
|
241
|
+
const meta = { driverAdapterError: error };
|
|
242
|
+
if (error.cause.kind === "UniqueConstraintViolation" && error.cause.table) {
|
|
243
|
+
meta.table = error.cause.table;
|
|
244
|
+
}
|
|
245
|
+
throw new UserFacingError(message, code, meta);
|
|
228
246
|
}
|
|
229
247
|
if (isGenericDatabaseErrorKind(error.cause.kind)) {
|
|
230
248
|
throw buildUnmappedDatabaseUserFacingError(error);
|
|
@@ -290,6 +308,7 @@ function getErrorCode(err) {
|
|
|
290
308
|
case "UniqueConstraintViolation":
|
|
291
309
|
return "P2002";
|
|
292
310
|
case "ForeignKeyConstraintViolation":
|
|
311
|
+
case "RestrictViolation":
|
|
293
312
|
return "P2003";
|
|
294
313
|
case "InvalidInputValue":
|
|
295
314
|
return "P2007";
|
|
@@ -362,6 +381,7 @@ function renderErrorMessage(err) {
|
|
|
362
381
|
case "UniqueConstraintViolation":
|
|
363
382
|
return `Unique constraint failed on the ${renderConstraint(err.cause.constraint)}`;
|
|
364
383
|
case "ForeignKeyConstraintViolation":
|
|
384
|
+
case "RestrictViolation":
|
|
365
385
|
return `Foreign key constraint violated on the ${renderConstraint(err.cause.constraint)}`;
|
|
366
386
|
case "UnsupportedNativeDataType":
|
|
367
387
|
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 +658,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
638
658
|
throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
|
|
639
659
|
}
|
|
640
660
|
}
|
|
641
|
-
if (Array.isArray(value) || value
|
|
661
|
+
if (Array.isArray(value) || isUint8Array(value)) {
|
|
642
662
|
for (const byte of value) {
|
|
643
663
|
if (byte !== 0) return true;
|
|
644
664
|
}
|
|
@@ -655,7 +675,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
655
675
|
if (typeof value === "string") {
|
|
656
676
|
return { $type: "DateTime", value: normalizeDateTime(value) };
|
|
657
677
|
}
|
|
658
|
-
if (typeof value === "number" || value
|
|
678
|
+
if (typeof value === "number" || isDate(value)) {
|
|
659
679
|
return { $type: "DateTime", value };
|
|
660
680
|
}
|
|
661
681
|
throw new DataMapperError(`Expected a date in column '${columnName}', got ${typeof value}: ${value}`);
|
|
@@ -686,7 +706,7 @@ function mapValue(value, columnName, scalarType, enums) {
|
|
|
686
706
|
if (Array.isArray(value)) {
|
|
687
707
|
return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
|
|
688
708
|
}
|
|
689
|
-
if (value
|
|
709
|
+
if (isUint8Array(value)) {
|
|
690
710
|
return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
|
|
691
711
|
}
|
|
692
712
|
throw new DataMapperError(`Expected a byte array in column '${columnName}', got ${typeof value}: ${value}`);
|
|
@@ -734,6 +754,7 @@ function normalizeDateTime(dt) {
|
|
|
734
754
|
}
|
|
735
755
|
|
|
736
756
|
// src/interpreter/query-interpreter.ts
|
|
757
|
+
import { Debug } from "@prisma/debug";
|
|
737
758
|
import { klona as klona2 } from "klona";
|
|
738
759
|
|
|
739
760
|
// src/sql-commenter.ts
|
|
@@ -1095,8 +1116,9 @@ function renderTemplateSql(fragments, placeholderFormat, params, argTypes) {
|
|
|
1095
1116
|
if (fragment.type === "stringChunk") {
|
|
1096
1117
|
continue;
|
|
1097
1118
|
}
|
|
1098
|
-
const
|
|
1099
|
-
const added =
|
|
1119
|
+
const fragmentParams = Array.from(flattenedFragmentParams(fragment));
|
|
1120
|
+
const added = fragmentParams.length;
|
|
1121
|
+
appendToArray(flattenedParams, fragmentParams);
|
|
1100
1122
|
if (fragment.argType.arity === "tuple") {
|
|
1101
1123
|
if (added % fragment.argType.elements.length !== 0) {
|
|
1102
1124
|
throw new Error(
|
|
@@ -1525,6 +1547,7 @@ function getErrorCode2(error) {
|
|
|
1525
1547
|
}
|
|
1526
1548
|
|
|
1527
1549
|
// src/interpreter/query-interpreter.ts
|
|
1550
|
+
var debug = Debug("prisma:client:queryInterpreter");
|
|
1528
1551
|
var QueryInterpreter = class _QueryInterpreter {
|
|
1529
1552
|
#onQuery;
|
|
1530
1553
|
#generators = new GeneratorRegistry();
|
|
@@ -1559,17 +1582,27 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1559
1582
|
});
|
|
1560
1583
|
}
|
|
1561
1584
|
async run(queryPlan, options) {
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1585
|
+
const generators = this.#generators.snapshot();
|
|
1586
|
+
const context = { ...options, generators };
|
|
1587
|
+
const purified = purifyQueryPlan(queryPlan, (node) => this.interpretNode(node, context))?.catch(
|
|
1588
|
+
(e) => rethrowAsUserFacing(e)
|
|
1589
|
+
);
|
|
1590
|
+
if (purified) {
|
|
1591
|
+
try {
|
|
1592
|
+
return this.#interpretPureNode(await purified, context.scope, generators).value;
|
|
1593
|
+
} catch (e) {
|
|
1594
|
+
rethrowAsUserFacing(e);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
const { value } = await this.interpretNode(queryPlan, context).catch((e) => rethrowAsUserFacing(e));
|
|
1566
1598
|
return value;
|
|
1567
1599
|
}
|
|
1568
1600
|
async interpretNode(node, context) {
|
|
1569
1601
|
switch (node.type) {
|
|
1570
1602
|
case "value": {
|
|
1571
1603
|
return {
|
|
1572
|
-
value: evaluateArg(node.args, context.scope, context.generators)
|
|
1604
|
+
value: evaluateArg(node.args, context.scope, context.generators),
|
|
1605
|
+
lastInsertId: node.lastInsertId
|
|
1573
1606
|
};
|
|
1574
1607
|
}
|
|
1575
1608
|
case "seq": {
|
|
@@ -1579,9 +1612,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1579
1612
|
}
|
|
1580
1613
|
return result ?? { value: void 0 };
|
|
1581
1614
|
}
|
|
1582
|
-
case "get": {
|
|
1583
|
-
return { value: context.scope[node.args.name] };
|
|
1584
|
-
}
|
|
1585
1615
|
case "let": {
|
|
1586
1616
|
const nestedScope = Object.create(context.scope);
|
|
1587
1617
|
for (const binding of node.args.bindings) {
|
|
@@ -1590,15 +1620,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1590
1620
|
}
|
|
1591
1621
|
return this.interpretNode(node.args.expr, { ...context, scope: nestedScope });
|
|
1592
1622
|
}
|
|
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
1623
|
case "concat": {
|
|
1603
1624
|
const parts = await Promise.all(
|
|
1604
1625
|
node.args.map((arg) => this.interpretNode(arg, context).then((res) => res.value))
|
|
@@ -1617,42 +1638,46 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1617
1638
|
}
|
|
1618
1639
|
case "execute": {
|
|
1619
1640
|
const queries = renderQuery(node.args, context.scope, context.generators, this.#maxChunkSize());
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
(
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1641
|
+
return this.#withChunkTransaction(queries.length, context, async (context2) => {
|
|
1642
|
+
let sum = 0;
|
|
1643
|
+
for (const query of queries) {
|
|
1644
|
+
const commentedQuery = applyComments(query, context2.sqlCommenter);
|
|
1645
|
+
sum += await this.#withQuerySpanAndEvent(
|
|
1646
|
+
commentedQuery,
|
|
1647
|
+
context2.queryable,
|
|
1648
|
+
() => context2.queryable.executeRaw(cloneObject(commentedQuery)).catch(
|
|
1649
|
+
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1650
|
+
)
|
|
1651
|
+
);
|
|
1652
|
+
}
|
|
1653
|
+
return { value: sum };
|
|
1654
|
+
});
|
|
1632
1655
|
}
|
|
1633
1656
|
case "query": {
|
|
1634
1657
|
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
|
-
|
|
1658
|
+
return this.#withChunkTransaction(queries.length, context, async (context2) => {
|
|
1659
|
+
let results;
|
|
1660
|
+
for (const query of queries) {
|
|
1661
|
+
const commentedQuery = applyComments(query, context2.sqlCommenter);
|
|
1662
|
+
const result = await this.#withQuerySpanAndEvent(
|
|
1663
|
+
commentedQuery,
|
|
1664
|
+
context2.queryable,
|
|
1665
|
+
() => context2.queryable.queryRaw(cloneObject(commentedQuery)).catch(
|
|
1666
|
+
(err) => node.args.type === "rawSql" ? rethrowAsUserFacingRawError(err) : rethrowAsUserFacing(err)
|
|
1667
|
+
)
|
|
1668
|
+
);
|
|
1669
|
+
if (results === void 0) {
|
|
1670
|
+
results = result;
|
|
1671
|
+
} else {
|
|
1672
|
+
appendToArray(results.rows, result.rows);
|
|
1673
|
+
results.lastInsertId = result.lastInsertId;
|
|
1674
|
+
}
|
|
1650
1675
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
};
|
|
1676
|
+
return {
|
|
1677
|
+
value: node.args.type === "rawSql" ? this.#rawSerializer(results) : this.#serializer(results),
|
|
1678
|
+
lastInsertId: results?.lastInsertId
|
|
1679
|
+
};
|
|
1680
|
+
});
|
|
1656
1681
|
}
|
|
1657
1682
|
case "reverse": {
|
|
1658
1683
|
const { value, lastInsertId } = await this.interpretNode(node.args, context);
|
|
@@ -1693,20 +1718,7 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1693
1718
|
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1694
1719
|
}
|
|
1695
1720
|
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
|
-
}
|
|
1721
|
+
return this.#withInternalTransaction(context, (context2) => this.interpretNode(node.args, context2));
|
|
1710
1722
|
}
|
|
1711
1723
|
case "dataMap": {
|
|
1712
1724
|
const { value, lastInsertId } = await this.interpretNode(node.args.expr, context);
|
|
@@ -1725,9 +1737,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1725
1737
|
return await this.interpretNode(node.args.else, context);
|
|
1726
1738
|
}
|
|
1727
1739
|
}
|
|
1728
|
-
case "unit": {
|
|
1729
|
-
return { value: void 0 };
|
|
1730
|
-
}
|
|
1731
1740
|
case "diff": {
|
|
1732
1741
|
const { value: from } = await this.interpretNode(node.args.from, context);
|
|
1733
1742
|
const { value: to } = await this.interpretNode(node.args.to, context);
|
|
@@ -1757,10 +1766,185 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1757
1766
|
}
|
|
1758
1767
|
return { value: record, lastInsertId };
|
|
1759
1768
|
}
|
|
1769
|
+
default:
|
|
1770
|
+
return this.#interpretPureNode(node, context.scope, context.generators);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
#interpretPureNode(node, scope, generators) {
|
|
1774
|
+
switch (node.type) {
|
|
1775
|
+
case "value": {
|
|
1776
|
+
return { value: evaluateArg(node.args, scope, generators), lastInsertId: node.lastInsertId };
|
|
1777
|
+
}
|
|
1778
|
+
case "seq": {
|
|
1779
|
+
let result;
|
|
1780
|
+
for (const arg of node.args) {
|
|
1781
|
+
result = this.#interpretPureNode(arg, scope, generators);
|
|
1782
|
+
}
|
|
1783
|
+
return result ?? { value: void 0 };
|
|
1784
|
+
}
|
|
1785
|
+
case "get": {
|
|
1786
|
+
return { value: scope[node.args.name] };
|
|
1787
|
+
}
|
|
1788
|
+
case "let": {
|
|
1789
|
+
const nestedScope = Object.create(scope);
|
|
1790
|
+
for (const binding of node.args.bindings) {
|
|
1791
|
+
const { value } = this.#interpretPureNode(binding.expr, nestedScope, generators);
|
|
1792
|
+
nestedScope[binding.name] = value;
|
|
1793
|
+
}
|
|
1794
|
+
return this.#interpretPureNode(node.args.expr, nestedScope, generators);
|
|
1795
|
+
}
|
|
1796
|
+
case "getFirstNonEmpty": {
|
|
1797
|
+
for (const name of node.args.names) {
|
|
1798
|
+
const value = scope[name];
|
|
1799
|
+
if (!isEmpty(value)) {
|
|
1800
|
+
return { value };
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
return { value: [] };
|
|
1804
|
+
}
|
|
1805
|
+
case "concat": {
|
|
1806
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1807
|
+
return {
|
|
1808
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : []
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
case "sum": {
|
|
1812
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1813
|
+
return {
|
|
1814
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => asNumber2(acc) + asNumber2(part)) : 0
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
case "reverse": {
|
|
1818
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1819
|
+
return { value: Array.isArray(value) ? value.reverse() : value, lastInsertId };
|
|
1820
|
+
}
|
|
1821
|
+
case "unique": {
|
|
1822
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1823
|
+
if (!Array.isArray(value)) {
|
|
1824
|
+
return { value, lastInsertId };
|
|
1825
|
+
}
|
|
1826
|
+
if (value.length > 1) {
|
|
1827
|
+
throw new Error(`Expected zero or one element, got ${value.length}`);
|
|
1828
|
+
}
|
|
1829
|
+
return { value: value[0] ?? null, lastInsertId };
|
|
1830
|
+
}
|
|
1831
|
+
case "required": {
|
|
1832
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1833
|
+
if (isEmpty(value)) {
|
|
1834
|
+
throw new Error("Required value is empty");
|
|
1835
|
+
}
|
|
1836
|
+
return { value, lastInsertId };
|
|
1837
|
+
}
|
|
1838
|
+
case "mapField": {
|
|
1839
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.records, scope, generators);
|
|
1840
|
+
return { value: mapField2(value, node.args.field), lastInsertId };
|
|
1841
|
+
}
|
|
1842
|
+
case "join": {
|
|
1843
|
+
const { value: parent, lastInsertId } = this.#interpretPureNode(node.args.parent, scope, generators);
|
|
1844
|
+
if (parent === null) {
|
|
1845
|
+
return { value: null, lastInsertId };
|
|
1846
|
+
}
|
|
1847
|
+
const children = node.args.children.map((joinExpr) => ({
|
|
1848
|
+
joinExpr,
|
|
1849
|
+
childRecords: this.#interpretPureNode(joinExpr.child, scope, generators).value
|
|
1850
|
+
}));
|
|
1851
|
+
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1852
|
+
}
|
|
1853
|
+
case "dataMap": {
|
|
1854
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1855
|
+
return { value: applyDataMap(value, node.args.structure, node.args.enums), lastInsertId };
|
|
1856
|
+
}
|
|
1857
|
+
case "validate": {
|
|
1858
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1859
|
+
performValidation(value, node.args.rules, node.args);
|
|
1860
|
+
return { value, lastInsertId };
|
|
1861
|
+
}
|
|
1862
|
+
case "if": {
|
|
1863
|
+
const { value } = this.#interpretPureNode(node.args.value, scope, generators);
|
|
1864
|
+
if (doesSatisfyRule(value, node.args.rule)) {
|
|
1865
|
+
return this.#interpretPureNode(node.args.then, scope, generators);
|
|
1866
|
+
} else {
|
|
1867
|
+
return this.#interpretPureNode(node.args.else, scope, generators);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
case "unit": {
|
|
1871
|
+
return { value: void 0 };
|
|
1872
|
+
}
|
|
1873
|
+
case "diff": {
|
|
1874
|
+
const { value: from } = this.#interpretPureNode(node.args.from, scope, generators);
|
|
1875
|
+
const { value: to } = this.#interpretPureNode(node.args.to, scope, generators);
|
|
1876
|
+
const keyGetter = (item) => item !== null ? getRecordKey(asRecord(item), node.args.fields) : null;
|
|
1877
|
+
const toSet = new Set(asList(to).map(keyGetter));
|
|
1878
|
+
return { value: asList(from).filter((item) => !toSet.has(keyGetter(item))) };
|
|
1879
|
+
}
|
|
1880
|
+
case "process": {
|
|
1881
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1882
|
+
const ops = cloneObject(node.args.operations);
|
|
1883
|
+
evaluateProcessingParameters(ops, scope, generators);
|
|
1884
|
+
return { value: processRecords(value, ops), lastInsertId };
|
|
1885
|
+
}
|
|
1886
|
+
case "initializeRecord": {
|
|
1887
|
+
const { lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1888
|
+
const record = {};
|
|
1889
|
+
for (const [key, initializer] of Object.entries(node.args.fields)) {
|
|
1890
|
+
record[key] = evalFieldInitializer(initializer, lastInsertId, scope, generators);
|
|
1891
|
+
}
|
|
1892
|
+
return { value: record, lastInsertId };
|
|
1893
|
+
}
|
|
1894
|
+
case "mapRecord": {
|
|
1895
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1896
|
+
const record = value === null ? {} : asRecord(value);
|
|
1897
|
+
for (const [key, entry] of Object.entries(node.args.fields)) {
|
|
1898
|
+
record[key] = evalFieldOperation(entry, record[key], scope, generators);
|
|
1899
|
+
}
|
|
1900
|
+
return { value: record, lastInsertId };
|
|
1901
|
+
}
|
|
1760
1902
|
default:
|
|
1761
1903
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
1762
1904
|
}
|
|
1763
1905
|
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Runs the statements of a `query` or `execute` node via `fn`, wrapping them in a
|
|
1908
|
+
* transaction when a chunkable statement was split into multiple queries at render time,
|
|
1909
|
+
* so that a partially applied write cannot be observed or left behind if a later chunk
|
|
1910
|
+
* fails. A single statement is atomic on its own, so it runs on the current context.
|
|
1911
|
+
*/
|
|
1912
|
+
#withChunkTransaction(statementCount, context, fn) {
|
|
1913
|
+
if (statementCount <= 1) {
|
|
1914
|
+
return fn(context);
|
|
1915
|
+
}
|
|
1916
|
+
return this.#withInternalTransaction(context, fn);
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Runs `fn` with a context whose queryable is guaranteed to be a transaction, starting a
|
|
1920
|
+
* new internal transaction and committing or rolling it back around the call.
|
|
1921
|
+
*
|
|
1922
|
+
* A disabled transaction manager means the queryable already is a transaction: executors
|
|
1923
|
+
* pass `{ enabled: false }` when the plan runs inside an interactive transaction, and the
|
|
1924
|
+
* context handed to `fn` carries it for the duration of an internal transaction. In that
|
|
1925
|
+
* case `fn` runs on the current context, since the statements it issues are already
|
|
1926
|
+
* covered by the surrounding transaction.
|
|
1927
|
+
*/
|
|
1928
|
+
async #withInternalTransaction(context, fn) {
|
|
1929
|
+
if (!context.transactionManager.enabled) {
|
|
1930
|
+
return fn(context);
|
|
1931
|
+
}
|
|
1932
|
+
const transactionManager = context.transactionManager.manager;
|
|
1933
|
+
const transactionInfo = await transactionManager.startInternalTransaction();
|
|
1934
|
+
const transaction = await transactionManager.getTransaction(transactionInfo, "query");
|
|
1935
|
+
try {
|
|
1936
|
+
const result = await fn({ ...context, queryable: transaction, transactionManager: { enabled: false } });
|
|
1937
|
+
await transactionManager.commitTransaction(transactionInfo.id);
|
|
1938
|
+
return result;
|
|
1939
|
+
} catch (e) {
|
|
1940
|
+
try {
|
|
1941
|
+
await transactionManager.rollbackTransaction(transactionInfo.id);
|
|
1942
|
+
} catch (rollbackError) {
|
|
1943
|
+
debug("failed to roll back an internal transaction", rollbackError);
|
|
1944
|
+
}
|
|
1945
|
+
throw e;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1764
1948
|
#maxChunkSize() {
|
|
1765
1949
|
if (this.#connectionInfo?.maxBindValues !== void 0) {
|
|
1766
1950
|
return this.#connectionInfo.maxBindValues;
|
|
@@ -1934,6 +2118,112 @@ function evalFieldOperation(op, value, scope, generators) {
|
|
|
1934
2118
|
assertNever(op, `Unexpected field operation type: ${op["type"]}`);
|
|
1935
2119
|
}
|
|
1936
2120
|
}
|
|
2121
|
+
function purifyQueryPlan(node, evalNode) {
|
|
2122
|
+
const impureNode = findUniqueUnconditionalImpureNode(node);
|
|
2123
|
+
if (!impureNode) {
|
|
2124
|
+
return void 0;
|
|
2125
|
+
}
|
|
2126
|
+
return evalNode(impureNode).then((result) => {
|
|
2127
|
+
const evaluated = {
|
|
2128
|
+
type: "value",
|
|
2129
|
+
args: result.value,
|
|
2130
|
+
lastInsertId: result.lastInsertId
|
|
2131
|
+
};
|
|
2132
|
+
const purified = replaceImpureNode(node, impureNode, evaluated);
|
|
2133
|
+
if (!purified) {
|
|
2134
|
+
throw new Error("Could not substitute the evaluated impure node into the query plan");
|
|
2135
|
+
}
|
|
2136
|
+
return purified;
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
function replaceImpureNode(node, target, replacement) {
|
|
2140
|
+
if (node === target) {
|
|
2141
|
+
return replacement;
|
|
2142
|
+
}
|
|
2143
|
+
switch (node.type) {
|
|
2144
|
+
case "seq":
|
|
2145
|
+
case "sum":
|
|
2146
|
+
case "concat": {
|
|
2147
|
+
for (let i = 0; i < node.args.length; i++) {
|
|
2148
|
+
const child = replaceImpureNode(node.args[i], target, replacement);
|
|
2149
|
+
if (child) {
|
|
2150
|
+
return { ...node, args: node.args.map((arg, j) => j === i ? child : arg) };
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
return void 0;
|
|
2154
|
+
}
|
|
2155
|
+
case "dataMap":
|
|
2156
|
+
case "validate":
|
|
2157
|
+
case "initializeRecord":
|
|
2158
|
+
case "mapRecord":
|
|
2159
|
+
case "process": {
|
|
2160
|
+
const expr = replaceImpureNode(node.args.expr, target, replacement);
|
|
2161
|
+
return expr && { ...node, args: { ...node.args, expr } };
|
|
2162
|
+
}
|
|
2163
|
+
case "mapField": {
|
|
2164
|
+
const records = replaceImpureNode(node.args.records, target, replacement);
|
|
2165
|
+
return records && { ...node, args: { ...node.args, records } };
|
|
2166
|
+
}
|
|
2167
|
+
case "reverse":
|
|
2168
|
+
case "unique":
|
|
2169
|
+
case "required": {
|
|
2170
|
+
const args = replaceImpureNode(node.args, target, replacement);
|
|
2171
|
+
return args && { ...node, args };
|
|
2172
|
+
}
|
|
2173
|
+
default:
|
|
2174
|
+
return void 0;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
function findUniqueUnconditionalImpureNode(node) {
|
|
2178
|
+
switch (node.type) {
|
|
2179
|
+
case "query":
|
|
2180
|
+
case "execute":
|
|
2181
|
+
return node;
|
|
2182
|
+
case "seq":
|
|
2183
|
+
case "sum":
|
|
2184
|
+
case "concat": {
|
|
2185
|
+
let found = void 0;
|
|
2186
|
+
for (const child of node.args) {
|
|
2187
|
+
const childFound = findUniqueUnconditionalImpureNode(child);
|
|
2188
|
+
if (childFound === null) {
|
|
2189
|
+
return null;
|
|
2190
|
+
}
|
|
2191
|
+
if (childFound) {
|
|
2192
|
+
if (found) {
|
|
2193
|
+
return null;
|
|
2194
|
+
}
|
|
2195
|
+
found = childFound;
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
return found;
|
|
2199
|
+
}
|
|
2200
|
+
case "dataMap":
|
|
2201
|
+
case "validate":
|
|
2202
|
+
case "initializeRecord":
|
|
2203
|
+
case "mapRecord":
|
|
2204
|
+
case "process":
|
|
2205
|
+
return findUniqueUnconditionalImpureNode(node.args.expr);
|
|
2206
|
+
case "mapField":
|
|
2207
|
+
return findUniqueUnconditionalImpureNode(node.args.records);
|
|
2208
|
+
case "reverse":
|
|
2209
|
+
case "unique":
|
|
2210
|
+
case "required":
|
|
2211
|
+
return findUniqueUnconditionalImpureNode(node.args);
|
|
2212
|
+
case "let":
|
|
2213
|
+
case "join":
|
|
2214
|
+
case "diff":
|
|
2215
|
+
case "if":
|
|
2216
|
+
case "transaction":
|
|
2217
|
+
return null;
|
|
2218
|
+
case "value":
|
|
2219
|
+
case "get":
|
|
2220
|
+
case "getFirstNonEmpty":
|
|
2221
|
+
case "unit":
|
|
2222
|
+
return void 0;
|
|
2223
|
+
default:
|
|
2224
|
+
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
1937
2227
|
function applyComments(query, sqlCommenter) {
|
|
1938
2228
|
if (!sqlCommenter || sqlCommenter.plugins.length === 0) {
|
|
1939
2229
|
return query;
|
|
@@ -2432,7 +2722,7 @@ function normalizeValue(type, value) {
|
|
|
2432
2722
|
}
|
|
2433
2723
|
|
|
2434
2724
|
// src/transaction-manager/transaction-manager.ts
|
|
2435
|
-
import { Debug } from "@prisma/debug";
|
|
2725
|
+
import { Debug as Debug2 } from "@prisma/debug";
|
|
2436
2726
|
|
|
2437
2727
|
// src/crypto.ts
|
|
2438
2728
|
async function getCrypto() {
|
|
@@ -2508,7 +2798,7 @@ function trackStartingTransaction() {
|
|
|
2508
2798
|
});
|
|
2509
2799
|
return { abortController: new AbortController(), settled, markSettled };
|
|
2510
2800
|
}
|
|
2511
|
-
var
|
|
2801
|
+
var debug2 = Debug2("prisma:client:transactionManager");
|
|
2512
2802
|
var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
|
|
2513
2803
|
var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
|
|
2514
2804
|
var PHANTOM_COMMIT_QUERY = () => ({
|
|
@@ -2677,7 +2967,7 @@ var TransactionManager = class {
|
|
|
2677
2967
|
}
|
|
2678
2968
|
}
|
|
2679
2969
|
} catch (e) {
|
|
2680
|
-
|
|
2970
|
+
debug2("error in discarded transaction:", e);
|
|
2681
2971
|
}
|
|
2682
2972
|
}
|
|
2683
2973
|
async commitTransaction(transactionId) {
|
|
@@ -2741,7 +3031,7 @@ var TransactionManager = class {
|
|
|
2741
3031
|
if (!transaction) {
|
|
2742
3032
|
const closedTransaction = this.closedTransactions.find((tx) => tx.id === transactionId);
|
|
2743
3033
|
if (closedTransaction) {
|
|
2744
|
-
|
|
3034
|
+
debug2("Transaction already closed.", { transactionId, status: closedTransaction.status });
|
|
2745
3035
|
switch (closedTransaction.status) {
|
|
2746
3036
|
case "closing":
|
|
2747
3037
|
case "waiting":
|
|
@@ -2758,7 +3048,7 @@ var TransactionManager = class {
|
|
|
2758
3048
|
});
|
|
2759
3049
|
}
|
|
2760
3050
|
} else {
|
|
2761
|
-
|
|
3051
|
+
debug2(`Transaction not found.`, transactionId);
|
|
2762
3052
|
throw new TransactionNotFoundError();
|
|
2763
3053
|
}
|
|
2764
3054
|
}
|
|
@@ -2809,25 +3099,29 @@ var TransactionManager = class {
|
|
|
2809
3099
|
}
|
|
2810
3100
|
}
|
|
2811
3101
|
#debugTransactionAlreadyClosedOnTimeout(transactionId) {
|
|
2812
|
-
|
|
3102
|
+
debug2("Transaction already committed or rolled back when timeout happened.", transactionId);
|
|
2813
3103
|
}
|
|
2814
3104
|
#startTransactionTimeout(transactionId, timeout) {
|
|
2815
3105
|
const timeoutStartedAt = Date.now();
|
|
2816
3106
|
const timer = createTimeoutIfDefined(async () => {
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
return;
|
|
2822
|
-
}
|
|
2823
|
-
await this.#runSerialized(tx, async () => {
|
|
2824
|
-
const current = this.transactions.get(transactionId);
|
|
2825
|
-
if (current && ["running", "waiting"].includes(current.status)) {
|
|
2826
|
-
await this.#closeTransaction(current, "timed_out");
|
|
2827
|
-
} else {
|
|
3107
|
+
try {
|
|
3108
|
+
debug2("Transaction timed out.", { transactionId, timeoutStartedAt, timeout });
|
|
3109
|
+
const tx = this.transactions.get(transactionId);
|
|
3110
|
+
if (!tx) {
|
|
2828
3111
|
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
3112
|
+
return;
|
|
2829
3113
|
}
|
|
2830
|
-
|
|
3114
|
+
await this.#runSerialized(tx, async () => {
|
|
3115
|
+
const current = this.transactions.get(transactionId);
|
|
3116
|
+
if (current && ["running", "waiting"].includes(current.status)) {
|
|
3117
|
+
await this.#closeTransaction(current, "timed_out");
|
|
3118
|
+
} else {
|
|
3119
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
3120
|
+
}
|
|
3121
|
+
});
|
|
3122
|
+
} catch (error) {
|
|
3123
|
+
debug2("Error while closing timed-out transaction.", { transactionId, error });
|
|
3124
|
+
}
|
|
2831
3125
|
}, timeout);
|
|
2832
3126
|
timer?.unref?.();
|
|
2833
3127
|
return timer;
|
|
@@ -2859,7 +3153,7 @@ var TransactionManager = class {
|
|
|
2859
3153
|
}
|
|
2860
3154
|
async #closeTransaction(tx, status) {
|
|
2861
3155
|
const createClosingPromise = async () => {
|
|
2862
|
-
|
|
3156
|
+
debug2("Closing transaction.", { transactionId: tx.id, status });
|
|
2863
3157
|
try {
|
|
2864
3158
|
if (tx.transaction && status === "committed") {
|
|
2865
3159
|
if (tx.transaction.options.usePhantomQuery) {
|