From bf94057d1bd77ebc254f1307f9bb1b59b028c871 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 20 Oct 2019 11:39:24 +0200 Subject: [PATCH 1/4] Call doWhenOpened for customStatement (#199) --- moor/CHANGELOG.md | 4 ++++ moor/lib/src/runtime/database.dart | 6 +++++- moor/test/custom_queries_test.dart | 6 ++++++ moor/test/data/utils/mocks.dart | 4 ++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/moor/CHANGELOG.md b/moor/CHANGELOG.md index 5e10adbf..5c35f229 100644 --- a/moor/CHANGELOG.md +++ b/moor/CHANGELOG.md @@ -1,3 +1,7 @@ +## unreleased + +- Fix crash when `customStatement` is the first operation used on a database ([#199](https://github.com/simolus3/moor/issues/199)) + ## 2.0.1 - Introduced `isBetween` and `isBetweenValues` methods for comparable expressions (int, double, datetime) diff --git a/moor/lib/src/runtime/database.dart b/moor/lib/src/runtime/database.dart index a12b8111..832ee2b5 100644 --- a/moor/lib/src/runtime/database.dart +++ b/moor/lib/src/runtime/database.dart @@ -270,7 +270,11 @@ mixin QueryEngine on DatabaseConnectionUser { @protected @visibleForTesting Future customStatement(String statement, [List args]) { - return _resolvedEngine.executor.runCustom(statement, args); + final engine = _resolvedEngine; + + return engine.executor.doWhenOpened((executor) { + return executor.runCustom(statement, args); + }); } /// Executes [action] in a transaction, which means that all its queries and diff --git a/moor/test/custom_queries_test.dart b/moor/test/custom_queries_test.dart index 516ddaec..787895d6 100644 --- a/moor/test/custom_queries_test.dart +++ b/moor/test/custom_queries_test.dart @@ -73,4 +73,10 @@ void main() { // shouldn't call stream queries - we didn't set the updates parameter verifyNever(streamQueries.handleTableUpdates(any)); }); + + test('custom statement', () async { + // regression test for https://github.com/simolus3/moor/issues/199 - the + // mock will throw when used before opening + expect(db.customStatement('UPDATE tbl SET a = b'), completes); + }); } diff --git a/moor/test/data/utils/mocks.dart b/moor/test/data/utils/mocks.dart index 2196cb28..c104c9e5 100644 --- a/moor/test/data/utils/mocks.dart +++ b/moor/test/data/utils/mocks.dart @@ -29,6 +29,10 @@ class MockExecutor extends Mock implements QueryExecutor { assert(_opened); return Future.value(0); }); + when(runCustom(any, any)).thenAnswer((_) { + assert(_opened); + return Future.value(0); + }); when(beginTransaction()).thenAnswer((_) { assert(_opened); return transactions; From 71247bd69220a4e37da61b77e2225516960c6a35 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 20 Oct 2019 21:10:23 +0200 Subject: [PATCH 2/4] Release moor 2.0.1+1: Fix for customStatement on open --- moor/CHANGELOG.md | 7 +++---- moor/pubspec.yaml | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/moor/CHANGELOG.md b/moor/CHANGELOG.md index 5c35f229..34ffcb3a 100644 --- a/moor/CHANGELOG.md +++ b/moor/CHANGELOG.md @@ -1,7 +1,3 @@ -## unreleased - -- Fix crash when `customStatement` is the first operation used on a database ([#199](https://github.com/simolus3/moor/issues/199)) - ## 2.0.1 - Introduced `isBetween` and `isBetweenValues` methods for comparable expressions (int, double, datetime) @@ -11,6 +7,9 @@ - Fix streams not emitting cached data when listening multiple times - __Breaking__: Remove the type parameter from `Insertable.createCompanion` (it was declared as an internal method) + +__2.0.1+1__: Fix crash when `customStatement` is the first operation used on a database +([#199](https://github.com/simolus3/moor/issues/199)) ## 2.0.0 This is the first major update after the initial release and moor and we have a lot to cover: diff --git a/moor/pubspec.yaml b/moor/pubspec.yaml index abdf1ec8..ac55236f 100644 --- a/moor/pubspec.yaml +++ b/moor/pubspec.yaml @@ -1,6 +1,6 @@ name: moor description: Moor is a safe and reactive persistence library for Dart applications -version: 2.0.1 +version: 2.0.1+1 repository: https://github.com/simolus3/moor homepage: https://moor.simonbinder.eu/ issue_tracker: https://github.com/simolus3/moor/issues @@ -28,8 +28,8 @@ dev_dependencies: coverage: any # will be determined by test_coverage test_coverage: ^0.3.0 -dependency_overrides: - moor_generator: - path: ../moor_generator - sqlparser: - path: ../sqlparser \ No newline at end of file +#dependency_overrides: +# moor_generator: +# path: ../moor_generator +# sqlparser: +# path: ../sqlparser \ No newline at end of file From b0a9255a075530b0f8ccc4ad6ee68a3d913b5867 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 27 Oct 2019 16:52:46 +0100 Subject: [PATCH 3/4] Update documentation on migrations --- .../en/docs/Advanced Features/migrations.md | 24 ++++++++++++------- docs/content/en/docs/faq.md | 7 ++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/docs/content/en/docs/Advanced Features/migrations.md b/docs/content/en/docs/Advanced Features/migrations.md index 48917c7e..31d12f15 100644 --- a/docs/content/en/docs/Advanced Features/migrations.md +++ b/docs/content/en/docs/Advanced Features/migrations.md @@ -51,17 +51,17 @@ to run the statements. Starting from moor 1.5, you can use the `beforeOpen` parameter in the `MigrationStrategy` which will be called after migrations, but after any other queries are run. You could use it to populate data after the database has been created: ```dart -beforeOpen: (db, details) async { +beforeOpen: (details) async { if (details.wasCreated) { - final workId = await db.into(categories).insert(Category(description: 'Work')); + final workId = await into(categories).insert(Category(description: 'Work')); - await db.into(todos).insert(TodoEntry( + await into(todos).insert(TodoEntry( content: 'A first todo entry', category: null, targetDate: DateTime.now(), )); - await db.into(todos).insert( + await into(todos).insert( TodoEntry( content: 'Rework persistence code', category: workId, @@ -72,12 +72,20 @@ beforeOpen: (db, details) async { ``` You could also activate pragma statements that you need: ```dart -beforeOpen: (db, details) async { +beforeOpen: (details) async { if (details.wasCreated) { // ... } - await db.customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA foreign_keys = ON'); } ``` -It is important that you run these queries on `db` explicitly. Failing to do so causes a deadlock which prevents the -database from being opened. \ No newline at end of file + +## During development + +During development, you might be changing your schema very often and don't want to write migrations for that +yet. You can just delete your apps' data and reinstall the app - the database will be deleted and all tables +will be created again. Please note that uninstalling is not enough sometimes - Android might have backed up +the database file and will re-create it when installing the app again. + +You can also delete and re-create all tables everytime your app is opened, see [this comment](https://github.com/simolus3/moor/issues/188#issuecomment-542682912) +on how that can be achieved. \ No newline at end of file diff --git a/docs/content/en/docs/faq.md b/docs/content/en/docs/faq.md index f969741e..5f4a3f38 100644 --- a/docs/content/en/docs/faq.md +++ b/docs/content/en/docs/faq.md @@ -41,6 +41,13 @@ If you're strict on keeping your business logic out of the widget layer, you pro framework like `kiwi` or `get_it` to instantiate services and view models. Creating a singleton instance of `MyDatabase` in your favorite dependency injection framework for flutter hence solves this problem for you. +## Why am I getting no such table errors? + +If you add another table after your app has already been installed, you need to write a [migration]({{< relref "Advanced Features/migrations.md" >}}) +that covers creating that table. If you're in the process of developing your app and want to use un- and reinstall your app +instead of writing migrations, that's fine too. Please note that your apps data might be backed up on Android, so +manually deleting your app's data instead of a reinstall is necessary on some devices. + ## How does moor compare to X? There are a variety of good persistence libraries for Dart and Flutter. From 067212e66c9e63832b053ce3739382a47bc26102 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sat, 9 Nov 2019 16:34:01 +0100 Subject: [PATCH 4/4] Make streams emit errors when they can't fetch data (#233) --- moor/lib/src/runtime/executor/stream_queries.dart | 15 +++++++++++---- moor/test/streams_test.dart | 9 +++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/moor/lib/src/runtime/executor/stream_queries.dart b/moor/lib/src/runtime/executor/stream_queries.dart index 4f2479bf..0a06e09f 100644 --- a/moor/lib/src/runtime/executor/stream_queries.dart +++ b/moor/lib/src/runtime/executor/stream_queries.dart @@ -194,11 +194,18 @@ class QueryStream { // Fetch data if it's needed, publish that data if it's possible. if (!_controller.hasListener) return; - final data = await _fetcher.fetchData(); - _lastData = data; + T data; - if (!_controller.isClosed) { - _controller.add(data); + try { + data = await _fetcher.fetchData(); + _lastData = data; + if (!_controller.isClosed) { + _controller.add(data); + } + } catch (e, s) { + if (!_controller.isClosed) { + _controller.addError(e, s); + } } } } diff --git a/moor/test/streams_test.dart b/moor/test/streams_test.dart index 81055c87..37349204 100644 --- a/moor/test/streams_test.dart +++ b/moor/test/streams_test.dart @@ -141,6 +141,15 @@ void main() { verify(executor.runSelect(any, any)).called(2); }); + test('stream emits error when loading the query throws', () { + final exception = Exception('stub'); + when(executor.runSelect(any, any)) + .thenAnswer((_) => Future.error(exception)); + + final result = db.customSelectQuery('select 1').watch().first; + expectLater(result, throwsA(exception)); + }); + group('stream keys', () { final keyA = StreamKey('SELECT * FROM users;', [], User); final keyB = StreamKey('SELECT * FROM users;', [], User);