delete method
Deletes the record: marks it when softDelete, removes it otherwise.
With base, the record as this writer read it, the delete is refused
when the row has moved since, as a write is. Either way it applies only
to the row at the version the delete itself read: one rewritten in
between is refused with DVConflictError rather than reported deleted.
Implementation
Future<void> delete(Object id,
{DVRecord? base, String? actor, String? tenant}) async {
tenant = _tenant(tenant);
final DVRecord? current = await read(id, withDeleted: true);
if (current == null) return;
if (versioned && base != null && base.version != current.version) {
throw DVConflictError(
table: table,
key: id,
mine: const <String, Object?>{},
theirs: current.values,
base: base.values,
expectedVersion: base.version,
actualVersion: current.version,
);
}
if (softDelete) {
if (current.deletedAt != null) return;
await _update(current, current.values,
deletedAt: _now(), actor: actor, tenant: tenant, deleted: true);
return;
}
final int affected = await database.execute(
'DELETE FROM $table WHERE $key = ? AND $versionColumn = ?$_scopeAnd',
<Object?>[id, current.version, ..._scopeParams],
);
if (affected != 1) {
// Another write moved the row after it was read. Logging and capturing
// a deletion now would record one that did not happen.
final DVRecord? now = await read(id, withDeleted: true);
throw DVConflictError(
table: table,
key: id,
mine: const <String, Object?>{},
theirs: now?.values ?? const <String, Object?>{},
base: current.values,
expectedVersion: current.version,
actualVersion: now?.version ?? -1,
);
}
final String? entry;
try {
entry = await _log(
id,
current.version + 1,
_diff(current.values, const <String, Object?>{}),
actor: actor,
tenant: tenant,
deleted: true,
);
} catch (error) {
await _insertRow(current);
throw DVHistoryWriteError(table: table, key: id, cause: error);
}
try {
await capture?.record(
table: this,
operation: DVCaptureOp.delete,
key: id,
version: current.version + 1,
values: const <String, Object?>{},
tenant: tenant,
);
} catch (error) {
await _insertRow(current);
await _unlog(entry);
throw DVCaptureWriteError(table: table, key: id, cause: error);
}
DVTransactionRunner.activeContext?.compensate(() async {
await _insertRow(current);
await _unlog(entry);
});
}