@prisma/query-plan-executor 7.5.0-dev.13 → 7.5.0-dev.14

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.ts CHANGED
@@ -668,6 +668,7 @@ json: {
668
668
  timeout?: number | undefined;
669
669
  maxWait?: number | undefined;
670
670
  isolationLevel?: "READ UNCOMMITTED" | "READ COMMITTED" | "REPEATABLE READ" | "SNAPSHOT" | "SERIALIZABLE" | undefined;
671
+ newTxId?: string | undefined;
671
672
  };
672
673
  };
673
674
  output: {
@@ -3248,6 +3249,18 @@ declare interface Transaction extends AdapterInfo, SqlQueryable {
3248
3249
  * Roll back the transaction.
3249
3250
  */
3250
3251
  rollback(): Promise<void>;
3252
+ /**
3253
+ * Creates a savepoint within the currently running transaction.
3254
+ */
3255
+ createSavepoint?(name: string): Promise<void>;
3256
+ /**
3257
+ * Rolls back transaction state to a previously created savepoint.
3258
+ */
3259
+ rollbackToSavepoint?(name: string): Promise<void>;
3260
+ /**
3261
+ * Releases a previously created savepoint. Optional because not every connector supports this operation.
3262
+ */
3263
+ releaseSavepoint?(name: string): Promise<void>;
3251
3264
  }
3252
3265
 
3253
3266
  declare type TransactionInfo = {
@@ -3289,6 +3302,7 @@ declare type TransactionOptions_2 = {
3289
3302
  maxWait?: number;
3290
3303
  timeout?: number;
3291
3304
  isolationLevel?: IsolationLevel;
3305
+ newTxId?: string;
3292
3306
  };
3293
3307
 
3294
3308
  declare type TypedResponse<T = unknown, U extends StatusCode = StatusCode, F extends ResponseFormat = T extends string ? 'text' : T extends JSONValue ? 'json' : ResponseFormat> = {
package/dist/index.js CHANGED
@@ -101325,7 +101325,7 @@ __export(index_exports, {
101325
101325
  module.exports = __toCommonJS(index_exports);
101326
101326
 
101327
101327
  // package.json
101328
- var version = "7.5.0-dev.13";
101328
+ var version = "7.5.0-dev.14";
101329
101329
 
101330
101330
  // ../../node_modules/.pnpm/temporal-polyfill@0.3.0/node_modules/temporal-polyfill/chunks/internal.js
101331
101331
  function clampProp(e2, n2, t2, o2, r2) {
@@ -111492,13 +111492,42 @@ var TransactionManager = class {
111492
111492
  );
111493
111493
  }
111494
111494
  async #startTransactionImpl(options) {
111495
+ if (options.newTxId) {
111496
+ return await this.#withActiveTransactionLock(options.newTxId, "start", async (existing) => {
111497
+ if (existing.status !== "running") {
111498
+ throw new TransactionInternalConsistencyError(
111499
+ `Transaction in invalid state ${existing.status} when starting a nested transaction.`
111500
+ );
111501
+ }
111502
+ if (!existing.transaction) {
111503
+ throw new TransactionInternalConsistencyError(
111504
+ `Transaction missing underlying driver transaction when starting a nested transaction.`
111505
+ );
111506
+ }
111507
+ existing.depth += 1;
111508
+ const savepointName = this.#nextSavepointName(existing);
111509
+ existing.savepoints.push(savepointName);
111510
+ try {
111511
+ await this.#requiredCreateSavepoint(existing.transaction)(savepointName);
111512
+ } catch (e2) {
111513
+ existing.depth -= 1;
111514
+ existing.savepoints.pop();
111515
+ throw e2;
111516
+ }
111517
+ return { id: existing.id };
111518
+ });
111519
+ }
111495
111520
  const transaction = {
111496
111521
  id: await randomUUID2(),
111497
111522
  status: "waiting",
111498
111523
  timer: void 0,
111499
111524
  timeout: options.timeout,
111500
111525
  startedAt: Date.now(),
111501
- transaction: void 0
111526
+ transaction: void 0,
111527
+ operationQueue: Promise.resolve(),
111528
+ depth: 1,
111529
+ savepoints: [],
111530
+ savepointCounter: 0
111502
111531
  };
111503
111532
  const abortController = new AbortController();
111504
111533
  const startTimer = createTimeoutIfDefined(() => abortController.abort(), options.maxWait);
@@ -111532,14 +111561,49 @@ var TransactionManager = class {
111532
111561
  }
111533
111562
  async commitTransaction(transactionId) {
111534
111563
  return await this.tracingHelper.runInChildSpan("commit_transaction", async () => {
111535
- const txw = this.#getActiveOrClosingTransaction(transactionId, "commit");
111536
- await this.#closeTransaction(txw, "committed");
111564
+ await this.#withActiveTransactionLock(transactionId, "commit", async (txw) => {
111565
+ if (txw.depth > 1) {
111566
+ if (!txw.transaction) throw new TransactionNotFoundError();
111567
+ const savepointName = txw.savepoints.at(-1);
111568
+ if (!savepointName) {
111569
+ throw new TransactionInternalConsistencyError(
111570
+ `Missing savepoint for nested commit. Depth: ${txw.depth}, transactionId: ${txw.id}`
111571
+ );
111572
+ }
111573
+ try {
111574
+ await this.#releaseSavepoint(txw.transaction, savepointName);
111575
+ } finally {
111576
+ txw.savepoints.pop();
111577
+ txw.depth -= 1;
111578
+ }
111579
+ return;
111580
+ }
111581
+ await this.#closeTransaction(txw, "committed");
111582
+ });
111537
111583
  });
111538
111584
  }
111539
111585
  async rollbackTransaction(transactionId) {
111540
111586
  return await this.tracingHelper.runInChildSpan("rollback_transaction", async () => {
111541
- const txw = this.#getActiveOrClosingTransaction(transactionId, "rollback");
111542
- await this.#closeTransaction(txw, "rolled_back");
111587
+ await this.#withActiveTransactionLock(transactionId, "rollback", async (txw) => {
111588
+ if (txw.depth > 1) {
111589
+ if (!txw.transaction) throw new TransactionNotFoundError();
111590
+ const savepointName = txw.savepoints.at(-1);
111591
+ if (!savepointName) {
111592
+ throw new TransactionInternalConsistencyError(
111593
+ `Missing savepoint for nested rollback. Depth: ${txw.depth}, transactionId: ${txw.id}`
111594
+ );
111595
+ }
111596
+ try {
111597
+ await this.#requiredRollbackToSavepoint(txw.transaction)(savepointName);
111598
+ await this.#releaseSavepoint(txw.transaction, savepointName);
111599
+ } finally {
111600
+ txw.savepoints.pop();
111601
+ txw.depth -= 1;
111602
+ }
111603
+ return;
111604
+ }
111605
+ await this.#closeTransaction(txw, "rolled_back");
111606
+ });
111543
111607
  });
111544
111608
  }
111545
111609
  async getTransaction(txInfo, operation) {
@@ -111583,22 +111647,90 @@ var TransactionManager = class {
111583
111647
  return transaction;
111584
111648
  }
111585
111649
  async cancelAllTransactions() {
111586
- await Promise.allSettled([...this.transactions.values()].map((tx) => this.#closeTransaction(tx, "rolled_back")));
111650
+ await Promise.allSettled(
111651
+ [...this.transactions.values()].map(
111652
+ (tx) => this.#runSerialized(tx, async () => {
111653
+ const current = this.transactions.get(tx.id);
111654
+ if (current) {
111655
+ await this.#closeTransaction(current, "rolled_back");
111656
+ }
111657
+ })
111658
+ )
111659
+ );
111660
+ }
111661
+ #nextSavepointName(transaction) {
111662
+ return `prisma_sp_${transaction.savepointCounter++}`;
111663
+ }
111664
+ #requiredCreateSavepoint(transaction) {
111665
+ if (transaction.createSavepoint) {
111666
+ return transaction.createSavepoint.bind(transaction);
111667
+ }
111668
+ throw new TransactionManagerError(
111669
+ `Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): createSavepoint is not implemented.`
111670
+ );
111671
+ }
111672
+ #requiredRollbackToSavepoint(transaction) {
111673
+ if (transaction.rollbackToSavepoint) {
111674
+ return transaction.rollbackToSavepoint.bind(transaction);
111675
+ }
111676
+ throw new TransactionManagerError(
111677
+ `Nested transactions are not supported by adapter "${transaction.adapterName}" (${transaction.provider}): rollbackToSavepoint is not implemented.`
111678
+ );
111679
+ }
111680
+ async #releaseSavepoint(transaction, name6) {
111681
+ if (transaction.releaseSavepoint) {
111682
+ await transaction.releaseSavepoint(name6);
111683
+ }
111684
+ }
111685
+ #debugTransactionAlreadyClosedOnTimeout(transactionId) {
111686
+ debug3("Transaction already committed or rolled back when timeout happened.", transactionId);
111587
111687
  }
111588
111688
  #startTransactionTimeout(transactionId, timeout) {
111589
111689
  const timeoutStartedAt = Date.now();
111590
111690
  const timer = createTimeoutIfDefined(async () => {
111591
111691
  debug3("Transaction timed out.", { transactionId, timeoutStartedAt, timeout });
111592
111692
  const tx = this.transactions.get(transactionId);
111593
- if (tx && ["running", "waiting"].includes(tx.status)) {
111594
- await this.#closeTransaction(tx, "timed_out");
111595
- } else {
111596
- debug3("Transaction already committed or rolled back when timeout happened.", transactionId);
111693
+ if (!tx) {
111694
+ this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
111695
+ return;
111597
111696
  }
111697
+ await this.#runSerialized(tx, async () => {
111698
+ const current = this.transactions.get(transactionId);
111699
+ if (current && ["running", "waiting"].includes(current.status)) {
111700
+ await this.#closeTransaction(current, "timed_out");
111701
+ } else {
111702
+ this.#debugTransactionAlreadyClosedOnTimeout(transactionId);
111703
+ }
111704
+ });
111598
111705
  }, timeout);
111599
111706
  timer?.unref?.();
111600
111707
  return timer;
111601
111708
  }
111709
+ // Any operation that mutates or closes a transaction must run through this lock so
111710
+ // status/savepoint/depth checks and updates happen against a stable view of state.
111711
+ async #withActiveTransactionLock(transactionId, operation, callback) {
111712
+ const tx = this.#getActiveOrClosingTransaction(transactionId, operation);
111713
+ return await this.#runSerialized(tx, async () => {
111714
+ const current = this.#getActiveOrClosingTransaction(transactionId, operation);
111715
+ return await callback(current);
111716
+ });
111717
+ }
111718
+ // Serializes operations per transaction id to prevent interleaving across awaits.
111719
+ // This avoids races where one operation mutates savepoint/depth state while another
111720
+ // operation is suspended, which could otherwise corrupt cleanup logic.
111721
+ async #runSerialized(tx, callback) {
111722
+ const previousOperation = tx.operationQueue;
111723
+ let releaseOperationLock;
111724
+ tx.operationQueue = new Promise((resolve) => {
111725
+ releaseOperationLock = resolve;
111726
+ });
111727
+ await previousOperation;
111728
+ try {
111729
+ return await callback();
111730
+ } finally {
111731
+ releaseOperationLock();
111732
+ }
111733
+ }
111602
111734
  async #closeTransaction(tx, status) {
111603
111735
  const createClosingPromise = async () => {
111604
111736
  debug3("Closing transaction.", { transactionId: tx.id, status });
@@ -114137,6 +114269,15 @@ var MariaDbTransaction = class extends MariaDbQueryable {
114137
114269
  this.cleanup?.();
114138
114270
  await this.client.end();
114139
114271
  }
114272
+ async createSavepoint(name22) {
114273
+ await this.executeRaw({ sql: `SAVEPOINT ${name22}`, args: [], argTypes: [] });
114274
+ }
114275
+ async rollbackToSavepoint(name22) {
114276
+ await this.executeRaw({ sql: `ROLLBACK TO ${name22}`, args: [], argTypes: [] });
114277
+ }
114278
+ async releaseSavepoint(name22) {
114279
+ await this.executeRaw({ sql: `RELEASE SAVEPOINT ${name22}`, args: [], argTypes: [] });
114280
+ }
114140
114281
  };
114141
114282
  var PrismaMariaDbAdapter = class extends MariaDbQueryable {
114142
114283
  constructor(client, capabilities, options) {
@@ -115165,6 +115306,12 @@ var MssqlTransaction = class extends MssqlQueryable {
115165
115306
  release2();
115166
115307
  }
115167
115308
  }
115309
+ async createSavepoint(name22) {
115310
+ await this.executeRaw({ sql: `SAVE TRANSACTION ${name22}`, args: [], argTypes: [] });
115311
+ }
115312
+ async rollbackToSavepoint(name22) {
115313
+ await this.executeRaw({ sql: `ROLLBACK TRANSACTION ${name22}`, args: [], argTypes: [] });
115314
+ }
115168
115315
  };
115169
115316
  var PrismaMssqlAdapter = class extends MssqlQueryable {
115170
115317
  constructor(pool2, options) {
@@ -115907,6 +116054,15 @@ var PgTransaction = class extends PgQueryable {
115907
116054
  this.cleanup?.();
115908
116055
  this.client.release();
115909
116056
  }
116057
+ async createSavepoint(name22) {
116058
+ await this.executeRaw({ sql: `SAVEPOINT ${name22}`, args: [], argTypes: [] });
116059
+ }
116060
+ async rollbackToSavepoint(name22) {
116061
+ await this.executeRaw({ sql: `ROLLBACK TO SAVEPOINT ${name22}`, args: [], argTypes: [] });
116062
+ }
116063
+ async releaseSavepoint(name22) {
116064
+ await this.executeRaw({ sql: `RELEASE SAVEPOINT ${name22}`, args: [], argTypes: [] });
116065
+ }
115910
116066
  };
115911
116067
  var PrismaPgAdapter = class extends PgQueryable {
115912
116068
  constructor(client, pgOptions, release2) {
@@ -116103,13 +116259,15 @@ function createConnectionStringRegex(protocols) {
116103
116259
  }
116104
116260
  function wrapFactory(protocols, factory) {
116105
116261
  return {
116106
- ...factory,
116262
+ adapterName: factory.adapterName,
116263
+ provider: factory.provider,
116107
116264
  connect: () => factory.connect().then(wrapAdapter.bind(null, protocols), rethrowSanitizedError.bind(null, protocols))
116108
116265
  };
116109
116266
  }
116110
116267
  function wrapAdapter(protocols, adapter) {
116111
116268
  return {
116112
- ...adapter,
116269
+ adapterName: adapter.adapterName,
116270
+ provider: adapter.provider,
116113
116271
  dispose: () => adapter.dispose().catch(rethrowSanitizedError.bind(null, protocols)),
116114
116272
  executeRaw: (query2) => adapter.executeRaw(query2).catch(rethrowSanitizedError.bind(null, protocols)),
116115
116273
  queryRaw: (query2) => adapter.queryRaw(query2).catch(rethrowSanitizedError.bind(null, protocols)),
@@ -116120,11 +116278,16 @@ function wrapAdapter(protocols, adapter) {
116120
116278
  }
116121
116279
  function wrapTransaction(protocols, tx) {
116122
116280
  return {
116123
- ...tx,
116281
+ adapterName: tx.adapterName,
116282
+ provider: tx.provider,
116283
+ options: tx.options,
116124
116284
  commit: () => tx.commit().catch(rethrowSanitizedError.bind(null, protocols)),
116125
116285
  rollback: () => tx.rollback().catch(rethrowSanitizedError.bind(null, protocols)),
116126
116286
  executeRaw: (query2) => tx.executeRaw(query2).catch(rethrowSanitizedError.bind(null, protocols)),
116127
- queryRaw: (query2) => tx.queryRaw(query2).catch(rethrowSanitizedError.bind(null, protocols))
116287
+ queryRaw: (query2) => tx.queryRaw(query2).catch(rethrowSanitizedError.bind(null, protocols)),
116288
+ createSavepoint: tx.createSavepoint ? (name6) => tx.createSavepoint(name6).catch(rethrowSanitizedError.bind(null, protocols)) : void 0,
116289
+ rollbackToSavepoint: tx.rollbackToSavepoint ? (name6) => tx.rollbackToSavepoint(name6).catch(rethrowSanitizedError.bind(null, protocols)) : void 0,
116290
+ releaseSavepoint: tx.releaseSavepoint ? (name6) => tx.releaseSavepoint(name6).catch(rethrowSanitizedError.bind(null, protocols)) : void 0
116128
116291
  };
116129
116292
  }
116130
116293
 
@@ -128429,7 +128592,8 @@ var QueryRequestBody = external_exports.object({
128429
128592
  var TransactionStartRequestBody = external_exports.object({
128430
128593
  timeout: external_exports.number().optional(),
128431
128594
  maxWait: external_exports.number().optional(),
128432
- isolationLevel: external_exports.enum(["READ UNCOMMITTED", "READ COMMITTED", "REPEATABLE READ", "SNAPSHOT", "SERIALIZABLE"]).optional()
128595
+ isolationLevel: external_exports.enum(["READ UNCOMMITTED", "READ COMMITTED", "REPEATABLE READ", "SNAPSHOT", "SERIALIZABLE"]).optional(),
128596
+ newTxId: external_exports.string().optional()
128433
128597
  });
128434
128598
 
128435
128599
  // src/server/server.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/query-plan-executor",
3
- "version": "7.5.0-dev.13",
3
+ "version": "7.5.0-dev.14",
4
4
  "description": "This package is intended for Prisma's internal use",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,11 +20,11 @@
20
20
  "temporal-polyfill": "0.3.0",
21
21
  "vitest": "3.2.4",
22
22
  "zod": "4.1.3",
23
- "@prisma/adapter-pg": "7.5.0-dev.13",
24
- "@prisma/adapter-mariadb": "7.5.0-dev.13",
25
- "@prisma/client-engine-runtime": "7.5.0-dev.13",
26
- "@prisma/adapter-mssql": "7.5.0-dev.13",
27
- "@prisma/driver-adapter-utils": "7.5.0-dev.13"
23
+ "@prisma/adapter-pg": "7.5.0-dev.14",
24
+ "@prisma/adapter-mariadb": "7.5.0-dev.14",
25
+ "@prisma/adapter-mssql": "7.5.0-dev.14",
26
+ "@prisma/client-engine-runtime": "7.5.0-dev.14",
27
+ "@prisma/driver-adapter-utils": "7.5.0-dev.14"
28
28
  },
29
29
  "files": [
30
30
  "dist"