withSavepoint<T> method

Future<T> withSavepoint<T>(
  1. String name,
  2. Future<T> action()
)

Runs action within a savepoint named name. On success the savepoint is released; on any thrown exception we rollback to the savepoint, then rethrow so the caller can decide what to do with the surrounding transaction.

This is the recommended way to do partial-rollback inside a longer transaction.

Implementation

Future<T> withSavepoint<T>(
  String name,
  Future<T> Function() action,
) async {
  if (!createSavepoint(name)) {
    throw StateError('Failed to create savepoint "$name" on txn $_txnId');
  }
  try {
    final result = await action();
    if (!releaseSavepoint(name)) {
      throw StateError(
        'Failed to release savepoint "$name" on txn $_txnId',
      );
    }
    return result;
  } on Object {
    if (!rollbackToSavepoint(name)) {
      // Rollback to savepoint failed; the transaction may be in an
      // inconsistent state. The caller will rethrow the original cause
      // and should decide whether to rollback the entire transaction.
      throw StateError(
        'Failed to rollback to savepoint "$name" on txn $_txnId '
        '(original error is suppressed — check structured error channel)',
      );
    }
    // We do not auto-release after rollback: SQL-92 keeps the savepoint
    // alive after ROLLBACK TO, and SQL Server has no RELEASE at all.
    rethrow;
  }
}