@prisma/client-engine-runtime 7.10.0-dev.3 → 7.10.0-dev.31
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 +384 -80
- package/dist/index.mjs +384 -80
- 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 +5 -0
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -145,6 +145,16 @@ function safeJsonStringify(obj) {
|
|
|
145
145
|
return val;
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
|
+
var MAX_PUSH_SPREAD_ARGS = 8192;
|
|
149
|
+
function appendToArray(target, source) {
|
|
150
|
+
if (source.length <= MAX_PUSH_SPREAD_ARGS) {
|
|
151
|
+
target.push(...source);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
for (let i = 0; i < source.length; i += MAX_PUSH_SPREAD_ARGS) {
|
|
155
|
+
target.push(...source.slice(i, i + MAX_PUSH_SPREAD_ARGS));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
148
158
|
|
|
149
159
|
// src/json-protocol.ts
|
|
150
160
|
function normalizeJsonProtocolValues(result) {
|
|
@@ -226,10 +236,8 @@ function deserializeTaggedValue({ $type, value }) {
|
|
|
226
236
|
switch ($type) {
|
|
227
237
|
case "BigInt":
|
|
228
238
|
return BigInt(value);
|
|
229
|
-
case "Bytes":
|
|
230
|
-
|
|
231
|
-
return new Uint8Array(buffer, byteOffset, byteLength);
|
|
232
|
-
}
|
|
239
|
+
case "Bytes":
|
|
240
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
233
241
|
case "DateTime":
|
|
234
242
|
return new Date(value);
|
|
235
243
|
case "Decimal":
|
|
@@ -343,6 +351,7 @@ function getErrorCode(err) {
|
|
|
343
351
|
case "UniqueConstraintViolation":
|
|
344
352
|
return "P2002";
|
|
345
353
|
case "ForeignKeyConstraintViolation":
|
|
354
|
+
case "RestrictViolation":
|
|
346
355
|
return "P2003";
|
|
347
356
|
case "InvalidInputValue":
|
|
348
357
|
return "P2007";
|
|
@@ -415,6 +424,7 @@ function renderErrorMessage(err) {
|
|
|
415
424
|
case "UniqueConstraintViolation":
|
|
416
425
|
return `Unique constraint failed on the ${renderConstraint(err.cause.constraint)}`;
|
|
417
426
|
case "ForeignKeyConstraintViolation":
|
|
427
|
+
case "RestrictViolation":
|
|
418
428
|
return `Foreign key constraint violated on the ${renderConstraint(err.cause.constraint)}`;
|
|
419
429
|
case "UnsupportedNativeDataType":
|
|
420
430
|
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\`.`;
|
|
@@ -1148,8 +1158,9 @@ function renderTemplateSql(fragments, placeholderFormat, params, argTypes) {
|
|
|
1148
1158
|
if (fragment.type === "stringChunk") {
|
|
1149
1159
|
continue;
|
|
1150
1160
|
}
|
|
1151
|
-
const
|
|
1152
|
-
const added =
|
|
1161
|
+
const fragmentParams = Array.from(flattenedFragmentParams(fragment));
|
|
1162
|
+
const added = fragmentParams.length;
|
|
1163
|
+
appendToArray(flattenedParams, fragmentParams);
|
|
1153
1164
|
if (fragment.argType.arity === "tuple") {
|
|
1154
1165
|
if (added % fragment.argType.elements.length !== 0) {
|
|
1155
1166
|
throw new Error(
|
|
@@ -1612,17 +1623,27 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1612
1623
|
});
|
|
1613
1624
|
}
|
|
1614
1625
|
async run(queryPlan, options) {
|
|
1615
|
-
const
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1626
|
+
const generators = this.#generators.snapshot();
|
|
1627
|
+
const context = { ...options, generators };
|
|
1628
|
+
const purified = purifyQueryPlan(queryPlan, (node) => this.interpretNode(node, context))?.catch(
|
|
1629
|
+
(e) => rethrowAsUserFacing(e)
|
|
1630
|
+
);
|
|
1631
|
+
if (purified) {
|
|
1632
|
+
try {
|
|
1633
|
+
return this.#interpretPureNode(await purified, context.scope, generators).value;
|
|
1634
|
+
} catch (e) {
|
|
1635
|
+
rethrowAsUserFacing(e);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const { value } = await this.interpretNode(queryPlan, context).catch((e) => rethrowAsUserFacing(e));
|
|
1619
1639
|
return value;
|
|
1620
1640
|
}
|
|
1621
1641
|
async interpretNode(node, context) {
|
|
1622
1642
|
switch (node.type) {
|
|
1623
1643
|
case "value": {
|
|
1624
1644
|
return {
|
|
1625
|
-
value: evaluateArg(node.args, context.scope, context.generators)
|
|
1645
|
+
value: evaluateArg(node.args, context.scope, context.generators),
|
|
1646
|
+
lastInsertId: node.lastInsertId
|
|
1626
1647
|
};
|
|
1627
1648
|
}
|
|
1628
1649
|
case "seq": {
|
|
@@ -1632,9 +1653,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1632
1653
|
}
|
|
1633
1654
|
return result ?? { value: void 0 };
|
|
1634
1655
|
}
|
|
1635
|
-
case "get": {
|
|
1636
|
-
return { value: context.scope[node.args.name] };
|
|
1637
|
-
}
|
|
1638
1656
|
case "let": {
|
|
1639
1657
|
const nestedScope = Object.create(context.scope);
|
|
1640
1658
|
for (const binding of node.args.bindings) {
|
|
@@ -1643,15 +1661,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1643
1661
|
}
|
|
1644
1662
|
return this.interpretNode(node.args.expr, { ...context, scope: nestedScope });
|
|
1645
1663
|
}
|
|
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
1664
|
case "concat": {
|
|
1656
1665
|
const parts = await Promise.all(
|
|
1657
1666
|
node.args.map((arg) => this.interpretNode(arg, context).then((res) => res.value))
|
|
@@ -1698,7 +1707,7 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1698
1707
|
if (results === void 0) {
|
|
1699
1708
|
results = result;
|
|
1700
1709
|
} else {
|
|
1701
|
-
results.rows
|
|
1710
|
+
appendToArray(results.rows, result.rows);
|
|
1702
1711
|
results.lastInsertId = result.lastInsertId;
|
|
1703
1712
|
}
|
|
1704
1713
|
}
|
|
@@ -1778,9 +1787,6 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1778
1787
|
return await this.interpretNode(node.args.else, context);
|
|
1779
1788
|
}
|
|
1780
1789
|
}
|
|
1781
|
-
case "unit": {
|
|
1782
|
-
return { value: void 0 };
|
|
1783
|
-
}
|
|
1784
1790
|
case "diff": {
|
|
1785
1791
|
const { value: from } = await this.interpretNode(node.args.from, context);
|
|
1786
1792
|
const { value: to } = await this.interpretNode(node.args.to, context);
|
|
@@ -1810,6 +1816,139 @@ var QueryInterpreter = class _QueryInterpreter {
|
|
|
1810
1816
|
}
|
|
1811
1817
|
return { value: record, lastInsertId };
|
|
1812
1818
|
}
|
|
1819
|
+
default:
|
|
1820
|
+
return this.#interpretPureNode(node, context.scope, context.generators);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
#interpretPureNode(node, scope, generators) {
|
|
1824
|
+
switch (node.type) {
|
|
1825
|
+
case "value": {
|
|
1826
|
+
return { value: evaluateArg(node.args, scope, generators), lastInsertId: node.lastInsertId };
|
|
1827
|
+
}
|
|
1828
|
+
case "seq": {
|
|
1829
|
+
let result;
|
|
1830
|
+
for (const arg of node.args) {
|
|
1831
|
+
result = this.#interpretPureNode(arg, scope, generators);
|
|
1832
|
+
}
|
|
1833
|
+
return result ?? { value: void 0 };
|
|
1834
|
+
}
|
|
1835
|
+
case "get": {
|
|
1836
|
+
return { value: scope[node.args.name] };
|
|
1837
|
+
}
|
|
1838
|
+
case "let": {
|
|
1839
|
+
const nestedScope = Object.create(scope);
|
|
1840
|
+
for (const binding of node.args.bindings) {
|
|
1841
|
+
const { value } = this.#interpretPureNode(binding.expr, nestedScope, generators);
|
|
1842
|
+
nestedScope[binding.name] = value;
|
|
1843
|
+
}
|
|
1844
|
+
return this.#interpretPureNode(node.args.expr, nestedScope, generators);
|
|
1845
|
+
}
|
|
1846
|
+
case "getFirstNonEmpty": {
|
|
1847
|
+
for (const name of node.args.names) {
|
|
1848
|
+
const value = scope[name];
|
|
1849
|
+
if (!isEmpty(value)) {
|
|
1850
|
+
return { value };
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
return { value: [] };
|
|
1854
|
+
}
|
|
1855
|
+
case "concat": {
|
|
1856
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1857
|
+
return {
|
|
1858
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : []
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
case "sum": {
|
|
1862
|
+
const parts = node.args.map((arg) => this.#interpretPureNode(arg, scope, generators).value);
|
|
1863
|
+
return {
|
|
1864
|
+
value: parts.length > 0 ? parts.reduce((acc, part) => asNumber2(acc) + asNumber2(part)) : 0
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
case "reverse": {
|
|
1868
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1869
|
+
return { value: Array.isArray(value) ? value.reverse() : value, lastInsertId };
|
|
1870
|
+
}
|
|
1871
|
+
case "unique": {
|
|
1872
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1873
|
+
if (!Array.isArray(value)) {
|
|
1874
|
+
return { value, lastInsertId };
|
|
1875
|
+
}
|
|
1876
|
+
if (value.length > 1) {
|
|
1877
|
+
throw new Error(`Expected zero or one element, got ${value.length}`);
|
|
1878
|
+
}
|
|
1879
|
+
return { value: value[0] ?? null, lastInsertId };
|
|
1880
|
+
}
|
|
1881
|
+
case "required": {
|
|
1882
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args, scope, generators);
|
|
1883
|
+
if (isEmpty(value)) {
|
|
1884
|
+
throw new Error("Required value is empty");
|
|
1885
|
+
}
|
|
1886
|
+
return { value, lastInsertId };
|
|
1887
|
+
}
|
|
1888
|
+
case "mapField": {
|
|
1889
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.records, scope, generators);
|
|
1890
|
+
return { value: mapField2(value, node.args.field), lastInsertId };
|
|
1891
|
+
}
|
|
1892
|
+
case "join": {
|
|
1893
|
+
const { value: parent, lastInsertId } = this.#interpretPureNode(node.args.parent, scope, generators);
|
|
1894
|
+
if (parent === null) {
|
|
1895
|
+
return { value: null, lastInsertId };
|
|
1896
|
+
}
|
|
1897
|
+
const children = node.args.children.map((joinExpr) => ({
|
|
1898
|
+
joinExpr,
|
|
1899
|
+
childRecords: this.#interpretPureNode(joinExpr.child, scope, generators).value
|
|
1900
|
+
}));
|
|
1901
|
+
return { value: attachChildrenToParents(parent, children, node.args.canAssumeStrictEquality), lastInsertId };
|
|
1902
|
+
}
|
|
1903
|
+
case "dataMap": {
|
|
1904
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1905
|
+
return { value: applyDataMap(value, node.args.structure, node.args.enums), lastInsertId };
|
|
1906
|
+
}
|
|
1907
|
+
case "validate": {
|
|
1908
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1909
|
+
performValidation(value, node.args.rules, node.args);
|
|
1910
|
+
return { value, lastInsertId };
|
|
1911
|
+
}
|
|
1912
|
+
case "if": {
|
|
1913
|
+
const { value } = this.#interpretPureNode(node.args.value, scope, generators);
|
|
1914
|
+
if (doesSatisfyRule(value, node.args.rule)) {
|
|
1915
|
+
return this.#interpretPureNode(node.args.then, scope, generators);
|
|
1916
|
+
} else {
|
|
1917
|
+
return this.#interpretPureNode(node.args.else, scope, generators);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
case "unit": {
|
|
1921
|
+
return { value: void 0 };
|
|
1922
|
+
}
|
|
1923
|
+
case "diff": {
|
|
1924
|
+
const { value: from } = this.#interpretPureNode(node.args.from, scope, generators);
|
|
1925
|
+
const { value: to } = this.#interpretPureNode(node.args.to, scope, generators);
|
|
1926
|
+
const keyGetter = (item) => item !== null ? getRecordKey(asRecord(item), node.args.fields) : null;
|
|
1927
|
+
const toSet = new Set(asList(to).map(keyGetter));
|
|
1928
|
+
return { value: asList(from).filter((item) => !toSet.has(keyGetter(item))) };
|
|
1929
|
+
}
|
|
1930
|
+
case "process": {
|
|
1931
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1932
|
+
const ops = cloneObject(node.args.operations);
|
|
1933
|
+
evaluateProcessingParameters(ops, scope, generators);
|
|
1934
|
+
return { value: processRecords(value, ops), lastInsertId };
|
|
1935
|
+
}
|
|
1936
|
+
case "initializeRecord": {
|
|
1937
|
+
const { lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1938
|
+
const record = {};
|
|
1939
|
+
for (const [key, initializer] of Object.entries(node.args.fields)) {
|
|
1940
|
+
record[key] = evalFieldInitializer(initializer, lastInsertId, scope, generators);
|
|
1941
|
+
}
|
|
1942
|
+
return { value: record, lastInsertId };
|
|
1943
|
+
}
|
|
1944
|
+
case "mapRecord": {
|
|
1945
|
+
const { value, lastInsertId } = this.#interpretPureNode(node.args.expr, scope, generators);
|
|
1946
|
+
const record = value === null ? {} : asRecord(value);
|
|
1947
|
+
for (const [key, entry] of Object.entries(node.args.fields)) {
|
|
1948
|
+
record[key] = evalFieldOperation(entry, record[key], scope, generators);
|
|
1949
|
+
}
|
|
1950
|
+
return { value: record, lastInsertId };
|
|
1951
|
+
}
|
|
1813
1952
|
default:
|
|
1814
1953
|
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
1815
1954
|
}
|
|
@@ -1987,6 +2126,112 @@ function evalFieldOperation(op, value, scope, generators) {
|
|
|
1987
2126
|
assertNever(op, `Unexpected field operation type: ${op["type"]}`);
|
|
1988
2127
|
}
|
|
1989
2128
|
}
|
|
2129
|
+
function purifyQueryPlan(node, evalNode) {
|
|
2130
|
+
const impureNode = findUniqueUnconditionalImpureNode(node);
|
|
2131
|
+
if (!impureNode) {
|
|
2132
|
+
return void 0;
|
|
2133
|
+
}
|
|
2134
|
+
return evalNode(impureNode).then((result) => {
|
|
2135
|
+
const evaluated = {
|
|
2136
|
+
type: "value",
|
|
2137
|
+
args: result.value,
|
|
2138
|
+
lastInsertId: result.lastInsertId
|
|
2139
|
+
};
|
|
2140
|
+
const purified = replaceImpureNode(node, impureNode, evaluated);
|
|
2141
|
+
if (!purified) {
|
|
2142
|
+
throw new Error("Could not substitute the evaluated impure node into the query plan");
|
|
2143
|
+
}
|
|
2144
|
+
return purified;
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
function replaceImpureNode(node, target, replacement) {
|
|
2148
|
+
if (node === target) {
|
|
2149
|
+
return replacement;
|
|
2150
|
+
}
|
|
2151
|
+
switch (node.type) {
|
|
2152
|
+
case "seq":
|
|
2153
|
+
case "sum":
|
|
2154
|
+
case "concat": {
|
|
2155
|
+
for (let i = 0; i < node.args.length; i++) {
|
|
2156
|
+
const child = replaceImpureNode(node.args[i], target, replacement);
|
|
2157
|
+
if (child) {
|
|
2158
|
+
return { ...node, args: node.args.map((arg, j) => j === i ? child : arg) };
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
return void 0;
|
|
2162
|
+
}
|
|
2163
|
+
case "dataMap":
|
|
2164
|
+
case "validate":
|
|
2165
|
+
case "initializeRecord":
|
|
2166
|
+
case "mapRecord":
|
|
2167
|
+
case "process": {
|
|
2168
|
+
const expr = replaceImpureNode(node.args.expr, target, replacement);
|
|
2169
|
+
return expr && { ...node, args: { ...node.args, expr } };
|
|
2170
|
+
}
|
|
2171
|
+
case "mapField": {
|
|
2172
|
+
const records = replaceImpureNode(node.args.records, target, replacement);
|
|
2173
|
+
return records && { ...node, args: { ...node.args, records } };
|
|
2174
|
+
}
|
|
2175
|
+
case "reverse":
|
|
2176
|
+
case "unique":
|
|
2177
|
+
case "required": {
|
|
2178
|
+
const args = replaceImpureNode(node.args, target, replacement);
|
|
2179
|
+
return args && { ...node, args };
|
|
2180
|
+
}
|
|
2181
|
+
default:
|
|
2182
|
+
return void 0;
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
function findUniqueUnconditionalImpureNode(node) {
|
|
2186
|
+
switch (node.type) {
|
|
2187
|
+
case "query":
|
|
2188
|
+
case "execute":
|
|
2189
|
+
return node;
|
|
2190
|
+
case "seq":
|
|
2191
|
+
case "sum":
|
|
2192
|
+
case "concat": {
|
|
2193
|
+
let found = void 0;
|
|
2194
|
+
for (const child of node.args) {
|
|
2195
|
+
const childFound = findUniqueUnconditionalImpureNode(child);
|
|
2196
|
+
if (childFound === null) {
|
|
2197
|
+
return null;
|
|
2198
|
+
}
|
|
2199
|
+
if (childFound) {
|
|
2200
|
+
if (found) {
|
|
2201
|
+
return null;
|
|
2202
|
+
}
|
|
2203
|
+
found = childFound;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
return found;
|
|
2207
|
+
}
|
|
2208
|
+
case "dataMap":
|
|
2209
|
+
case "validate":
|
|
2210
|
+
case "initializeRecord":
|
|
2211
|
+
case "mapRecord":
|
|
2212
|
+
case "process":
|
|
2213
|
+
return findUniqueUnconditionalImpureNode(node.args.expr);
|
|
2214
|
+
case "mapField":
|
|
2215
|
+
return findUniqueUnconditionalImpureNode(node.args.records);
|
|
2216
|
+
case "reverse":
|
|
2217
|
+
case "unique":
|
|
2218
|
+
case "required":
|
|
2219
|
+
return findUniqueUnconditionalImpureNode(node.args);
|
|
2220
|
+
case "let":
|
|
2221
|
+
case "join":
|
|
2222
|
+
case "diff":
|
|
2223
|
+
case "if":
|
|
2224
|
+
case "transaction":
|
|
2225
|
+
return null;
|
|
2226
|
+
case "value":
|
|
2227
|
+
case "get":
|
|
2228
|
+
case "getFirstNonEmpty":
|
|
2229
|
+
case "unit":
|
|
2230
|
+
return void 0;
|
|
2231
|
+
default:
|
|
2232
|
+
assertNever(node, `Unexpected node type: ${node.type}`);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
1990
2235
|
function applyComments(query, sqlCommenter) {
|
|
1991
2236
|
if (!sqlCommenter || sqlCommenter.plugins.length === 0) {
|
|
1992
2237
|
return query;
|
|
@@ -2553,6 +2798,14 @@ var InvalidTransactionIsolationLevelError = class extends TransactionManagerErro
|
|
|
2553
2798
|
|
|
2554
2799
|
// src/transaction-manager/transaction-manager.ts
|
|
2555
2800
|
var MAX_CLOSED_TRANSACTIONS = 100;
|
|
2801
|
+
var CANCEL_ROLLBACK_GRACE_MS = 2e3;
|
|
2802
|
+
function trackStartingTransaction() {
|
|
2803
|
+
let markSettled;
|
|
2804
|
+
const settled = new Promise((resolve) => {
|
|
2805
|
+
markSettled = resolve;
|
|
2806
|
+
});
|
|
2807
|
+
return { abortController: new AbortController(), settled, markSettled };
|
|
2808
|
+
}
|
|
2556
2809
|
var debug = (0, import_debug.Debug)("prisma:client:transactionManager");
|
|
2557
2810
|
var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
|
|
2558
2811
|
var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
|
|
@@ -2572,6 +2825,9 @@ var TransactionManager = class {
|
|
|
2572
2825
|
// List of last closed transactions. Max MAX_CLOSED_TRANSACTIONS entries.
|
|
2573
2826
|
// Used to provide better error messages than a generic "transaction not found".
|
|
2574
2827
|
closedTransactions = [];
|
|
2828
|
+
// Transactions that are still being started. Tracked separately so that
|
|
2829
|
+
// `cancelAllTransactions` can reach them: they are not in `transactions` yet.
|
|
2830
|
+
#startingTransactions = /* @__PURE__ */ new Set();
|
|
2575
2831
|
driverAdapter;
|
|
2576
2832
|
transactionOptions;
|
|
2577
2833
|
tracingHelper;
|
|
@@ -2635,56 +2891,91 @@ var TransactionManager = class {
|
|
|
2635
2891
|
return { id: existing.id };
|
|
2636
2892
|
});
|
|
2637
2893
|
}
|
|
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
|
-
|
|
2894
|
+
const starting = trackStartingTransaction();
|
|
2895
|
+
const { abortController } = starting;
|
|
2896
|
+
this.#startingTransactions.add(starting);
|
|
2897
|
+
let discarding;
|
|
2898
|
+
try {
|
|
2899
|
+
const transaction = {
|
|
2900
|
+
id: await randomUUID(),
|
|
2901
|
+
status: "waiting",
|
|
2902
|
+
timer: void 0,
|
|
2903
|
+
timeout: options.timeout,
|
|
2904
|
+
startedAt: Date.now(),
|
|
2905
|
+
transaction: void 0,
|
|
2906
|
+
operationQueue: Promise.resolve(),
|
|
2907
|
+
depth: 1,
|
|
2908
|
+
savepoints: [],
|
|
2909
|
+
savepointCounter: 0
|
|
2910
|
+
};
|
|
2911
|
+
if (abortController.signal.aborted) {
|
|
2912
|
+
throw new TransactionStartTimeoutError();
|
|
2913
|
+
}
|
|
2914
|
+
const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
|
|
2915
|
+
startTimer?.unref?.();
|
|
2916
|
+
const startTransactionPromise = this.driverAdapter.startTransaction(options.isolationLevel).catch(rethrowAsUserFacing);
|
|
2917
|
+
transaction.transaction = await Promise.race([
|
|
2918
|
+
startTransactionPromise.finally(() => clearTimeout(startTimer)),
|
|
2919
|
+
once(abortController.signal, "abort").then(() => void 0)
|
|
2920
|
+
]);
|
|
2921
|
+
this.transactions.set(transaction.id, transaction);
|
|
2922
|
+
switch (transaction.status) {
|
|
2923
|
+
case "waiting":
|
|
2924
|
+
if (abortController.signal.aborted) {
|
|
2925
|
+
transaction.transaction = void 0;
|
|
2926
|
+
discarding = this.#discardStartedTransaction(startTransactionPromise);
|
|
2927
|
+
await this.#closeTransaction(transaction, "timed_out");
|
|
2928
|
+
throw new TransactionStartTimeoutError();
|
|
2929
|
+
}
|
|
2930
|
+
transaction.status = "running";
|
|
2931
|
+
transaction.startedAt = Date.now();
|
|
2932
|
+
transaction.timer = this.#startTransactionTimeout(transaction.id, options.timeout);
|
|
2933
|
+
return { id: transaction.id };
|
|
2934
|
+
case "timed_out":
|
|
2935
|
+
case "running":
|
|
2936
|
+
case "committed":
|
|
2937
|
+
case "rolled_back":
|
|
2938
|
+
throw new TransactionInternalConsistencyError(
|
|
2939
|
+
`Transaction in invalid state ${transaction.status} although it just finished startup.`
|
|
2940
|
+
);
|
|
2941
|
+
default:
|
|
2942
|
+
return assertNever(transaction["status"], "Unknown transaction status.");
|
|
2943
|
+
}
|
|
2944
|
+
} finally {
|
|
2945
|
+
this.#startingTransactions.delete(starting);
|
|
2946
|
+
if (discarding) {
|
|
2947
|
+
void discarding.finally(starting.markSettled);
|
|
2948
|
+
} else {
|
|
2949
|
+
starting.markSettled();
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
/**
|
|
2954
|
+
* Rolls back a transaction whose start was abandoned, and releases its connection.
|
|
2955
|
+
*
|
|
2956
|
+
* The `startTransaction` promise may still be running in the background. If it eventually
|
|
2957
|
+
* succeeds, we need to roll back and release the connection to avoid leaking it and
|
|
2958
|
+
* exhausting the connection pool. For adapters that don't use phantom queries (e.g. pg/neon),
|
|
2959
|
+
* `rollback()` only releases the connection without sending SQL, so we send an explicit
|
|
2960
|
+
* ROLLBACK first; otherwise the connection returns to the pool mid-transaction because
|
|
2961
|
+
* `BEGIN` already ran on the wire during startup.
|
|
2962
|
+
*
|
|
2963
|
+
* Errors are only logged: the caller has already reported the failure that led here.
|
|
2964
|
+
*/
|
|
2965
|
+
async #discardStartedTransaction(startTransactionPromise) {
|
|
2966
|
+
try {
|
|
2967
|
+
const tx = await startTransactionPromise;
|
|
2968
|
+
if (tx.options.usePhantomQuery) {
|
|
2969
|
+
await tx.rollback();
|
|
2970
|
+
} else {
|
|
2971
|
+
try {
|
|
2972
|
+
await tx.executeRaw(ROLLBACK_QUERY());
|
|
2973
|
+
} finally {
|
|
2974
|
+
await tx.rollback();
|
|
2675
2975
|
}
|
|
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.");
|
|
2976
|
+
}
|
|
2977
|
+
} catch (e) {
|
|
2978
|
+
debug("error in discarded transaction:", e);
|
|
2688
2979
|
}
|
|
2689
2980
|
}
|
|
2690
2981
|
async commitTransaction(transactionId) {
|
|
@@ -2775,16 +3066,21 @@ var TransactionManager = class {
|
|
|
2775
3066
|
return transaction;
|
|
2776
3067
|
}
|
|
2777
3068
|
async cancelAllTransactions() {
|
|
2778
|
-
|
|
2779
|
-
|
|
3069
|
+
const starting = [...this.#startingTransactions];
|
|
3070
|
+
for (const { abortController } of starting) {
|
|
3071
|
+
abortController.abort();
|
|
3072
|
+
}
|
|
3073
|
+
await Promise.allSettled([
|
|
3074
|
+
...[...this.transactions.values()].map(
|
|
2780
3075
|
(tx) => this.#runSerialized(tx, async () => {
|
|
2781
3076
|
const current = this.transactions.get(tx.id);
|
|
2782
3077
|
if (current) {
|
|
2783
3078
|
await this.#closeTransaction(current, "rolled_back");
|
|
2784
3079
|
}
|
|
2785
3080
|
})
|
|
2786
|
-
)
|
|
2787
|
-
|
|
3081
|
+
),
|
|
3082
|
+
...starting.map(({ settled }) => settleWithin(settled, CANCEL_ROLLBACK_GRACE_MS))
|
|
3083
|
+
]);
|
|
2788
3084
|
}
|
|
2789
3085
|
#nextSavepointName(transaction) {
|
|
2790
3086
|
return `prisma_sp_${transaction.savepointCounter++}`;
|
|
@@ -2937,6 +3233,14 @@ var TransactionManager = class {
|
|
|
2937
3233
|
function createTimeoutIfDefined(cb, ms) {
|
|
2938
3234
|
return ms !== void 0 ? setTimeout(cb, ms) : void 0;
|
|
2939
3235
|
}
|
|
3236
|
+
function settleWithin(promise, timeout) {
|
|
3237
|
+
let timer;
|
|
3238
|
+
const deadline = new Promise((resolve) => {
|
|
3239
|
+
timer = setTimeout(resolve, timeout);
|
|
3240
|
+
timer?.unref?.();
|
|
3241
|
+
});
|
|
3242
|
+
return Promise.race([promise, deadline]).finally(() => clearTimeout(timer));
|
|
3243
|
+
}
|
|
2940
3244
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2941
3245
|
0 && (module.exports = {
|
|
2942
3246
|
DataMapperError,
|