delete method

Future<void> delete(
  1. Object id, {
  2. DVRecord? base,
  3. String? actor,
  4. String? tenant,
})

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);
  });
}