Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throws last core error when beginWriteAsync and commitWriteAsync #974

Merged
merged 14 commits into from
Dec 12, 2022
Merged
2 changes: 1 addition & 1 deletion lib/src/cli/metrics/metrics.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 16 additions & 6 deletions lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,17 @@ class _RealmCore {
return LastError(error.ref.error, message, userError);
}

bool clearLastError() {
return _realmLib.realm_clear_last_error();
}

void throwLastError([String? errorMessage]) {
using((Arena arena) {
final lastError = getLastError(arena);
if (errorMessage == null && lastError == null) return;
if (lastError?.userError != null) {
throw UserCallbackException(lastError!.userError!);
}

throw RealmException('${errorMessage != null ? "$errorMessage. " : ""}${lastError ?? ""}');
});
}
Expand Down Expand Up @@ -586,8 +590,8 @@ class _RealmCore {
Future<void> beginWriteAsync(Realm realm, CancellationToken? ct) {
final completer = WriteCompleter(realm, ct);
if (!completer.isCancelled) {
completer.id = _realmLib.realm_async_begin_write(realm.handle._pointer, Pointer.fromFunction(_completeAsyncBeginWrite), completer.toPersistentHandle(),
_realmLib.addresses.realm_dart_delete_persistent_handle, true);
_realmLib.invokeGetInt(() => completer.id = _realmLib.realm_async_begin_write(realm.handle._pointer, Pointer.fromFunction(_completeAsyncBeginWrite),
completer.toPersistentHandle(), _realmLib.addresses.realm_dart_delete_persistent_handle, true));
}

return completer.future;
Expand All @@ -596,8 +600,8 @@ class _RealmCore {
Future<void> commitWriteAsync(Realm realm, CancellationToken? ct) {
final completer = WriteCompleter(realm, ct);
if (!completer.isCancelled) {
completer.id = _realmLib.realm_async_commit(realm.handle._pointer, Pointer.fromFunction(_completeAsyncCommit), completer.toPersistentHandle(),
_realmLib.addresses.realm_dart_delete_persistent_handle, false);
_realmLib.invokeGetInt(() => completer.id = _realmLib.realm_async_commit(realm.handle._pointer, Pointer.fromFunction(_completeAsyncCommit),
completer.toPersistentHandle(), _realmLib.addresses.realm_dart_delete_persistent_handle, false));
}

return completer.future;
Expand Down Expand Up @@ -1848,7 +1852,7 @@ class _RealmCore {
return;
}
if (errorCode != nullptr) {
// Throw RealmException instead of RealmError to be recoverable by the user.
// Throw RealmException instead of RealmError to be recoverable by the user.
completer.completeError(RealmException(errorCode.toSyncError().toString()));
} else {
completer.complete();
Expand Down Expand Up @@ -2378,6 +2382,12 @@ extension _RealmLibraryEx on RealmLibrary {
}
return result;
}

void invokeGetInt(int Function() callback, [String? errorMessage]) {
nirinchev marked this conversation as resolved.
Show resolved Hide resolved
realmCore.clearLastError();
callback();
realmCore.throwLastError(errorMessage);
}
}

Pointer<realm_value_t> _toRealmValue(Object? value, Allocator allocator) {
Expand Down
10 changes: 0 additions & 10 deletions lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,6 @@ class Realm implements Finalizable {

/// Begins a write transaction for this [Realm].
Transaction beginWrite() {
_ensureWritable();

realmCore.beginWrite(this);

return Transaction._(this);
Expand All @@ -313,8 +311,6 @@ class Realm implements Finalizable {
/// Asynchronously begins a write transaction for this [Realm]. You can supply a
/// [CancellationToken] to cancel the operation.
Future<Transaction> beginWriteAsync([CancellationToken? cancellationToken]) async {
_ensureWritable();

await realmCore.beginWriteAsync(this, cancellationToken);

return Transaction._(this);
Expand Down Expand Up @@ -474,12 +470,6 @@ class Realm implements Finalizable {
throw RealmError('Cannot $operation because the object is managed by another Realm instance');
}
}

void _ensureWritable() {
blagoev marked this conversation as resolved.
Show resolved Hide resolved
if (isFrozen) {
throw RealmError('Starting a write transaction on a frozen Realm is not allowed.');
}
}
}

/// Provides a scope to safely write data to a [Realm]. Can be created using [Realm.beginWrite] or
Expand Down
5 changes: 3 additions & 2 deletions test/configuration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,15 @@ Future<void> main([List<String>? args]) async {
expect(cars.length, 1);
});

test('Configuration readOnly - writing on read-only Realms throws', () {
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
test('Configuration readOnly - writing on read-only Realms throws', () async {
Configuration config = Configuration.local([Car.schema]);
var realm = getRealm(config);
realm.close();

config = Configuration.local([Car.schema], isReadOnly: true);
realm = getRealm(config);
expect(() => realm.write(() {}), throws<RealmException>("Can't perform transactions on read-only Realms."));
realm.writesExpectException<RealmException>("Can't perform transactions on read-only Realms.");
await realm.asyncWritesExpectException<RealmException>("Can't perform transactions on read-only Realms.");
});

test('Configuration inMemory - no files after closing realm', () {
Expand Down
44 changes: 9 additions & 35 deletions test/realm_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,15 @@ Future<void> main([List<String>? args]) async {
expect(realm.write(() => realm.add(carTwo, update: true)), carTwo);
});

test('Realm write after realm is closed', () {
test('Realm write after realm is closed', () async {
var config = Configuration.local([Car.schema]);
var realm = getRealm(config);

final car = Car('Tesla');

realm.close();
expect(() => realm.write(() {}), throws<RealmClosedError>("Cannot access realm that has been closed"));
realm.writesExpectException<RealmClosedError>("Cannot access realm that has been closed");
await realm.asyncWritesExpectException<RealmClosedError>("Cannot access realm that has been closed");
});

test('Realm query', () {
Expand Down Expand Up @@ -574,12 +575,12 @@ Future<void> main([List<String>? args]) async {
expect(returnedCar, car);
});

test('Realm write inside another write throws', () {
test('Realm write inside another write throws', () async {
nirinchev marked this conversation as resolved.
Show resolved Hide resolved
final config = Configuration.local([Car.schema]);
final realm = getRealm(config);
realm.write(() {
realm.write(() async {
nirinchev marked this conversation as resolved.
Show resolved Hide resolved
// Second write inside the first one fails but the error is caught
expect(() => realm.write(() {}), throws<RealmException>('The Realm is already in a write transaction'));
realm.writesExpectException<RealmException>("The Realm is already in a write transaction");
});
});

Expand Down Expand Up @@ -717,36 +718,13 @@ Future<void> main([List<String>? args]) async {
expect(realm.all<Person>().length, 0);
});

test('FrozenRealm cannot write', () {
final config = Configuration.local([Person.schema]);
final realm = getRealm(config);

final frozenRealm = freezeRealm(realm);
expect(() => frozenRealm.write(() {}), throws<RealmError>("Starting a write transaction on a frozen Realm is not allowed."));
});

test('FrozenRealm cannot beginWrite', () {
test('FrozenRealm cannot write', () async {
final config = Configuration.local([Person.schema]);
final realm = getRealm(config);

final frozenRealm = freezeRealm(realm);
expect(() => frozenRealm.beginWrite(), throws<RealmError>("Starting a write transaction on a frozen Realm is not allowed."));
});

test('FrozenRealm cannot writeAsync', () async {
final config = Configuration.local([Person.schema]);
final realm = getRealm(config);

final frozenRealm = freezeRealm(realm);
await expectLater(() => frozenRealm.writeAsync(() {}), throws<RealmError>("Starting a write transaction on a frozen Realm is not allowed."));
});

test('FrozenRealm cannot beginWriteAsync', () async {
final config = Configuration.local([Person.schema]);
final realm = getRealm(config);

final frozenRealm = freezeRealm(realm);
await expectLater(() => frozenRealm.beginWriteAsync(), throws<RealmError>("Starting a write transaction on a frozen Realm is not allowed."));
frozenRealm.writesExpectException<RealmException>("Can't perform transactions on a frozen Realm");
await frozenRealm.asyncWritesExpectException<RealmException>("Can't perform transactions on a frozen Realm");
});

test('realm.freeze when frozen returns the same instance', () {
Expand Down Expand Up @@ -1016,7 +994,6 @@ Future<void> main([List<String>? args]) async {
test('Realm.beginWriteAsync starts write transaction', () async {
final realm = getRealm(Configuration.local([Person.schema]));
final transaction = await realm.beginWriteAsync();

expect(transaction.isOpen, true);
});

Expand All @@ -1025,7 +1002,6 @@ Future<void> main([List<String>? args]) async {
final transaction = await realm.beginWriteAsync();
realm.add(Person('John'));
transaction.commit();

expect(realm.all<Person>().length, 1);
});

Expand All @@ -1034,7 +1010,6 @@ Future<void> main([List<String>? args]) async {
final transaction = await realm.beginWriteAsync();
realm.add(Person('John'));
await transaction.commitAsync();

expect(realm.all<Person>().length, 1);
});

Expand All @@ -1052,7 +1027,6 @@ Future<void> main([List<String>? args]) async {
final transaction = realm.beginWrite();
realm.add(Person('John'));
await transaction.commitAsync();

expect(realm.all<Person>().length, 1);
});

Expand Down
12 changes: 12 additions & 0 deletions test/test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,15 @@ Future<void> _printPlatformInfo() async {

print('Current PID $pid; OS $os, $pointerSize bit, CPU ${cpu ?? 'unknown'}');
}

extension $WriteExtension on Realm {
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
void writesExpectException<TException>(String exceptionMessage) {
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
desistefanova marked this conversation as resolved.
Show resolved Hide resolved
expect(() => write(() {}), throws<TException>(exceptionMessage));
expect(() => beginWrite(), throws<TException>(exceptionMessage));
}

Future<void> asyncWritesExpectException<TException>(String exceptionMessage) async {
await expectLater(() => writeAsync(() {}), throws<TException>(exceptionMessage));
await expectLater(() => beginWriteAsync(), throws<TException>(exceptionMessage));
}
}