@prisma/client-engine-runtime 7.5.0-dev.2 → 7.5.0-dev.21
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 +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +169 -19
- package/dist/index.mjs +168 -18
- package/dist/json-protocol.d.ts +6 -2
- package/dist/query-plan.d.ts +3 -0
- package/dist/transaction-manager/transaction.d.ts +1 -0
- package/package.json +7 -10
package/dist/index.d.mts
CHANGED
|
@@ -80,7 +80,7 @@ export declare type DecimalTaggedValue = {
|
|
|
80
80
|
value: string;
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
export declare function
|
|
83
|
+
export declare function deserializeJsonObject(result: unknown): unknown;
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Checks if two objects representing the names and values of key columns match. A match is
|
|
@@ -155,6 +155,9 @@ export declare type Fragment = {
|
|
|
155
155
|
type: 'parameter';
|
|
156
156
|
} | {
|
|
157
157
|
type: 'parameterTuple';
|
|
158
|
+
itemPrefix: string;
|
|
159
|
+
itemSeparator: string;
|
|
160
|
+
itemSuffix: string;
|
|
158
161
|
} | {
|
|
159
162
|
type: 'parameterTupleList';
|
|
160
163
|
itemPrefix: string;
|
|
@@ -187,7 +190,7 @@ export declare type JoinExpression = {
|
|
|
187
190
|
isRelationUnique: boolean;
|
|
188
191
|
};
|
|
189
192
|
|
|
190
|
-
export declare type JsonInputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | FieldRefTaggedValue | JsonTaggedValue | EnumTaggedValue;
|
|
193
|
+
export declare type JsonInputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | FieldRefTaggedValue | JsonTaggedValue | EnumTaggedValue | RawTaggedValue;
|
|
191
194
|
|
|
192
195
|
export declare type JsonOutputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | JsonTaggedValue;
|
|
193
196
|
|
|
@@ -421,6 +424,11 @@ export declare type RawResponse = {
|
|
|
421
424
|
rows: unknown[][];
|
|
422
425
|
};
|
|
423
426
|
|
|
427
|
+
export declare type RawTaggedValue = {
|
|
428
|
+
$type: 'Raw';
|
|
429
|
+
value: unknown;
|
|
430
|
+
};
|
|
431
|
+
|
|
424
432
|
export declare type ResultNode = {
|
|
425
433
|
type: 'affectedRows';
|
|
426
434
|
} | {
|
|
@@ -492,6 +500,7 @@ export declare type TransactionOptions = {
|
|
|
492
500
|
maxWait?: number;
|
|
493
501
|
timeout?: number;
|
|
494
502
|
isolationLevel?: IsolationLevel;
|
|
503
|
+
newTxId?: string;
|
|
495
504
|
};
|
|
496
505
|
|
|
497
506
|
export declare class UserFacingError extends Error {
|
package/dist/index.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export declare type DecimalTaggedValue = {
|
|
|
80
80
|
value: string;
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
export declare function
|
|
83
|
+
export declare function deserializeJsonObject(result: unknown): unknown;
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Checks if two objects representing the names and values of key columns match. A match is
|
|
@@ -155,6 +155,9 @@ export declare type Fragment = {
|
|
|
155
155
|
type: 'parameter';
|
|
156
156
|
} | {
|
|
157
157
|
type: 'parameterTuple';
|
|
158
|
+
itemPrefix: string;
|
|
159
|
+
itemSeparator: string;
|
|
160
|
+
itemSuffix: string;
|
|
158
161
|
} | {
|
|
159
162
|
type: 'parameterTupleList';
|
|
160
163
|
itemPrefix: string;
|
|
@@ -187,7 +190,7 @@ export declare type JoinExpression = {
|
|
|
187
190
|
isRelationUnique: boolean;
|
|
188
191
|
};
|
|
189
192
|
|
|
190
|
-
export declare type JsonInputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | FieldRefTaggedValue | JsonTaggedValue | EnumTaggedValue;
|
|
193
|
+
export declare type JsonInputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | FieldRefTaggedValue | JsonTaggedValue | EnumTaggedValue | RawTaggedValue;
|
|
191
194
|
|
|
192
195
|
export declare type JsonOutputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | JsonTaggedValue;
|
|
193
196
|
|
|
@@ -421,6 +424,11 @@ export declare type RawResponse = {
|
|
|
421
424
|
rows: unknown[][];
|
|
422
425
|
};
|
|
423
426
|
|
|
427
|
+
export declare type RawTaggedValue = {
|
|
428
|
+
$type: 'Raw';
|
|
429
|
+
value: unknown;
|
|
430
|
+
};
|
|
431
|
+
|
|
424
432
|
export declare type ResultNode = {
|
|
425
433
|
type: 'affectedRows';
|
|
426
434
|
} | {
|
|
@@ -492,6 +500,7 @@ export declare type TransactionOptions = {
|
|
|
492
500
|
maxWait?: number;
|
|
493
501
|
timeout?: number;
|
|
494
502
|
isolationLevel?: IsolationLevel;
|
|
503
|
+
newTxId?: string;
|
|
495
504
|
};
|
|
496
505
|
|
|
497
506
|
export declare class UserFacingError extends Error {
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
UserFacingError: () => UserFacingError,
|
|
38
38
|
applySqlCommenters: () => applySqlCommenters,
|
|
39
39
|
convertCompactedRows: () => convertCompactedRows,
|
|
40
|
-
|
|
40
|
+
deserializeJsonObject: () => deserializeJsonObject,
|
|
41
41
|
doKeysMatch: () => doKeysMatch,
|
|
42
42
|
isDeepStrictEqual: () => isDeepStrictEqual,
|
|
43
43
|
isPrismaValueGenerator: () => isPrismaValueGenerator,
|
|
@@ -170,7 +170,10 @@ function normalizeJsonProtocolValues(result) {
|
|
|
170
170
|
function isTaggedValue(value) {
|
|
171
171
|
return value !== null && typeof value == "object" && typeof value["$type"] === "string";
|
|
172
172
|
}
|
|
173
|
-
function normalizeTaggedValue({
|
|
173
|
+
function normalizeTaggedValue({
|
|
174
|
+
$type,
|
|
175
|
+
value
|
|
176
|
+
}) {
|
|
174
177
|
switch ($type) {
|
|
175
178
|
case "BigInt":
|
|
176
179
|
return { $type, value: String(value) };
|
|
@@ -182,6 +185,12 @@ function normalizeTaggedValue({ $type, value }) {
|
|
|
182
185
|
return { $type, value: String(new import_client_runtime_utils2.Decimal(value)) };
|
|
183
186
|
case "Json":
|
|
184
187
|
return { $type, value: JSON.stringify(JSON.parse(value)) };
|
|
188
|
+
case "Raw":
|
|
189
|
+
return { $type, value };
|
|
190
|
+
case "FieldRef":
|
|
191
|
+
return { $type, value };
|
|
192
|
+
case "Enum":
|
|
193
|
+
return { $type, value };
|
|
185
194
|
default:
|
|
186
195
|
assertNever(value, "Unknown tagged value");
|
|
187
196
|
}
|
|
@@ -193,12 +202,12 @@ function mapObjectValues(object, mapper) {
|
|
|
193
202
|
}
|
|
194
203
|
return result;
|
|
195
204
|
}
|
|
196
|
-
function
|
|
205
|
+
function deserializeJsonObject(result) {
|
|
197
206
|
if (result === null) {
|
|
198
207
|
return result;
|
|
199
208
|
}
|
|
200
209
|
if (Array.isArray(result)) {
|
|
201
|
-
return result.map(
|
|
210
|
+
return result.map(deserializeJsonObject);
|
|
202
211
|
}
|
|
203
212
|
if (typeof result === "object") {
|
|
204
213
|
if (isTaggedValue(result)) {
|
|
@@ -207,7 +216,7 @@ function deserializeJsonResponse(result) {
|
|
|
207
216
|
if (result.constructor !== null && result.constructor.name !== "Object") {
|
|
208
217
|
return result;
|
|
209
218
|
}
|
|
210
|
-
return mapObjectValues(result,
|
|
219
|
+
return mapObjectValues(result, deserializeJsonObject);
|
|
211
220
|
}
|
|
212
221
|
return result;
|
|
213
222
|
}
|
|
@@ -225,6 +234,12 @@ function deserializeTaggedValue({ $type, value }) {
|
|
|
225
234
|
return new import_client_runtime_utils2.Decimal(value);
|
|
226
235
|
case "Json":
|
|
227
236
|
return JSON.parse(value);
|
|
237
|
+
case "Raw":
|
|
238
|
+
return value;
|
|
239
|
+
case "FieldRef":
|
|
240
|
+
throw new Error("FieldRef tagged values cannot be deserialized to JavaScript values");
|
|
241
|
+
case "Enum":
|
|
242
|
+
return value;
|
|
228
243
|
default:
|
|
229
244
|
assertNever(value, "Unknown tagged value");
|
|
230
245
|
}
|
|
@@ -456,7 +471,7 @@ function resolveArgPlaceholders(args, placeholderValues) {
|
|
|
456
471
|
function convertCompactedRows(rows, compiledBatch, placeholderValues = {}) {
|
|
457
472
|
const keysPerRow = rows.map(
|
|
458
473
|
(item) => compiledBatch.keys.reduce((acc, key) => {
|
|
459
|
-
acc[key] =
|
|
474
|
+
acc[key] = deserializeJsonObject(item[key]);
|
|
460
475
|
return acc;
|
|
461
476
|
}, {})
|
|
462
477
|
);
|
|
@@ -1121,7 +1136,10 @@ function renderFragment(fragment, placeholderFormat, ctx) {
|
|
|
1121
1136
|
case "stringChunk":
|
|
1122
1137
|
return fragment.chunk;
|
|
1123
1138
|
case "parameterTuple": {
|
|
1124
|
-
const placeholders = fragment.value.length == 0 ? "NULL" : fragment.value.map(() =>
|
|
1139
|
+
const placeholders = fragment.value.length == 0 ? "NULL" : fragment.value.map(() => {
|
|
1140
|
+
const item = formatPlaceholder(placeholderFormat, ctx.placeholderNumber++);
|
|
1141
|
+
return `${fragment.itemPrefix}${item}${fragment.itemSuffix}`;
|
|
1142
|
+
}).join(fragment.itemSeparator);
|
|
1125
1143
|
return `(${placeholders})`;
|
|
1126
1144
|
}
|
|
1127
1145
|
case "parameterTupleList": {
|
|
@@ -2072,13 +2090,42 @@ var TransactionManager = class {
|
|
|
2072
2090
|
);
|
|
2073
2091
|
}
|
|
2074
2092
|
async #startTransactionImpl(options) {
|
|
2093
|
+
if (options.newTxId) {
|
|
2094
|
+
return await this.#withActiveTransactionLock(options.newTxId, "start", async (existing) => {
|
|
2095
|
+
if (existing.status !== "running") {
|
|
2096
|
+
throw new TransactionInternalConsistencyError(
|
|
2097
|
+
`Transaction in invalid state ${existing.status} when starting a nested transaction.`
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2100
|
+
if (!existing.transaction) {
|
|
2101
|
+
throw new TransactionInternalConsistencyError(
|
|
2102
|
+
`Transaction missing underlying driver transaction when starting a nested transaction.`
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2105
|
+
existing.depth += 1;
|
|
2106
|
+
const savepointName = this.#nextSavepointName(existing);
|
|
2107
|
+
existing.savepoints.push(savepointName);
|
|
2108
|
+
try {
|
|
2109
|
+
await this.#requiredCreateSavepoint(existing.transaction)(savepointName);
|
|
2110
|
+
} catch (e) {
|
|
2111
|
+
existing.depth -= 1;
|
|
2112
|
+
existing.savepoints.pop();
|
|
2113
|
+
throw e;
|
|
2114
|
+
}
|
|
2115
|
+
return { id: existing.id };
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2075
2118
|
const transaction = {
|
|
2076
2119
|
id: await randomUUID(),
|
|
2077
2120
|
status: "waiting",
|
|
2078
2121
|
timer: void 0,
|
|
2079
2122
|
timeout: options.timeout,
|
|
2080
2123
|
startedAt: Date.now(),
|
|
2081
|
-
transaction: void 0
|
|
2124
|
+
transaction: void 0,
|
|
2125
|
+
operationQueue: Promise.resolve(),
|
|
2126
|
+
depth: 1,
|
|
2127
|
+
savepoints: [],
|
|
2128
|
+
savepointCounter: 0
|
|
2082
2129
|
};
|
|
2083
2130
|
const abortController = new AbortController();
|
|
2084
2131
|
const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
|
|
@@ -2112,14 +2159,49 @@ var TransactionManager = class {
|
|
|
2112
2159
|
}
|
|
2113
2160
|
async commitTransaction(transactionId) {
|
|
2114
2161
|
return await this.tracingHelper.runInChildSpan("commit_transaction", async () => {
|
|
2115
|
-
|
|
2116
|
-
|
|
2162
|
+
await this.#withActiveTransactionLock(transactionId, "commit", async (txw) => {
|
|
2163
|
+
if (txw.depth > 1) {
|
|
2164
|
+
if (!txw.transaction) throw new TransactionNotFoundError();
|
|
2165
|
+
const savepointName = txw.savepoints.at(-1);
|
|
2166
|
+
if (!savepointName) {
|
|
2167
|
+
throw new TransactionInternalConsistencyError(
|
|
2168
|
+
`Missing savepoint for nested commit. Depth: ${txw.depth}, transactionId: ${txw.id}`
|
|
2169
|
+
);
|
|
2170
|
+
}
|
|
2171
|
+
try {
|
|
2172
|
+
await this.#releaseSavepoint(txw.transaction, savepointName);
|
|
2173
|
+
} finally {
|
|
2174
|
+
txw.savepoints.pop();
|
|
2175
|
+
txw.depth -= 1;
|
|
2176
|
+
}
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
await this.#closeTransaction(txw, "committed");
|
|
2180
|
+
});
|
|
2117
2181
|
});
|
|
2118
2182
|
}
|
|
2119
2183
|
async rollbackTransaction(transactionId) {
|
|
2120
2184
|
return await this.tracingHelper.runInChildSpan("rollback_transaction", async () => {
|
|
2121
|
-
|
|
2122
|
-
|
|
2185
|
+
await this.#withActiveTransactionLock(transactionId, "rollback", async (txw) => {
|
|
2186
|
+
if (txw.depth > 1) {
|
|
2187
|
+
if (!txw.transaction) throw new TransactionNotFoundError();
|
|
2188
|
+
const savepointName = txw.savepoints.at(-1);
|
|
2189
|
+
if (!savepointName) {
|
|
2190
|
+
throw new TransactionInternalConsistencyError(
|
|
2191
|
+
`Missing savepoint for nested rollback. Depth: ${txw.depth}, transactionId: ${txw.id}`
|
|
2192
|
+
);
|
|
2193
|
+
}
|
|
2194
|
+
try {
|
|
2195
|
+
await this.#requiredRollbackToSavepoint(txw.transaction)(savepointName);
|
|
2196
|
+
await this.#releaseSavepoint(txw.transaction, savepointName);
|
|
2197
|
+
} finally {
|
|
2198
|
+
txw.savepoints.pop();
|
|
2199
|
+
txw.depth -= 1;
|
|
2200
|
+
}
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
await this.#closeTransaction(txw, "rolled_back");
|
|
2204
|
+
});
|
|
2123
2205
|
});
|
|
2124
2206
|
}
|
|
2125
2207
|
async getTransaction(txInfo, operation) {
|
|
@@ -2163,22 +2245,90 @@ var TransactionManager = class {
|
|
|
2163
2245
|
return transaction;
|
|
2164
2246
|
}
|
|
2165
2247
|
async cancelAllTransactions() {
|
|
2166
|
-
await Promise.allSettled(
|
|
2248
|
+
await Promise.allSettled(
|
|
2249
|
+
[...this.transactions.values()].map(
|
|
2250
|
+
(tx) => this.#runSerialized(tx, async () => {
|
|
2251
|
+
const current = this.transactions.get(tx.id);
|
|
2252
|
+
if (current) {
|
|
2253
|
+
await this.#closeTransaction(current, "rolled_back");
|
|
2254
|
+
}
|
|
2255
|
+
})
|
|
2256
|
+
)
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
#nextSavepointName(transaction) {
|
|
2260
|
+
return `prisma_sp_${transaction.savepointCounter++}`;
|
|
2261
|
+
}
|
|
2262
|
+
#requiredCreateSavepoint(transaction) {
|
|
2263
|
+
if (transaction.createSavepoint) {
|
|
2264
|
+
return transaction.createSavepoint.bind(transaction);
|
|
2265
|
+
}
|
|
2266
|
+
throw new TransactionManagerError(
|
|
2267
|
+
`Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): createSavepoint is not implemented.`
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
2270
|
+
#requiredRollbackToSavepoint(transaction) {
|
|
2271
|
+
if (transaction.rollbackToSavepoint) {
|
|
2272
|
+
return transaction.rollbackToSavepoint.bind(transaction);
|
|
2273
|
+
}
|
|
2274
|
+
throw new TransactionManagerError(
|
|
2275
|
+
`Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): rollbackToSavepoint is not implemented.`
|
|
2276
|
+
);
|
|
2277
|
+
}
|
|
2278
|
+
async #releaseSavepoint(transaction, name) {
|
|
2279
|
+
if (transaction.releaseSavepoint) {
|
|
2280
|
+
await transaction.releaseSavepoint(name);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
#debugTransactionAlreadyClosedOnTimeout(transactionId) {
|
|
2284
|
+
debug("Transaction already committed or rolled back when timeout happened.", transactionId);
|
|
2167
2285
|
}
|
|
2168
2286
|
#startTransactionTimeout(transactionId, timeout) {
|
|
2169
2287
|
const timeoutStartedAt = Date.now();
|
|
2170
2288
|
const timer = createTimeoutIfDefined(async () => {
|
|
2171
2289
|
debug("Transaction timed out.", { transactionId, timeoutStartedAt, timeout });
|
|
2172
2290
|
const tx = this.transactions.get(transactionId);
|
|
2173
|
-
if (tx
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2291
|
+
if (!tx) {
|
|
2292
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
await this.#runSerialized(tx, async () => {
|
|
2296
|
+
const current = this.transactions.get(transactionId);
|
|
2297
|
+
if (current && ["running", "waiting"].includes(current.status)) {
|
|
2298
|
+
await this.#closeTransaction(current, "timed_out");
|
|
2299
|
+
} else {
|
|
2300
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
2301
|
+
}
|
|
2302
|
+
});
|
|
2178
2303
|
}, timeout);
|
|
2179
2304
|
timer?.unref?.();
|
|
2180
2305
|
return timer;
|
|
2181
2306
|
}
|
|
2307
|
+
// Any operation that mutates or closes a transaction must run through this lock so
|
|
2308
|
+
// status/savepoint/depth checks and updates happen against a stable view of state.
|
|
2309
|
+
async #withActiveTransactionLock(transactionId, operation, callback) {
|
|
2310
|
+
const tx = this.#getActiveOrClosingTransaction(transactionId, operation);
|
|
2311
|
+
return await this.#runSerialized(tx, async () => {
|
|
2312
|
+
const current = this.#getActiveOrClosingTransaction(transactionId, operation);
|
|
2313
|
+
return await callback(current);
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
// Serializes operations per transaction id to prevent interleaving across awaits.
|
|
2317
|
+
// This avoids races where one operation mutates savepoint/depth state while another
|
|
2318
|
+
// operation is suspended, which could otherwise corrupt cleanup logic.
|
|
2319
|
+
async #runSerialized(tx, callback) {
|
|
2320
|
+
const previousOperation = tx.operationQueue;
|
|
2321
|
+
let releaseOperationLock;
|
|
2322
|
+
tx.operationQueue = new Promise((resolve) => {
|
|
2323
|
+
releaseOperationLock = resolve;
|
|
2324
|
+
});
|
|
2325
|
+
await previousOperation;
|
|
2326
|
+
try {
|
|
2327
|
+
return await callback();
|
|
2328
|
+
} finally {
|
|
2329
|
+
releaseOperationLock();
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2182
2332
|
async #closeTransaction(tx, status) {
|
|
2183
2333
|
const createClosingPromise = async () => {
|
|
2184
2334
|
debug("Closing transaction.", { transactionId: tx.id, status });
|
|
@@ -2266,7 +2416,7 @@ function createTimeoutIfDefined(cb, ms) {
|
|
|
2266
2416
|
UserFacingError,
|
|
2267
2417
|
applySqlCommenters,
|
|
2268
2418
|
convertCompactedRows,
|
|
2269
|
-
|
|
2419
|
+
deserializeJsonObject,
|
|
2270
2420
|
doKeysMatch,
|
|
2271
2421
|
isDeepStrictEqual,
|
|
2272
2422
|
isPrismaValueGenerator,
|
package/dist/index.mjs
CHANGED
|
@@ -119,7 +119,10 @@ function normalizeJsonProtocolValues(result) {
|
|
|
119
119
|
function isTaggedValue(value) {
|
|
120
120
|
return value !== null && typeof value == "object" && typeof value["$type"] === "string";
|
|
121
121
|
}
|
|
122
|
-
function normalizeTaggedValue({
|
|
122
|
+
function normalizeTaggedValue({
|
|
123
|
+
$type,
|
|
124
|
+
value
|
|
125
|
+
}) {
|
|
123
126
|
switch ($type) {
|
|
124
127
|
case "BigInt":
|
|
125
128
|
return { $type, value: String(value) };
|
|
@@ -131,6 +134,12 @@ function normalizeTaggedValue({ $type, value }) {
|
|
|
131
134
|
return { $type, value: String(new Decimal2(value)) };
|
|
132
135
|
case "Json":
|
|
133
136
|
return { $type, value: JSON.stringify(JSON.parse(value)) };
|
|
137
|
+
case "Raw":
|
|
138
|
+
return { $type, value };
|
|
139
|
+
case "FieldRef":
|
|
140
|
+
return { $type, value };
|
|
141
|
+
case "Enum":
|
|
142
|
+
return { $type, value };
|
|
134
143
|
default:
|
|
135
144
|
assertNever(value, "Unknown tagged value");
|
|
136
145
|
}
|
|
@@ -142,12 +151,12 @@ function mapObjectValues(object, mapper) {
|
|
|
142
151
|
}
|
|
143
152
|
return result;
|
|
144
153
|
}
|
|
145
|
-
function
|
|
154
|
+
function deserializeJsonObject(result) {
|
|
146
155
|
if (result === null) {
|
|
147
156
|
return result;
|
|
148
157
|
}
|
|
149
158
|
if (Array.isArray(result)) {
|
|
150
|
-
return result.map(
|
|
159
|
+
return result.map(deserializeJsonObject);
|
|
151
160
|
}
|
|
152
161
|
if (typeof result === "object") {
|
|
153
162
|
if (isTaggedValue(result)) {
|
|
@@ -156,7 +165,7 @@ function deserializeJsonResponse(result) {
|
|
|
156
165
|
if (result.constructor !== null && result.constructor.name !== "Object") {
|
|
157
166
|
return result;
|
|
158
167
|
}
|
|
159
|
-
return mapObjectValues(result,
|
|
168
|
+
return mapObjectValues(result, deserializeJsonObject);
|
|
160
169
|
}
|
|
161
170
|
return result;
|
|
162
171
|
}
|
|
@@ -174,6 +183,12 @@ function deserializeTaggedValue({ $type, value }) {
|
|
|
174
183
|
return new Decimal2(value);
|
|
175
184
|
case "Json":
|
|
176
185
|
return JSON.parse(value);
|
|
186
|
+
case "Raw":
|
|
187
|
+
return value;
|
|
188
|
+
case "FieldRef":
|
|
189
|
+
throw new Error("FieldRef tagged values cannot be deserialized to JavaScript values");
|
|
190
|
+
case "Enum":
|
|
191
|
+
return value;
|
|
177
192
|
default:
|
|
178
193
|
assertNever(value, "Unknown tagged value");
|
|
179
194
|
}
|
|
@@ -405,7 +420,7 @@ function resolveArgPlaceholders(args, placeholderValues) {
|
|
|
405
420
|
function convertCompactedRows(rows, compiledBatch, placeholderValues = {}) {
|
|
406
421
|
const keysPerRow = rows.map(
|
|
407
422
|
(item) => compiledBatch.keys.reduce((acc, key) => {
|
|
408
|
-
acc[key] =
|
|
423
|
+
acc[key] = deserializeJsonObject(item[key]);
|
|
409
424
|
return acc;
|
|
410
425
|
}, {})
|
|
411
426
|
);
|
|
@@ -1070,7 +1085,10 @@ function renderFragment(fragment, placeholderFormat, ctx) {
|
|
|
1070
1085
|
case "stringChunk":
|
|
1071
1086
|
return fragment.chunk;
|
|
1072
1087
|
case "parameterTuple": {
|
|
1073
|
-
const placeholders = fragment.value.length == 0 ? "NULL" : fragment.value.map(() =>
|
|
1088
|
+
const placeholders = fragment.value.length == 0 ? "NULL" : fragment.value.map(() => {
|
|
1089
|
+
const item = formatPlaceholder(placeholderFormat, ctx.placeholderNumber++);
|
|
1090
|
+
return `${fragment.itemPrefix}${item}${fragment.itemSuffix}`;
|
|
1091
|
+
}).join(fragment.itemSeparator);
|
|
1074
1092
|
return `(${placeholders})`;
|
|
1075
1093
|
}
|
|
1076
1094
|
case "parameterTupleList": {
|
|
@@ -2021,13 +2039,42 @@ var TransactionManager = class {
|
|
|
2021
2039
|
);
|
|
2022
2040
|
}
|
|
2023
2041
|
async #startTransactionImpl(options) {
|
|
2042
|
+
if (options.newTxId) {
|
|
2043
|
+
return await this.#withActiveTransactionLock(options.newTxId, "start", async (existing) => {
|
|
2044
|
+
if (existing.status !== "running") {
|
|
2045
|
+
throw new TransactionInternalConsistencyError(
|
|
2046
|
+
`Transaction in invalid state ${existing.status} when starting a nested transaction.`
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
if (!existing.transaction) {
|
|
2050
|
+
throw new TransactionInternalConsistencyError(
|
|
2051
|
+
`Transaction missing underlying driver transaction when starting a nested transaction.`
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
existing.depth += 1;
|
|
2055
|
+
const savepointName = this.#nextSavepointName(existing);
|
|
2056
|
+
existing.savepoints.push(savepointName);
|
|
2057
|
+
try {
|
|
2058
|
+
await this.#requiredCreateSavepoint(existing.transaction)(savepointName);
|
|
2059
|
+
} catch (e) {
|
|
2060
|
+
existing.depth -= 1;
|
|
2061
|
+
existing.savepoints.pop();
|
|
2062
|
+
throw e;
|
|
2063
|
+
}
|
|
2064
|
+
return { id: existing.id };
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2024
2067
|
const transaction = {
|
|
2025
2068
|
id: await randomUUID(),
|
|
2026
2069
|
status: "waiting",
|
|
2027
2070
|
timer: void 0,
|
|
2028
2071
|
timeout: options.timeout,
|
|
2029
2072
|
startedAt: Date.now(),
|
|
2030
|
-
transaction: void 0
|
|
2073
|
+
transaction: void 0,
|
|
2074
|
+
operationQueue: Promise.resolve(),
|
|
2075
|
+
depth: 1,
|
|
2076
|
+
savepoints: [],
|
|
2077
|
+
savepointCounter: 0
|
|
2031
2078
|
};
|
|
2032
2079
|
const abortController = new AbortController();
|
|
2033
2080
|
const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
|
|
@@ -2061,14 +2108,49 @@ var TransactionManager = class {
|
|
|
2061
2108
|
}
|
|
2062
2109
|
async commitTransaction(transactionId) {
|
|
2063
2110
|
return await this.tracingHelper.runInChildSpan("commit_transaction", async () => {
|
|
2064
|
-
|
|
2065
|
-
|
|
2111
|
+
await this.#withActiveTransactionLock(transactionId, "commit", async (txw) => {
|
|
2112
|
+
if (txw.depth > 1) {
|
|
2113
|
+
if (!txw.transaction) throw new TransactionNotFoundError();
|
|
2114
|
+
const savepointName = txw.savepoints.at(-1);
|
|
2115
|
+
if (!savepointName) {
|
|
2116
|
+
throw new TransactionInternalConsistencyError(
|
|
2117
|
+
`Missing savepoint for nested commit. Depth: ${txw.depth}, transactionId: ${txw.id}`
|
|
2118
|
+
);
|
|
2119
|
+
}
|
|
2120
|
+
try {
|
|
2121
|
+
await this.#releaseSavepoint(txw.transaction, savepointName);
|
|
2122
|
+
} finally {
|
|
2123
|
+
txw.savepoints.pop();
|
|
2124
|
+
txw.depth -= 1;
|
|
2125
|
+
}
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
await this.#closeTransaction(txw, "committed");
|
|
2129
|
+
});
|
|
2066
2130
|
});
|
|
2067
2131
|
}
|
|
2068
2132
|
async rollbackTransaction(transactionId) {
|
|
2069
2133
|
return await this.tracingHelper.runInChildSpan("rollback_transaction", async () => {
|
|
2070
|
-
|
|
2071
|
-
|
|
2134
|
+
await this.#withActiveTransactionLock(transactionId, "rollback", async (txw) => {
|
|
2135
|
+
if (txw.depth > 1) {
|
|
2136
|
+
if (!txw.transaction) throw new TransactionNotFoundError();
|
|
2137
|
+
const savepointName = txw.savepoints.at(-1);
|
|
2138
|
+
if (!savepointName) {
|
|
2139
|
+
throw new TransactionInternalConsistencyError(
|
|
2140
|
+
`Missing savepoint for nested rollback. Depth: ${txw.depth}, transactionId: ${txw.id}`
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
try {
|
|
2144
|
+
await this.#requiredRollbackToSavepoint(txw.transaction)(savepointName);
|
|
2145
|
+
await this.#releaseSavepoint(txw.transaction, savepointName);
|
|
2146
|
+
} finally {
|
|
2147
|
+
txw.savepoints.pop();
|
|
2148
|
+
txw.depth -= 1;
|
|
2149
|
+
}
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
await this.#closeTransaction(txw, "rolled_back");
|
|
2153
|
+
});
|
|
2072
2154
|
});
|
|
2073
2155
|
}
|
|
2074
2156
|
async getTransaction(txInfo, operation) {
|
|
@@ -2112,22 +2194,90 @@ var TransactionManager = class {
|
|
|
2112
2194
|
return transaction;
|
|
2113
2195
|
}
|
|
2114
2196
|
async cancelAllTransactions() {
|
|
2115
|
-
await Promise.allSettled(
|
|
2197
|
+
await Promise.allSettled(
|
|
2198
|
+
[...this.transactions.values()].map(
|
|
2199
|
+
(tx) => this.#runSerialized(tx, async () => {
|
|
2200
|
+
const current = this.transactions.get(tx.id);
|
|
2201
|
+
if (current) {
|
|
2202
|
+
await this.#closeTransaction(current, "rolled_back");
|
|
2203
|
+
}
|
|
2204
|
+
})
|
|
2205
|
+
)
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2208
|
+
#nextSavepointName(transaction) {
|
|
2209
|
+
return `prisma_sp_${transaction.savepointCounter++}`;
|
|
2210
|
+
}
|
|
2211
|
+
#requiredCreateSavepoint(transaction) {
|
|
2212
|
+
if (transaction.createSavepoint) {
|
|
2213
|
+
return transaction.createSavepoint.bind(transaction);
|
|
2214
|
+
}
|
|
2215
|
+
throw new TransactionManagerError(
|
|
2216
|
+
`Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): createSavepoint is not implemented.`
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
#requiredRollbackToSavepoint(transaction) {
|
|
2220
|
+
if (transaction.rollbackToSavepoint) {
|
|
2221
|
+
return transaction.rollbackToSavepoint.bind(transaction);
|
|
2222
|
+
}
|
|
2223
|
+
throw new TransactionManagerError(
|
|
2224
|
+
`Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): rollbackToSavepoint is not implemented.`
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
async #releaseSavepoint(transaction, name) {
|
|
2228
|
+
if (transaction.releaseSavepoint) {
|
|
2229
|
+
await transaction.releaseSavepoint(name);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
#debugTransactionAlreadyClosedOnTimeout(transactionId) {
|
|
2233
|
+
debug("Transaction already committed or rolled back when timeout happened.", transactionId);
|
|
2116
2234
|
}
|
|
2117
2235
|
#startTransactionTimeout(transactionId, timeout) {
|
|
2118
2236
|
const timeoutStartedAt = Date.now();
|
|
2119
2237
|
const timer = createTimeoutIfDefined(async () => {
|
|
2120
2238
|
debug("Transaction timed out.", { transactionId, timeoutStartedAt, timeout });
|
|
2121
2239
|
const tx = this.transactions.get(transactionId);
|
|
2122
|
-
if (tx
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2240
|
+
if (!tx) {
|
|
2241
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
await this.#runSerialized(tx, async () => {
|
|
2245
|
+
const current = this.transactions.get(transactionId);
|
|
2246
|
+
if (current && ["running", "waiting"].includes(current.status)) {
|
|
2247
|
+
await this.#closeTransaction(current, "timed_out");
|
|
2248
|
+
} else {
|
|
2249
|
+
this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
|
|
2250
|
+
}
|
|
2251
|
+
});
|
|
2127
2252
|
}, timeout);
|
|
2128
2253
|
timer?.unref?.();
|
|
2129
2254
|
return timer;
|
|
2130
2255
|
}
|
|
2256
|
+
// Any operation that mutates or closes a transaction must run through this lock so
|
|
2257
|
+
// status/savepoint/depth checks and updates happen against a stable view of state.
|
|
2258
|
+
async #withActiveTransactionLock(transactionId, operation, callback) {
|
|
2259
|
+
const tx = this.#getActiveOrClosingTransaction(transactionId, operation);
|
|
2260
|
+
return await this.#runSerialized(tx, async () => {
|
|
2261
|
+
const current = this.#getActiveOrClosingTransaction(transactionId, operation);
|
|
2262
|
+
return await callback(current);
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
// Serializes operations per transaction id to prevent interleaving across awaits.
|
|
2266
|
+
// This avoids races where one operation mutates savepoint/depth state while another
|
|
2267
|
+
// operation is suspended, which could otherwise corrupt cleanup logic.
|
|
2268
|
+
async #runSerialized(tx, callback) {
|
|
2269
|
+
const previousOperation = tx.operationQueue;
|
|
2270
|
+
let releaseOperationLock;
|
|
2271
|
+
tx.operationQueue = new Promise((resolve) => {
|
|
2272
|
+
releaseOperationLock = resolve;
|
|
2273
|
+
});
|
|
2274
|
+
await previousOperation;
|
|
2275
|
+
try {
|
|
2276
|
+
return await callback();
|
|
2277
|
+
} finally {
|
|
2278
|
+
releaseOperationLock();
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2131
2281
|
async #closeTransaction(tx, status) {
|
|
2132
2282
|
const createClosingPromise = async () => {
|
|
2133
2283
|
debug("Closing transaction.", { transactionId: tx.id, status });
|
|
@@ -2214,7 +2364,7 @@ export {
|
|
|
2214
2364
|
UserFacingError,
|
|
2215
2365
|
applySqlCommenters,
|
|
2216
2366
|
convertCompactedRows,
|
|
2217
|
-
|
|
2367
|
+
deserializeJsonObject,
|
|
2218
2368
|
doKeysMatch,
|
|
2219
2369
|
isDeepStrictEqual,
|
|
2220
2370
|
isPrismaValueGenerator,
|
package/dist/json-protocol.d.ts
CHANGED
|
@@ -29,10 +29,14 @@ export type JsonTaggedValue = {
|
|
|
29
29
|
$type: 'Json';
|
|
30
30
|
value: string;
|
|
31
31
|
};
|
|
32
|
-
export type
|
|
32
|
+
export type RawTaggedValue = {
|
|
33
|
+
$type: 'Raw';
|
|
34
|
+
value: unknown;
|
|
35
|
+
};
|
|
36
|
+
export type JsonInputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | FieldRefTaggedValue | JsonTaggedValue | EnumTaggedValue | RawTaggedValue;
|
|
33
37
|
export type JsonOutputTaggedValue = DateTaggedValue | DecimalTaggedValue | BytesTaggedValue | BigIntTaggedValue | JsonTaggedValue;
|
|
34
38
|
export type JsOutputValue = null | string | number | boolean | bigint | Uint8Array | Date | Decimal | JsOutputValue[] | {
|
|
35
39
|
[key: string]: JsOutputValue;
|
|
36
40
|
};
|
|
37
41
|
export declare function normalizeJsonProtocolValues(result: unknown): unknown;
|
|
38
|
-
export declare function
|
|
42
|
+
export declare function deserializeJsonObject(result: unknown): unknown;
|
package/dist/query-plan.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/client-engine-runtime",
|
|
3
|
-
"version": "7.5.0-dev.
|
|
3
|
+
"version": "7.5.0-dev.21",
|
|
4
4
|
"description": "This package is intended for Prisma's internal use",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -31,19 +31,16 @@
|
|
|
31
31
|
"nanoid": "5.1.5",
|
|
32
32
|
"ulid": "3.0.0",
|
|
33
33
|
"uuid": "11.1.0",
|
|
34
|
-
"@prisma/
|
|
35
|
-
"@prisma/
|
|
36
|
-
"@prisma/driver-adapter-utils": "7.5.0-dev.
|
|
37
|
-
"@prisma/sqlcommenter": "7.5.0-dev.
|
|
34
|
+
"@prisma/client-runtime-utils": "7.5.0-dev.21",
|
|
35
|
+
"@prisma/debug": "7.5.0-dev.21",
|
|
36
|
+
"@prisma/driver-adapter-utils": "7.5.0-dev.21",
|
|
37
|
+
"@prisma/sqlcommenter": "7.5.0-dev.21"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@codspeed/benchmark.js-plugin": "4.0.0",
|
|
41
41
|
"@types/benchmark": "2.1.5",
|
|
42
|
-
"@types/jest": "29.5.14",
|
|
43
42
|
"@types/node": "~20.19.24",
|
|
44
|
-
"benchmark": "2.1.4"
|
|
45
|
-
"jest": "29.7.0",
|
|
46
|
-
"jest-junit": "16.0.0"
|
|
43
|
+
"benchmark": "2.1.4"
|
|
47
44
|
},
|
|
48
45
|
"files": [
|
|
49
46
|
"dist"
|
|
@@ -52,6 +49,6 @@
|
|
|
52
49
|
"scripts": {
|
|
53
50
|
"dev": "DEV=true tsx helpers/build.ts",
|
|
54
51
|
"build": "tsx helpers/build.ts",
|
|
55
|
-
"test": "
|
|
52
|
+
"test": "vitest run"
|
|
56
53
|
}
|
|
57
54
|
}
|