@vibeorm/adapter-mysql 2.0.0-alpha.8 → 2.0.0

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/README.md CHANGED
@@ -5,10 +5,10 @@
5
5
  MySQL adapter built on [mysql2](https://github.com/sidorares/node-mysql2) (`mysql2/promise`), which ships as a direct dependency. It implements the frozen `DatabaseAdapter` contract from `@vibeorm/runtime` and runs on both Bun and Node.
6
6
 
7
7
  ```bash
8
- bun add @vibeorm/adapter-mysql@alpha
8
+ bun add @vibeorm/adapter-mysql
9
9
  ```
10
10
 
11
- > **Pre-release.** `2.0.0-alpha.x`, published under the `alpha` dist-tag. `npm i vibeorm` still resolves to the stable v1 line.
11
+ > **Stable.** `2.0.0`, published under the `latest` dist-tag. The v1 line stays installable as `vibeorm@1`.
12
12
 
13
13
  ## Usage
14
14
 
package/dist/index.d.ts CHANGED
@@ -178,4 +178,6 @@ export { createSavepointCounter, nextSavepointName } from "./savepoints.ts";
178
178
  export type { SavepointCounter } from "./savepoints.ts";
179
179
  export { ISOLATION_LEVEL_SQL, RESET_MAX_EXECUTION_TIME_SQL, classifyRawTransactionControl, maxExecutionTimeStatement, refuseNestedTransactionOptions, setIsolationLevelStatement, } from "./transaction-sql.ts";
180
180
  export type { RawTransactionControl } from "./transaction-sql.ts";
181
+ export { DEFAULT_DEADLINE_CLOCK, engineStatementBudgetMs, isTransactionDeadlineError, startTransactionBudget, transactionClosedError, transactionDeadlineError, transactionOutcomeUnknownError, validateTransactionDeadline, } from "./transaction-budget.ts";
182
+ export type { BudgetStage, DeadlineClock, TransactionBudget, TransactionOutcome } from "./transaction-budget.ts";
181
183
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -123,6 +123,131 @@ async function runMapped(fn) {
123
123
  }
124
124
  }
125
125
 
126
+ // packages/adapter-mysql/src/placeholders.ts
127
+ import { VibeError as VibeError3 } from "@vibeorm/schema";
128
+ var ROW_COUNT_HINT = /\b(?:limit|offset)\b/i;
129
+ function isIdentChar(char) {
130
+ return char !== undefined && /[A-Za-z0-9_$]/.test(char);
131
+ }
132
+ function skipQuoted(text, start, quote) {
133
+ let index = start + 1;
134
+ while (index < text.length) {
135
+ const char = text[index];
136
+ if (char === "\\") {
137
+ index += 2;
138
+ continue;
139
+ }
140
+ if (char === quote) {
141
+ if (text[index + 1] === quote) {
142
+ index += 2;
143
+ continue;
144
+ }
145
+ return index + 1;
146
+ }
147
+ index += 1;
148
+ }
149
+ return text.length;
150
+ }
151
+ function skipLineComment(text, start) {
152
+ const newline = text.indexOf(`
153
+ `, start);
154
+ return newline === -1 ? text.length : newline + 1;
155
+ }
156
+ function skipBlockComment(text, start) {
157
+ const close = text.indexOf("*/", start + 2);
158
+ return close === -1 ? text.length : close + 2;
159
+ }
160
+ function scanPlaceholders(params) {
161
+ const { text } = params;
162
+ const rowCountPositions = new Set;
163
+ let count = 0;
164
+ let inRowCount = false;
165
+ let index = 0;
166
+ while (index < text.length) {
167
+ const char = text[index];
168
+ if (char === "'" || char === '"' || char === "`") {
169
+ index = skipQuoted(text, index, char);
170
+ inRowCount = false;
171
+ continue;
172
+ }
173
+ if (char === "-" && text[index + 1] === "-" && (text[index + 2] === undefined || /\s/.test(text[index + 2]))) {
174
+ index = skipLineComment(text, index);
175
+ continue;
176
+ }
177
+ if (char === "#") {
178
+ index = skipLineComment(text, index);
179
+ continue;
180
+ }
181
+ if (char === "/" && text[index + 1] === "*") {
182
+ index = skipBlockComment(text, index);
183
+ continue;
184
+ }
185
+ if (char === "?") {
186
+ if (inRowCount)
187
+ rowCountPositions.add(count);
188
+ count += 1;
189
+ index += 1;
190
+ continue;
191
+ }
192
+ if (/\s/.test(char) || char === ",") {
193
+ index += 1;
194
+ continue;
195
+ }
196
+ if (/[0-9]/.test(char)) {
197
+ while (index < text.length && /[0-9]/.test(text[index]))
198
+ index += 1;
199
+ continue;
200
+ }
201
+ if (/[A-Za-z_$]/.test(char)) {
202
+ const start = index;
203
+ while (isIdentChar(text[index]))
204
+ index += 1;
205
+ const word = text.slice(start, index).toUpperCase();
206
+ inRowCount = word === "LIMIT" || word === "OFFSET";
207
+ continue;
208
+ }
209
+ inRowCount = false;
210
+ index += 1;
211
+ }
212
+ return { count, rowCountPositions };
213
+ }
214
+ function toRowCount(params) {
215
+ const { value, ordinal, text } = params;
216
+ const refuse = (reason) => {
217
+ throw new VibeError3({
218
+ code: "VIBE_VALIDATION",
219
+ message: `parameter $${ordinal} sits in a LIMIT/OFFSET position and ${reason} \u2014 ` + "a row count must be a non-negative integer",
220
+ meta: { placeholder: ordinal, value: String(value), text }
221
+ });
222
+ };
223
+ if (typeof value === "number") {
224
+ if (!Number.isSafeInteger(value))
225
+ return refuse("is not a safe integer");
226
+ if (value < 0)
227
+ return refuse("is negative");
228
+ return String(value);
229
+ }
230
+ if (typeof value === "bigint") {
231
+ if (value < 0n)
232
+ return refuse("is negative");
233
+ return value.toString();
234
+ }
235
+ return value;
236
+ }
237
+ function bindRowCountParams(params) {
238
+ const { text, values } = params;
239
+ if (values.length === 0)
240
+ return values;
241
+ if (!values.some((value) => typeof value === "number" || typeof value === "bigint"))
242
+ return values;
243
+ if (!ROW_COUNT_HINT.test(text))
244
+ return values;
245
+ const scanned = scanPlaceholders({ text });
246
+ if (scanned.rowCountPositions.size === 0 || scanned.count !== values.length)
247
+ return values;
248
+ return values.map((value, index) => scanned.rowCountPositions.has(index) ? toRowCount({ value, ordinal: index + 1, text }) : value);
249
+ }
250
+
126
251
  // packages/adapter-mysql/src/results.ts
127
252
  function isResultSetHeader(params) {
128
253
  const { value } = params;
@@ -152,6 +277,128 @@ function normalizeDriverResult(params) {
152
277
  return normalizeSingle(result);
153
278
  }
154
279
 
280
+ // packages/adapter-mysql/src/transaction-budget.ts
281
+ import { VibeError as VibeError4 } from "@vibeorm/schema";
282
+ var DEFAULT_DEADLINE_CLOCK = () => Date.now();
283
+ function validateTransactionDeadline(params) {
284
+ const { options, support, provider } = params;
285
+ const deadline = options?.deadline;
286
+ if (deadline === undefined)
287
+ return;
288
+ if (support === "unsupported") {
289
+ throw new VibeError4({
290
+ code: "VIBE_UNSUPPORTED_CAPABILITY",
291
+ message: `${provider} cannot enforce a transaction deadline \u2014 remove the \`deadline\` option`,
292
+ meta: { provider, feature: "transactionDeadline", supported: support }
293
+ });
294
+ }
295
+ if (!Number.isInteger(deadline.totalMs) || deadline.totalMs <= 0) {
296
+ throw new VibeError4({
297
+ code: "VIBE_VALIDATION",
298
+ message: `transaction deadline \`totalMs\` must be a positive integer of milliseconds, received ${String(deadline.totalMs)}`,
299
+ meta: { provider, feature: "transactionDeadline", totalMs: deadline.totalMs }
300
+ });
301
+ }
302
+ const requested = deadline.enforcement ?? "between-statements";
303
+ if (requested === "cancel-running-statements" && support !== "cancel-running-statements") {
304
+ throw new VibeError4({
305
+ code: "VIBE_UNSUPPORTED_CAPABILITY",
306
+ message: `${provider} cannot cancel a statement that is already running, so the requested ` + "`cancel-running-statements` deadline guarantee is refused rather than silently downgraded \u2014 " + 'ask for "between-statements" if a budget checked between statements is enough',
307
+ meta: { provider, feature: "transactionDeadline", requested, supported: support }
308
+ });
309
+ }
310
+ }
311
+ function startTransactionBudget(params) {
312
+ const { options, provider } = params;
313
+ const clock = params.clock ?? DEFAULT_DEADLINE_CLOCK;
314
+ const deadline = options?.deadline;
315
+ const totalMs = deadline === undefined ? null : deadline.totalMs;
316
+ const enforcement = deadline === undefined ? null : deadline.enforcement ?? "between-statements";
317
+ const expiresAt = totalMs === null ? null : clock() + totalMs;
318
+ let outcome = null;
319
+ const budget = {
320
+ enforcement,
321
+ totalMs,
322
+ statementTimeoutMs: options?.timeout ?? null,
323
+ remainingMs() {
324
+ return expiresAt === null ? null : Math.max(0, expiresAt - clock());
325
+ },
326
+ expired() {
327
+ return expiresAt !== null && clock() >= expiresAt;
328
+ },
329
+ assertUsable(assertParams) {
330
+ if (outcome !== null) {
331
+ throw transactionClosedError({ provider, outcome, stage: assertParams.stage });
332
+ }
333
+ if (expiresAt !== null && clock() >= expiresAt) {
334
+ throw transactionDeadlineError({
335
+ provider,
336
+ stage: assertParams.stage,
337
+ totalMs,
338
+ overdueMs: clock() - expiresAt
339
+ });
340
+ }
341
+ },
342
+ close(closeParams) {
343
+ if (outcome === null)
344
+ outcome = closeParams.outcome;
345
+ },
346
+ get outcome() {
347
+ return outcome;
348
+ }
349
+ };
350
+ return budget;
351
+ }
352
+ function engineStatementBudgetMs(params) {
353
+ const { budget } = params;
354
+ if (budget.enforcement !== "cancel-running-statements")
355
+ return null;
356
+ const remaining = budget.remainingMs();
357
+ if (remaining === null)
358
+ return null;
359
+ const totalBound = Math.max(1, Math.ceil(remaining));
360
+ return budget.statementTimeoutMs !== null && budget.statementTimeoutMs > 0 ? Math.min(totalBound, budget.statementTimeoutMs) : totalBound;
361
+ }
362
+ function transactionDeadlineError(params) {
363
+ const { provider, stage, totalMs, overdueMs } = params;
364
+ const what = stage === "commit" ? "the transaction was rolled back instead of committed" : "the statement was never sent and the transaction is being rolled back";
365
+ return new VibeError4({
366
+ code: "VIBE_TRANSACTION",
367
+ message: `transaction deadline of ${totalMs} ms expired ${Math.max(0, Math.round(overdueMs))} ms ago \u2014 ${what}. ` + "Nothing is retried automatically: work the callback did outside the database may already have happened.",
368
+ meta: { provider, reason: "transaction-deadline", stage, totalMs, overdueMs: Math.max(0, Math.round(overdueMs)) }
369
+ });
370
+ }
371
+ function transactionClosedError(params) {
372
+ const { provider, outcome, stage } = params;
373
+ return new VibeError4({
374
+ code: "VIBE_TRANSACTION",
375
+ message: `this transaction has already finished (${outcome}), so its client can no longer be used \u2014 ` + "a handle kept past the end of `$transaction` would run outside the transaction, on a connection " + "that now belongs to someone else",
376
+ meta: { provider, reason: "transaction-closed", outcome, stage }
377
+ });
378
+ }
379
+ function transactionOutcomeUnknownError(params) {
380
+ const { provider, totalMs, cause } = params;
381
+ return new VibeError4({
382
+ code: "VIBE_TRANSACTION",
383
+ message: `transaction deadline of ${totalMs} ms expired and the rollback that should have cleaned up failed \u2014 ` + "the commit state of this transaction is UNKNOWN. It is not retried automatically; inspect the data " + "before deciding.",
384
+ meta: {
385
+ provider,
386
+ reason: "transaction-outcome-unknown",
387
+ outcome: "unknown",
388
+ totalMs,
389
+ rollbackFailed: true
390
+ },
391
+ cause
392
+ });
393
+ }
394
+ function isTransactionDeadlineError(error) {
395
+ const reason = error?.meta?.reason;
396
+ return reason === "transaction-deadline";
397
+ }
398
+
399
+ // packages/adapter-mysql/src/savepoint-gate.ts
400
+ import { AsyncLocalStorage } from "async_hooks";
401
+
155
402
  // packages/adapter-mysql/src/savepoints.ts
156
403
  function createSavepointCounter() {
157
404
  return { n: 0 };
@@ -160,8 +407,111 @@ function nextSavepointName(params) {
160
407
  return `vibeorm_sp_${params.counter.n++}`;
161
408
  }
162
409
 
410
+ // packages/adapter-mysql/src/savepoint-gate.ts
411
+ var CURRENT = new AsyncLocalStorage;
412
+ function createSavepointScope() {
413
+ return { depth: 0, state: { counter: createSavepointCounter(), open: null } };
414
+ }
415
+ function runRootScope(params) {
416
+ return CURRENT.run(params.scope, params.body);
417
+ }
418
+ function scopeTurn(params) {
419
+ const open = params.scope.state.open;
420
+ if (mayProceed({ scope: params.scope, open }))
421
+ return;
422
+ return waitForTurn({ scope: params.scope });
423
+ }
424
+ async function runNestedSavepoint(params) {
425
+ const { scope, execute, body } = params;
426
+ const node = await acquire({ scope });
427
+ try {
428
+ if (execute !== undefined)
429
+ await execute(`SAVEPOINT ${node.name}`);
430
+ let result;
431
+ try {
432
+ result = await CURRENT.run(node.scope, () => body(node.scope));
433
+ } catch (error) {
434
+ await drainDescendants({ node });
435
+ if (execute !== undefined) {
436
+ try {
437
+ await execute(`ROLLBACK TO SAVEPOINT ${node.name}`);
438
+ } catch {}
439
+ }
440
+ throw error;
441
+ }
442
+ await drainDescendants({ node });
443
+ if (execute !== undefined)
444
+ await execute(`RELEASE SAVEPOINT ${node.name}`);
445
+ return result;
446
+ } finally {
447
+ closeNode({ node });
448
+ }
449
+ }
450
+ function mayProceed(params) {
451
+ const { scope, open } = params;
452
+ if (open === null)
453
+ return true;
454
+ if (open.scope === scope)
455
+ return true;
456
+ if (open.scope.depth < scope.depth)
457
+ return true;
458
+ return CURRENT.getStore() === open.scope;
459
+ }
460
+ async function waitForTurn(params) {
461
+ for (;; ) {
462
+ const open = params.scope.state.open;
463
+ if (open === null || mayProceed({ scope: params.scope, open }))
464
+ return;
465
+ await open.settled;
466
+ }
467
+ }
468
+ async function acquire(params) {
469
+ const { scope } = params;
470
+ for (;; ) {
471
+ const open = scope.state.open;
472
+ if (mayProceed({ scope, open }))
473
+ return pushNode({ scope, parent: open });
474
+ await open.settled;
475
+ }
476
+ }
477
+ function pushNode(params) {
478
+ const { scope, parent } = params;
479
+ const state = scope.state;
480
+ let settle = () => {
481
+ return;
482
+ };
483
+ const settled = new Promise((resolve) => {
484
+ settle = () => resolve();
485
+ });
486
+ const node = {
487
+ scope: { depth: (parent === null ? 0 : parent.scope.depth) + 1, state },
488
+ name: nextSavepointName({ counter: state.counter }),
489
+ parent,
490
+ settled,
491
+ settle
492
+ };
493
+ state.open = node;
494
+ return node;
495
+ }
496
+ function closeNode(params) {
497
+ const { node } = params;
498
+ const state = node.scope.state;
499
+ if (state.open === node)
500
+ state.open = node.parent;
501
+ node.settle();
502
+ }
503
+ async function drainDescendants(params) {
504
+ const state = params.node.scope.state;
505
+ for (;; ) {
506
+ const open = state.open;
507
+ if (open === null || open === params.node || open.scope.depth <= params.node.scope.depth)
508
+ return;
509
+ await open.settled;
510
+ }
511
+ }
512
+
163
513
  // packages/adapter-mysql/src/transaction-sql.ts
164
- import { VibeError as VibeError3 } from "@vibeorm/schema";
514
+ import { VibeError as VibeError5 } from "@vibeorm/schema";
165
515
  var ISOLATION_LEVEL_SQL = Object.freeze({
166
516
  ReadCommitted: "READ COMMITTED",
167
517
  RepeatableRead: "REPEATABLE READ",
@@ -173,7 +523,7 @@ function setIsolationLevelStatement(params) {
173
523
  function maxExecutionTimeStatement(params) {
174
524
  const { timeout } = params;
175
525
  if (!Number.isInteger(timeout) || timeout < 0) {
176
- throw new VibeError3({
526
+ throw new VibeError5({
177
527
  code: "VIBE_VALIDATION",
178
528
  message: `transaction timeout must be a non-negative integer of milliseconds, received ${String(timeout)}`,
179
529
  meta: { timeout }
@@ -200,6 +550,8 @@ function refuseNestedTransactionOptions(params) {
200
550
  passed.push("isolationLevel");
201
551
  if (options.timeout !== undefined)
202
552
  passed.push("timeout");
553
+ if (options.deadline !== undefined)
554
+ passed.push("deadline");
203
555
  if (passed.length === 0)
204
556
  return;
205
557
  const meta = { provider, nested: true };
@@ -207,9 +559,11 @@ function refuseNestedTransactionOptions(params) {
207
559
  meta.isolationLevel = options.isolationLevel;
208
560
  if (options.timeout !== undefined)
209
561
  meta.timeout = options.timeout;
210
- throw new VibeError3({
562
+ if (options.deadline !== undefined)
563
+ meta.deadline = options.deadline;
564
+ throw new VibeError5({
211
565
  code: "VIBE_VALIDATION",
212
- message: `nested transactions run as savepoints and cannot honor ${passed.join(" or ")} \u2014 a savepoint cannot change the isolation level of the transaction it joins, and a nested timeout is not enforceable; pass options on the top-level $transaction`,
566
+ message: `nested transactions run as savepoints and cannot honor ${passed.join(" or ")} \u2014 a savepoint cannot change the isolation level of the transaction it joins, a nested timeout is not enforceable, and a nested deadline would need its own session setting on a connection it does not own (it inherits the top-level one instead); pass options on the top-level $transaction`,
213
567
  meta
214
568
  });
215
569
  }
@@ -218,6 +572,11 @@ function refuseNestedTransactionOptions(params) {
218
572
  var DIALECT = "mysql";
219
573
  var PROVIDER = "mysql";
220
574
  var WIRE = Object.freeze({ dateTime: "native", json: "parsed", numbers: "native" });
575
+ var BUDGETS = Object.freeze({
576
+ acquireTimeout: true,
577
+ statementTimeout: "select-only",
578
+ transactionDeadline: "between-statements"
579
+ });
221
580
  function defaultCreatePool(config) {
222
581
  return mysql2CreatePool(config);
223
582
  }
@@ -274,36 +633,51 @@ function mysqlAdapter(options) {
274
633
  const [result] = await runMapped(() => params.queryable.execute(params.text, params.values));
275
634
  return normalizeDriverResult({ result }).rows;
276
635
  }
636
+ async function runExecuteAffected(params) {
637
+ const values = bindRowCountParams({ text: params.text, values: params.values });
638
+ const [result] = await runMapped(() => params.queryable.execute(params.text, values));
639
+ return normalizeDriverResult({ result });
640
+ }
277
641
  async function runExecuteUnsafe(params) {
278
642
  const [result] = await runMapped(() => params.queryable.query(params.text, params.values));
279
643
  return normalizeDriverResult({ result });
280
644
  }
281
645
  function createConnectionAdapter(params) {
282
- const { conn, savepointCounter } = params;
646
+ const { conn, scope, budget } = params;
283
647
  return {
284
648
  dialect: DIALECT,
285
649
  provider: PROVIDER,
286
650
  wire: WIRE,
651
+ budgets: BUDGETS,
287
652
  async execute(execParams) {
653
+ const turn = scopeTurn({ scope });
654
+ if (turn !== undefined)
655
+ await turn;
656
+ budget.assertUsable({ stage: "statement" });
288
657
  return runExecute({ queryable: conn, ...execParams });
289
658
  },
290
659
  async executeUnsafe(execParams) {
660
+ const turn = scopeTurn({ scope });
661
+ if (turn !== undefined)
662
+ await turn;
663
+ budget.assertUsable({ stage: "statement" });
291
664
  return runExecuteUnsafe({ queryable: conn, ...execParams });
292
665
  },
666
+ async executeAffected(execParams) {
667
+ const turn = scopeTurn({ scope });
668
+ if (turn !== undefined)
669
+ await turn;
670
+ budget.assertUsable({ stage: "statement" });
671
+ return runExecuteAffected({ queryable: conn, ...execParams });
672
+ },
293
673
  async transaction(fn, nestedOptions) {
294
674
  refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });
295
- const savepoint = nextSavepointName({ counter: savepointCounter });
296
- await runMapped(() => conn.query(`SAVEPOINT ${savepoint}`));
297
- try {
298
- const result = await fn(createConnectionAdapter({ conn, savepointCounter }));
299
- await runMapped(() => conn.query(`RELEASE SAVEPOINT ${savepoint}`));
300
- return result;
301
- } catch (error) {
302
- try {
303
- await conn.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
304
- } catch {}
305
- throw error;
306
- }
675
+ budget.assertUsable({ stage: "nested" });
676
+ return runNestedSavepoint({
677
+ scope,
678
+ execute: (statement) => runMapped(() => conn.query(statement)),
679
+ body: (child) => fn(createConnectionAdapter({ conn, scope: child, budget }))
680
+ });
307
681
  },
308
682
  async connect() {},
309
683
  async disconnect() {},
@@ -316,6 +690,7 @@ function mysqlAdapter(options) {
316
690
  dialect: DIALECT,
317
691
  provider: PROVIDER,
318
692
  wire: WIRE,
693
+ budgets: BUDGETS,
319
694
  async execute(params) {
320
695
  if (acquireTimeoutMs === undefined) {
321
696
  return runExecute({ queryable: getPool(), ...params });
@@ -327,6 +702,17 @@ function mysqlAdapter(options) {
327
702
  conn.release();
328
703
  }
329
704
  },
705
+ async executeAffected(params) {
706
+ if (acquireTimeoutMs === undefined) {
707
+ return runExecuteAffected({ queryable: getPool(), ...params });
708
+ }
709
+ const conn = await acquireConnection();
710
+ try {
711
+ return await runExecuteAffected({ queryable: conn, ...params });
712
+ } finally {
713
+ conn.release();
714
+ }
715
+ },
330
716
  async executeUnsafe(params) {
331
717
  const control = classifyRawTransactionControl({ text: params.text });
332
718
  if (rawSession !== null) {
@@ -364,7 +750,14 @@ function mysqlAdapter(options) {
364
750
  },
365
751
  async transaction(fn, transactionOptions) {
366
752
  const timeoutText = transactionOptions?.timeout === undefined ? undefined : maxExecutionTimeStatement({ timeout: transactionOptions.timeout });
753
+ validateTransactionDeadline({
754
+ options: transactionOptions,
755
+ support: BUDGETS.transactionDeadline,
756
+ provider: PROVIDER
757
+ });
367
758
  const conn = await acquireConnection();
759
+ let rollbackFailed = false;
760
+ const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });
368
761
  try {
369
762
  if (timeoutText !== undefined) {
370
763
  await runMapped(() => conn.query(timeoutText));
@@ -375,19 +768,36 @@ function mysqlAdapter(options) {
375
768
  await runMapped(() => conn.query(isolationText));
376
769
  }
377
770
  await runMapped(() => conn.beginTransaction());
771
+ const scope = createSavepointScope();
378
772
  try {
379
- const result = await fn(createConnectionAdapter({ conn, savepointCounter: createSavepointCounter() }));
773
+ const result = await runRootScope({
774
+ scope,
775
+ body: () => fn(createConnectionAdapter({ conn, scope, budget }))
776
+ });
777
+ budget.assertUsable({ stage: "commit" });
380
778
  await runMapped(() => conn.commit());
779
+ budget.close({ outcome: "committed" });
381
780
  return result;
382
781
  } catch (error) {
383
782
  try {
384
783
  await conn.rollback();
385
- } catch {}
784
+ budget.close({ outcome: "rolled-back" });
785
+ } catch {
786
+ rollbackFailed = true;
787
+ budget.close({ outcome: "unknown" });
788
+ if (budget.totalMs !== null && isTransactionDeadlineError(error)) {
789
+ throw transactionOutcomeUnknownError({
790
+ provider: PROVIDER,
791
+ totalMs: budget.totalMs,
792
+ cause: error
793
+ });
794
+ }
795
+ }
386
796
  throw error;
387
797
  }
388
798
  } finally {
389
- let poisoned = false;
390
- if (timeoutText !== undefined) {
799
+ let poisoned = rollbackFailed;
800
+ if (timeoutText !== undefined && !poisoned) {
391
801
  try {
392
802
  await conn.query(RESET_MAX_EXECUTION_TIME_SQL);
393
803
  } catch {
@@ -430,6 +840,11 @@ function mysqlAdapter(options) {
430
840
  };
431
841
  }
432
842
  export {
843
+ validateTransactionDeadline,
844
+ transactionOutcomeUnknownError,
845
+ transactionDeadlineError,
846
+ transactionClosedError,
847
+ startTransactionBudget,
433
848
  setIsolationLevelStatement,
434
849
  runMapped,
435
850
  refuseNestedTransactionOptions,
@@ -439,7 +854,9 @@ export {
439
854
  mysqlAdapter,
440
855
  maxExecutionTimeStatement,
441
856
  mapMysqlDriverError,
857
+ isTransactionDeadlineError,
442
858
  isResultSetHeader,
859
+ engineStatementBudgetMs,
443
860
  createSavepointCounter,
444
861
  classifyRawTransactionControl,
445
862
  acquireWithTimeout,
@@ -449,7 +866,8 @@ export {
449
866
  MYSQL_ERRNO_ERROR_CODES,
450
867
  MYSQL_DEFAULT_CONNECTION_LIMIT,
451
868
  ISOLATION_LEVEL_SQL,
869
+ DEFAULT_DEADLINE_CLOCK,
452
870
  DEFAULT_ACQUIRE_TIMERS
453
871
  };
454
872
 
455
- //# debugId=CAF3242F05183E7764756E2164756E21
873
+ //# debugId=C68F0F22780DCB5D64756E2164756E21