guard method
Future<MssqlWriteOutcome>
guard({
- required String operation,
- required int expected,
- required Future<
MssqlWriteOutcome> write(- MssqlWriteEngine<
TRow> engine
- MssqlWriteEngine<
Runs write and insists it affected exactly expected rows.
A guard that only threw would be worse than none: the write would still
be in the database, and the caller would have an exception saying it
changed the wrong number of rows and no way to know it was kept. So the
write runs inside a transaction it can be rolled back with — a savepoint
when the caller already has one open, following the nesting contract in
MssqlTransaction.savepoint, and a transaction of its own when it does
not.
The one case that cannot be made safe is a session this layer does not
recognise, such as a hand-written MssqlSession implementation: there is
no way to open a transaction on it, so the write is reported as kept
rather than quietly claimed to be undone.
Implementation
Future<MssqlWriteOutcome> guard({
required String operation,
required int expected,
required Future<MssqlWriteOutcome> Function(MssqlWriteEngine<TRow> engine)
write,
}) async {
final inner = _unwrapped(session);
if (inner is MssqlTransaction) {
return inner.savepoint(() => _checked(this, operation, expected, write));
}
if (inner is MssqlConnection) {
MssqlTransaction? tx;
try {
final result = await inner.transaction((transaction) {
tx = transaction;
return _checked(
withSession(_rewrapped(transaction)),
operation,
expected,
write,
);
});
if (tx != null) changes?.commitPending(tx);
return result;
} catch (_) {
if (tx != null) changes?.discardPending(tx);
rethrow;
}
}
// A pooled session leases a different connection per command, so there is
// nothing here to open a transaction on: the guard's read of the count
// could land on another connection than the write. Saying the write was
// kept is the only true answer.
final outcome = await write(this);
if (outcome.affectedRows == expected) return outcome;
throw MssqlAffectedRowsException(
table: binding.qualifiedName,
operation: operation,
expected: expected,
actual: outcome.affectedRows,
);
}