transaction<R> method
Runs action inside a transaction, with a new TSelf over the
transaction's session.
Every table getter inside action sees the same transaction session, so
a read and a write in the same callback go to the same physical lease.
Three cases, and none of them silently runs action without a
transaction:
- A connection-backed database opens one on that connection.
- A withPool database takes one lease and opens the transaction on it, so the whole callback runs on one physical connection.
- A database already forked over an
MssqlTransactionnests through a savepoint, so a failure insideactionrolls backaction's writes and leaves the outer transaction to its own scope.
A session that is none of those — a bare MssqlSession implementation
that cannot hold a lease — is refused rather than run unprotected.
Implementation
Future<R> transaction<R>(Future<R> Function(TSelf db) action) async {
final s = session;
if (s is MssqlTransaction) {
// Nested: the inner scope is a savepoint of the open transaction, not
// a second transaction and not an unprotected run. Rolling back to the
// savepoint undoes [action] and nothing the caller did before it.
return s.savepoint<R>(() => action(this as TSelf));
}
if (s is MssqlConnection) {
return _openOn<R>(action, (callback) => s.transaction(callback));
}
final borrowed = pool;
if (borrowed != null) {
return _openOn<R>(action, (callback) => borrowed.transaction(callback));
}
throw StateError(
'This AppDatabase runs on a session that cannot open a transaction: '
'${s.runtimeType}. Build it with AppDatabase.borrow(connection), '
'AppDatabase.withPool(pool) or AppDatabase.open(config). Running the '
'callback without a transaction would look like it worked and leave '
'every statement in it separately committed.',
);
}