From 12d2fc4d76db714f0793d868813078d19feee173 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sat, 22 May 2021 16:49:20 +0200 Subject: [PATCH 01/10] Fix deserializing nullable types (#1211) --- moor/lib/src/runtime/data_class.dart | 4 +- moor/test/data_class_test.dart | 88 +++++++++++++++------------- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/moor/lib/src/runtime/data_class.dart b/moor/lib/src/runtime/data_class.dart index d8324892..3a34bc22 100644 --- a/moor/lib/src/runtime/data_class.dart +++ b/moor/lib/src/runtime/data_class.dart @@ -173,13 +173,13 @@ class _DefaultValueSerializer extends ValueSerializer { return DateTime.fromMillisecondsSinceEpoch(json as int) as T; } - if (_typeList is List && json is int) { + if (_typeList is List && json is int) { return json.toDouble() as T; } // blobs are encoded as a regular json array, so we manually convert that to // a Uint8List - if (_typeList is List && json is! Uint8List) { + if (_typeList is List && json is! Uint8List) { final asList = (json as List).cast(); return Uint8List.fromList(asList) as T; } diff --git a/moor/test/data_class_test.dart b/moor/test/data_class_test.dart index 60def7da..48db4d92 100644 --- a/moor/test/data_class_test.dart +++ b/moor/test/data_class_test.dart @@ -18,54 +18,62 @@ void main() { expect(deserialized, equals(deserialized)); }); - test('can deserialize ints as doubles', () { + group('default serializer', () { const serializer = ValueSerializer.defaults(); + test('can deserialize ints as doubles', () { + expect(serializer.fromJson(3), 3.0); + }); - expect(serializer.fromJson(3), 3.0); - }); + test('can deserialize non-null values with nullable types', () { + expect(serializer.fromJson(3), 3.0); + expect(serializer.fromJson(0), + DateTime.fromMillisecondsSinceEpoch(0)); + expect(serializer.fromJson([0, 1]), [0, 1]); + }); - test('default serializer can be overridden globally', () { - final old = moorRuntimeOptions.defaultSerializer; - moorRuntimeOptions.defaultSerializer = _MySerializer(); + test('can be overridden globally', () { + final old = moorRuntimeOptions.defaultSerializer; + moorRuntimeOptions.defaultSerializer = _MySerializer(); - final entry = TodoEntry( - id: 13, - title: 'Title', - content: 'Content', - category: 3, - targetDate: DateTime.now(), - ); - expect( - entry.toJson(), - { - 'id': 'foo', - 'title': 'foo', - 'content': 'foo', - 'category': 'foo', - 'target_date': 'foo', - }, - ); + final entry = TodoEntry( + id: 13, + title: 'Title', + content: 'Content', + category: 3, + targetDate: DateTime.now(), + ); + expect( + entry.toJson(), + { + 'id': 'foo', + 'title': 'foo', + 'content': 'foo', + 'category': 'foo', + 'target_date': 'foo', + }, + ); - moorRuntimeOptions.defaultSerializer = old; - }); + moorRuntimeOptions.defaultSerializer = old; + }); - test('can serialize and deserialize blob columns', () { - final user = User( - id: 3, - name: 'Username', - isAwesome: true, - profilePicture: Uint8List.fromList([1, 2, 3, 4]), - creationTime: DateTime.now(), - ); + test('can serialize and deserialize blob columns', () { + final user = User( + id: 3, + name: 'Username', + isAwesome: true, + profilePicture: Uint8List.fromList([1, 2, 3, 4]), + creationTime: DateTime.now(), + ); - final recovered = User.fromJsonString(user.toJsonString()); + final recovered = User.fromJsonString(user.toJsonString()); - // Note: Some precision is lost when serializing DateTimes, so we're using - // custom expects instead of expect(recovered, user) - expect(recovered.id, user.id); - expect(recovered.name, user.name); - expect(recovered.isAwesome, user.isAwesome); - expect(recovered.profilePicture, user.profilePicture); + // Note: Some precision is lost when serializing DateTimes, so we're using + // custom expects instead of expect(recovered, user) + expect(recovered.id, user.id); + expect(recovered.name, user.name); + expect(recovered.isAwesome, user.isAwesome); + expect(recovered.profilePicture, user.profilePicture); + }); }); test('generated data classes can be converted to companions', () { From 81424c488bbdb2ef2dcc21ae6e93859ca586391a Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 21 May 2021 22:24:00 +0200 Subject: [PATCH 02/10] Fix encoding updates without kind over isolates --- moor/lib/src/runtime/remote/protocol.dart | 4 +++- moor/test/isolate_test.dart | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/moor/lib/src/runtime/remote/protocol.dart b/moor/lib/src/runtime/remote/protocol.dart index ad13b9ee..cb3c7c73 100644 --- a/moor/lib/src/runtime/remote/protocol.dart +++ b/moor/lib/src/runtime/remote/protocol.dart @@ -205,9 +205,11 @@ class MoorProtocol { final updates = []; for (var i = 1; i < fullMessage!.length; i++) { final encodedUpdate = fullMessage[i] as List; + final kindIndex = encodedUpdate[1] as int?; + updates.add( TableUpdate(encodedUpdate[0] as String, - kind: UpdateKind.values[encodedUpdate[1] as int]), + kind: kindIndex == null ? null : UpdateKind.values[kindIndex]), ); } return NotifyTablesUpdated(updates); diff --git a/moor/test/isolate_test.dart b/moor/test/isolate_test.dart index f7834e0e..f1bcdae8 100644 --- a/moor/test/isolate_test.dart +++ b/moor/test/isolate_test.dart @@ -238,6 +238,13 @@ void _runTests( await database.customSelect('SELECT title FROM sample').get(); expect(result.map((f) => f.read('title')), ["O'Connor", "Tomeo's"]); }); + + test('can dispatch table updates', () async { + await database.customStatement('SELECT 1'); + expect(database.tableUpdates(TableUpdateQuery.onTable(database.users)), + emitsInOrder([null])); + database.markTablesUpdated({database.users}); + }); } DatabaseConnection _backgroundConnection() { From 63de86404acf2dec24d3ada49d9c04d14b685cd0 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 21 May 2021 11:01:26 +0200 Subject: [PATCH 03/10] Fix code generation for custom data classes --- moor/example/example.g.dart | 16 ++++++------ moor/test/data/tables/custom_tables.g.dart | 26 +++++++++---------- moor/test/data/tables/todos.g.dart | 22 ++++++++-------- .../moor_files_integration_test.dart | 8 ++++++ .../lib/src/writer/tables/table_writer.dart | 14 +++++----- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/moor/example/example.g.dart b/moor/example/example.g.dart index 0e5ce71a..9fec3b9e 100644 --- a/moor/example/example.g.dart +++ b/moor/example/example.g.dart @@ -191,8 +191,8 @@ class $CategoriesTable extends Categories Set get $primaryKey => {id}; @override Category map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return Category.fromData(data, _db, prefix: effectivePrefix); + return Category.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -466,8 +466,8 @@ class $RecipesTable extends Recipes with TableInfo<$RecipesTable, Recipe> { Set get $primaryKey => {id}; @override Recipe map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return Recipe.fromData(data, _db, prefix: effectivePrefix); + return Recipe.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -698,8 +698,8 @@ class $IngredientsTable extends Ingredients Set get $primaryKey => {id}; @override Ingredient map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return Ingredient.fromData(data, _db, prefix: effectivePrefix); + return Ingredient.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -944,8 +944,8 @@ class $IngredientInRecipesTable extends IngredientInRecipes Set get $primaryKey => {recipe, ingredient}; @override IngredientInRecipe map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return IngredientInRecipe.fromData(data, _db, prefix: effectivePrefix); + return IngredientInRecipe.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override diff --git a/moor/test/data/tables/custom_tables.g.dart b/moor/test/data/tables/custom_tables.g.dart index 3faef096..56db6627 100644 --- a/moor/test/data/tables/custom_tables.g.dart +++ b/moor/test/data/tables/custom_tables.g.dart @@ -278,8 +278,8 @@ class ConfigTable extends Table with TableInfo { Set get $primaryKey => {configKey}; @override Config map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return Config.fromData(data, _db, prefix: effectivePrefix); + return Config.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -467,8 +467,8 @@ class WithDefaults extends Table with TableInfo { Set get $primaryKey => {}; @override WithDefault map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return WithDefault.fromData(data, _db, prefix: effectivePrefix); + return WithDefault.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -557,7 +557,7 @@ class NoIds extends Table with TableInfo { Set get $primaryKey => {payload}; @override NoIdRow map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return NoIdRow( const BlobType() .mapFromDatabaseResponse(data['${effectivePrefix}payload'])!, @@ -782,8 +782,8 @@ class WithConstraints extends Table Set get $primaryKey => {}; @override WithConstraint map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return WithConstraint.fromData(data, _db, prefix: effectivePrefix); + return WithConstraint.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -1060,8 +1060,8 @@ class Mytable extends Table with TableInfo { Set get $primaryKey => {someid}; @override MytableData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return MytableData.fromData(data, _db, prefix: effectivePrefix); + return MytableData.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -1286,8 +1286,8 @@ class Email extends Table Set get $primaryKey => {}; @override EMail map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return EMail.fromData(data, _db, prefix: effectivePrefix); + return EMail.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -1477,8 +1477,8 @@ class WeirdTable extends Table with TableInfo { Set get $primaryKey => {}; @override WeirdData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return WeirdData.fromData(data, _db, prefix: effectivePrefix); + return WeirdData.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override diff --git a/moor/test/data/tables/todos.g.dart b/moor/test/data/tables/todos.g.dart index 596dde54..23a7ba35 100644 --- a/moor/test/data/tables/todos.g.dart +++ b/moor/test/data/tables/todos.g.dart @@ -321,8 +321,8 @@ class $TodosTableTable extends TodosTable Set get $primaryKey => {id}; @override TodoEntry map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return TodoEntry.fromData(data, _db, prefix: effectivePrefix); + return TodoEntry.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -546,8 +546,8 @@ class $CategoriesTable extends Categories Set get $primaryKey => {id}; @override Category map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return Category.fromData(data, _db, prefix: effectivePrefix); + return Category.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -861,8 +861,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> { Set get $primaryKey => {id}; @override User map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return User.fromData(data, _db, prefix: effectivePrefix); + return User.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -1055,8 +1055,8 @@ class $SharedTodosTable extends SharedTodos Set get $primaryKey => {todo, user}; @override SharedTodo map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return SharedTodo.fromData(data, _db, prefix: effectivePrefix); + return SharedTodo.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override @@ -1204,7 +1204,7 @@ class $TableWithoutPKTable extends TableWithoutPK Set get $primaryKey => {}; @override CustomRowClass map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return CustomRowClass.map( const IntType() .mapFromDatabaseResponse(data['${effectivePrefix}not_really_an_id'])!, @@ -1367,8 +1367,8 @@ class $PureDefaultsTable extends PureDefaults Set get $primaryKey => {txt}; @override PureDefault map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; - return PureDefault.fromData(data, _db, prefix: effectivePrefix); + return PureDefault.fromData(data, _db, + prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override diff --git a/moor/test/integration_tests/moor_files_integration_test.dart b/moor/test/integration_tests/moor_files_integration_test.dart index 516311ab..2a5d97e9 100644 --- a/moor/test/integration_tests/moor_files_integration_test.dart +++ b/moor/test/integration_tests/moor_files_integration_test.dart @@ -27,6 +27,14 @@ void main() { await expectLater(db.nullableQuery().getSingle(), completion(isNull)); }); + test('can select to existing data classes', () async { + await db + .into(db.noIds) + .insert(NoIdsCompanion.insert(payload: Uint8List(12))); + final result = await db.select(db.noIds).getSingle(); + expect(result.payload, hasLength(12)); + }); + group('views', () { test('can be selected from', () { return expectLater(db.readView().get(), completion(isEmpty)); diff --git a/moor_generator/lib/src/writer/tables/table_writer.dart b/moor_generator/lib/src/writer/tables/table_writer.dart index ad68f1cd..d8568278 100644 --- a/moor_generator/lib/src/writer/tables/table_writer.dart +++ b/moor_generator/lib/src/writer/tables/table_writer.dart @@ -121,13 +121,13 @@ class TableWriter { final dataClassName = table.dartTypeName; - _buffer - ..write('@override\n$dataClassName map(Map data, ' - '{${scope.nullableType('String')} tablePrefix}) {\n') - ..write('final effectivePrefix = ' - "tablePrefix != null ? '\$tablePrefix.' : null;"); + _buffer.write('@override\n$dataClassName map(Map data, ' + '{${scope.nullableType('String')} tablePrefix}) {\n'); if (table.hasExistingRowClass) { + _buffer.write('final effectivePrefix = ' + "tablePrefix != null ? '\$tablePrefix.' : '';"); + final info = table.existingRowClass; final positionalToIndex = {}; final named = {}; @@ -165,8 +165,8 @@ class TableWriter { _buffer.write(';\n'); } else { // Use default .fromData constructor in the moor-generated data class - _buffer.write('return $dataClassName.fromData' - '(data, _db, prefix: effectivePrefix);\n'); + _buffer.write('return $dataClassName.fromData(data, _db, ' + "prefix: tablePrefix != null ? '\$tablePrefix.' : null);\n"); } _buffer.write('}\n'); From 893b95e7e924bbe45322dff6faa21e9d87a835dd Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 24 May 2021 12:52:42 +0200 Subject: [PATCH 04/10] Prepare to release moor/moor_generator 4.3.1 --- moor/CHANGELOG.md | 5 +++++ moor/pubspec.yaml | 2 +- moor_generator/CHANGELOG.md | 4 ++++ moor_generator/pubspec.yaml | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/moor/CHANGELOG.md b/moor/CHANGELOG.md index bf8488d8..c97f39a9 100644 --- a/moor/CHANGELOG.md +++ b/moor/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.3.1 + +- Fix encoding table updates without a kind over isolates +- Fix deserializing some nullable types in the default value serializer + ## 4.3.0 - Support custom, existing classes for rows! See the `@UseRowClass` annotation diff --git a/moor/pubspec.yaml b/moor/pubspec.yaml index 822e8a19..b0ddfef5 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: 4.3.0 +version: 4.3.1 repository: https://github.com/simolus3/moor homepage: https://moor.simonbinder.eu/ issue_tracker: https://github.com/simolus3/moor/issues diff --git a/moor_generator/CHANGELOG.md b/moor_generator/CHANGELOG.md index 428f2b06..d33d95b6 100644 --- a/moor_generator/CHANGELOG.md +++ b/moor_generator/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.3.1 + +- Fix code generation for custom data classes + ## 4.3.0 - Generate non-nullable nested result classes when possible diff --git a/moor_generator/pubspec.yaml b/moor_generator/pubspec.yaml index 3adab39f..2d7f5fa4 100644 --- a/moor_generator/pubspec.yaml +++ b/moor_generator/pubspec.yaml @@ -1,6 +1,6 @@ name: moor_generator description: Dev-dependency to generate table and dataclasses together with the moor package. -version: 4.3.0 +version: 4.3.1 repository: https://github.com/simolus3/moor homepage: https://moor.simonbinder.eu/ issue_tracker: https://github.com/simolus3/moor/issues From 2a8b0fff26cc2781bc910ddd27c1a69005510c71 Mon Sep 17 00:00:00 2001 From: Filip Petrovic Date: Mon, 24 May 2021 13:29:07 -0700 Subject: [PATCH 05/10] Update advanced_dart_tables.md --- docs/pages/docs/Getting started/advanced_dart_tables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/docs/Getting started/advanced_dart_tables.md b/docs/pages/docs/Getting started/advanced_dart_tables.md index 0f708bb6..487ddde5 100644 --- a/docs/pages/docs/Getting started/advanced_dart_tables.md +++ b/docs/pages/docs/Getting started/advanced_dart_tables.md @@ -65,7 +65,7 @@ an exception will be thrown. When using sql, moor also warns about that at compi If you do want to make a column nullable, just use `nullable()`: ```dart class Items { - IntColumn get category => integer().nullable(); + IntColumn get category => integer().nullable()(); // ... } ``` From 65c4efed9bd4402242d6ae310b83397d9ab84576 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 26 May 2021 20:37:58 +0200 Subject: [PATCH 06/10] Fix error with the latest analyzer --- moor_generator/lib/src/backends/common/base_plugin.dart | 2 +- moor_generator/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/moor_generator/lib/src/backends/common/base_plugin.dart b/moor_generator/lib/src/backends/common/base_plugin.dart index b04105c7..c8cef82c 100644 --- a/moor_generator/lib/src/backends/common/base_plugin.dart +++ b/moor_generator/lib/src/backends/common/base_plugin.dart @@ -53,7 +53,7 @@ abstract class BaseMoorPlugin extends ServerPlugin { pathContext: resourceProvider.pathContext, )..optionsFilePath = contextRoot.optionsFile; - final builder = ContextBuilder(resourceProvider, sdkManager, null) + final builder = ContextBuilder(resourceProvider, sdkManager) ..analysisDriverScheduler = dartScheduler ..byteStore = byteStore ..performanceLog = performanceLog; diff --git a/moor_generator/pubspec.yaml b/moor_generator/pubspec.yaml index 2d7f5fa4..74fade34 100644 --- a/moor_generator/pubspec.yaml +++ b/moor_generator/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: sqlparser: ^0.16.0 # Dart analysis - analyzer: "^1.5.0" + analyzer: "^1.7.1" analyzer_plugin_fork: "^0.5.0" source_span: ^1.5.5 package_config: ^2.0.0 From 35e72a78d24424fa97da54edbeddaeccdd1fdc19 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 26 May 2021 20:43:09 +0200 Subject: [PATCH 07/10] Prepare to publish moor_generator 4.3.2 --- moor_generator/CHANGELOG.md | 4 ++++ moor_generator/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/moor_generator/CHANGELOG.md b/moor_generator/CHANGELOG.md index d33d95b6..36b64dec 100644 --- a/moor_generator/CHANGELOG.md +++ b/moor_generator/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.3.2 + +- Support the latest analyzer version + ## 4.3.1 - Fix code generation for custom data classes diff --git a/moor_generator/pubspec.yaml b/moor_generator/pubspec.yaml index 74fade34..96254907 100644 --- a/moor_generator/pubspec.yaml +++ b/moor_generator/pubspec.yaml @@ -1,6 +1,6 @@ name: moor_generator description: Dev-dependency to generate table and dataclasses together with the moor package. -version: 4.3.1 +version: 4.3.2 repository: https://github.com/simolus3/moor homepage: https://moor.simonbinder.eu/ issue_tracker: https://github.com/simolus3/moor/issues From 6aa8c47d6ad089eaf4006ca930e6231d765a5d20 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Tue, 25 May 2021 18:45:35 +0200 Subject: [PATCH 08/10] Merge pull request #1219 from kuhnroyal/bugfix/insert-returning-stream-update Fix insertReturning stream update --- .github/workflows/main.yml | 41 ++++++++++++------- .../query_builder/statements/insert.dart | 2 + moor/test/insert_test.dart | 26 ++++++++++++ .../insert_integration_test.dart | 8 ++-- .../moor_files_integration_test.dart | 7 +--- moor/test/skips.dart | 5 +++ 6 files changed, 64 insertions(+), 25 deletions(-) create mode 100644 moor/test/skips.dart diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ca0cd840..f664e529 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,25 +9,35 @@ jobs: moor: name: "moor package" runs-on: ubuntu-20.04 - + defaults: + run: + working-directory: moor steps: # setup - uses: actions/checkout@v2 - uses: cedx/setup-dart@v2 - - run: sudo apt-get install -y libsqlite3-dev - name: Install sqlite3 for tests + - name: Install sqlite3 for tests + run: | + mkdir sqlite + cd sqlite + curl https://sqlite.org/2021/sqlite-autoconf-3350500.tar.gz --output sqlite.tar.gz + tar zxvf sqlite.tar.gz + cd sqlite-autoconf-3350500 + ./configure + make + sudo make install + echo "/usr/local/lib" >> $GITHUB_PATH + echo "LD_LIBRARY_PATH=`pwd`/.libs" >> $GITHUB_ENV + - name: Check sqlite3 version + run: sqlite3 --version - run: dart pub upgrade - working-directory: moor # analysis - - run: dart format -o none --set-exit-if-changed moor/ + - run: dart format -o none --set-exit-if-changed . name: dartfmt - run: dart analyze --fatal-infos --fatal-warnings - working-directory: moor/ # build, test and upload coverage - run: dart run build_runner build --delete-conflicting-outputs - working-directory: moor - run: dart test #-x background_isolate --coverage=coverage - working-directory: moor # - uses: actions/upload-artifact@v2 # with: # name: moor-coverage-data @@ -38,20 +48,24 @@ jobs: moor_generator: runs-on: ubuntu-20.04 + defaults: + run: + working-directory: moor_generator steps: # setup - uses: actions/checkout@v2 - uses: cedx/setup-dart@v2 - run: dart pub upgrade - working-directory: moor_generator # analysis - - run: dart format -o none --set-exit-if-changed moor_generator/ + - run: dart format -o none --set-exit-if-changed . name: dartfmt - run: dart analyze --fatal-infos --fatal-warnings - working-directory: moor_generator/ sqlparser: runs-on: ubuntu-20.04 + defaults: + run: + working-directory: sqlparser steps: # setup - uses: actions/checkout@v2 @@ -59,15 +73,12 @@ jobs: - run: sudo apt-get install -y libsqlite3-dev name: Install sqlite3 for tests - run: dart pub upgrade - working-directory: sqlparser # analysis - - run: dart format -o none --set-exit-if-changed sqlparser/ + - run: dart format -o none --set-exit-if-changed . name: dartfmt - run: dart analyze --fatal-infos --fatal-warnings - working-directory: sqlparser/ # tests and coverage - run: dart test #--coverage=coverage - working-directory: sqlparser # - uses: actions/upload-artifact@v2 # with: # name: sqlparser-coverage-data diff --git a/moor/lib/src/runtime/query_builder/statements/insert.dart b/moor/lib/src/runtime/query_builder/statements/insert.dart index 754cbf19..b69f65e8 100644 --- a/moor/lib/src/runtime/query_builder/statements/insert.dart +++ b/moor/lib/src/runtime/query_builder/statements/insert.dart @@ -87,6 +87,8 @@ class InsertStatement { return database.doWhenOpened((e) async { final result = await e.runSelect(ctx.sql, ctx.boundVariables); + database + .notifyUpdates({TableUpdate.onTable(table, kind: UpdateKind.insert)}); return table.map(result.single); }); } diff --git a/moor/test/insert_test.dart b/moor/test/insert_test.dart index f4ef2f3e..b8421bbf 100644 --- a/moor/test/insert_test.dart +++ b/moor/test/insert_test.dart @@ -4,6 +4,7 @@ import 'package:test/test.dart'; import 'data/tables/todos.dart'; import 'data/utils/mocks.dart'; +import 'skips.dart'; void main() { late TodoDb db; @@ -69,6 +70,31 @@ void main() { {const TableUpdate('users', kind: UpdateKind.insert)})); }); + test('notifies stream queries on insertReturning', () async { + when(executor.runSelect(any, any)).thenAnswer((_) { + return Future.value([ + { + 'id': 5, + 'name': 'User McUserface', + 'is_awesome': true, + 'profile_picture': Uint8List(0), + 'creation_time': DateTime.now().millisecondsSinceEpoch, + } + ]); + }); + + final user = await db.into(db.users).insertReturning(UsersCompanion( + name: const Value('User McUserface'), + isAwesome: const Value(true), + profilePicture: Value(Uint8List(0)), + )); + + verify(streamQueries.handleTableUpdates( + {const TableUpdate('users', kind: UpdateKind.insert)})); + + expect(user.id, 5); + }, skip: onNoReturningSupport()); + group('enforces integrity', () { test('for regular inserts', () async { InvalidDataException exception; diff --git a/moor/test/integration_tests/insert_integration_test.dart b/moor/test/integration_tests/insert_integration_test.dart index 98faabf9..2c100eed 100644 --- a/moor/test/integration_tests/insert_integration_test.dart +++ b/moor/test/integration_tests/insert_integration_test.dart @@ -1,16 +1,14 @@ +import 'package:moor/ffi.dart'; @TestOn('vm') import 'package:moor/moor.dart'; -import 'package:moor/ffi.dart'; -import 'package:sqlite3/sqlite3.dart'; import 'package:test/test.dart'; import '../data/tables/todos.dart'; +import '../skips.dart'; void main() { late TodoDb db; - final supportsReturning = sqlite3.version.versionNumber > 3035000; - setUp(() { db = TodoDb(VmDatabase.memory()); }); @@ -40,5 +38,5 @@ void main() { priority: CategoryPriority.low, ), ); - }, skip: supportsReturning ? null : 'RETURNING is not supported'); + }, skip: onNoReturningSupport()); } diff --git a/moor/test/integration_tests/moor_files_integration_test.dart b/moor/test/integration_tests/moor_files_integration_test.dart index 2a5d97e9..12be56dd 100644 --- a/moor/test/integration_tests/moor_files_integration_test.dart +++ b/moor/test/integration_tests/moor_files_integration_test.dart @@ -1,10 +1,10 @@ import 'package:moor/ffi.dart'; import 'package:moor/moor.dart' hide isNull; -import 'package:sqlite3/sqlite3.dart'; import 'package:test/test.dart'; import '../data/tables/converter.dart'; import '../data/tables/custom_tables.dart'; +import '../skips.dart'; void main() { late VmDatabase executor; @@ -70,9 +70,6 @@ void main() { }); }); - final sqliteVersion = sqlite3.version; - final hasReturning = sqliteVersion.versionNumber > 3035000; - group('returning', () { test('for custom inserts', () async { final result = await db.addConfig( @@ -94,5 +91,5 @@ void main() { ), ); }); - }, skip: hasReturning ? null : 'RETURNING not supported by current sqlite'); + }, skip: onNoReturningSupport()); } diff --git a/moor/test/skips.dart b/moor/test/skips.dart new file mode 100644 index 00000000..1ed0e6f1 --- /dev/null +++ b/moor/test/skips.dart @@ -0,0 +1,5 @@ +import 'package:sqlite3/sqlite3.dart'; + +String? onNoReturningSupport() => sqlite3.version.versionNumber > 3035000 + ? null + : 'RETURNING not supported by sqlite version ${sqlite3.version.libVersion}'; From 04caf09abb309765dda49419578b5211a560f9cf Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 28 May 2021 21:59:47 +0200 Subject: [PATCH 09/10] Fix lock not returning futures in right order --- moor/lib/src/utils/synchronized.dart | 8 +++-- .../integration_tests/regress_1232_test.dart | 32 +++++++++++++++++++ moor/test/utils/synchronized_test.dart | 7 +++- 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 moor/test/integration_tests/regress_1232_test.dart diff --git a/moor/lib/src/utils/synchronized.dart b/moor/lib/src/utils/synchronized.dart index b4e52d52..aebe8b6b 100644 --- a/moor/lib/src/utils/synchronized.dart +++ b/moor/lib/src/utils/synchronized.dart @@ -8,11 +8,13 @@ class Lock { /// then calls [block] before further [synchronized] calls are allowed. Future synchronized(FutureOr Function() block) { final previous = _last; - // We can use synchronous futures for _last since we always complete through - // callBlockAndComplete(), which is asynchronous. - final blockCompleted = Completer.sync(); + // This controller may not be sync: It must complete just after + // callBlockAndComplete completes. + final blockCompleted = Completer(); _last = blockCompleted.future; + // Note: We can't use async/await here because this future must complete + // just before the blockCompleted completer. Future callBlockAndComplete() async { try { return await block(); diff --git a/moor/test/integration_tests/regress_1232_test.dart b/moor/test/integration_tests/regress_1232_test.dart new file mode 100644 index 00000000..6702e0f4 --- /dev/null +++ b/moor/test/integration_tests/regress_1232_test.dart @@ -0,0 +1,32 @@ +@TestOn('vm') +import 'package:moor/ffi.dart'; +import 'package:moor/moor.dart'; +import 'package:test/test.dart'; + +import '../data/tables/todos.dart'; + +void main() { + test('regression test for #1232', () async { + // replace with generated table + final db = TodoDb(VmDatabase.memory()); + final someTables = {db.todosTable}; + + await db.customStatement('create table tbl (x int)'); + await db.customInsert('insert into tbl values(1)'); + + Stream watchValue() => db + .customSelect('select * from tbl', readsFrom: someTables) + .map((row) => row.read('x')) + .watchSingle(); + + expect(await watchValue().first, 1); + await Future.delayed(Duration.zero); + + watchValue().listen(null); + + await db.customUpdate('update tbl set x = 2', + updates: someTables, updateKind: UpdateKind.update); + + expect(await watchValue().first, 2); + }); +} diff --git a/moor/test/utils/synchronized_test.dart b/moor/test/utils/synchronized_test.dart index 74c90663..531fdd01 100644 --- a/moor/test/utils/synchronized_test.dart +++ b/moor/test/utils/synchronized_test.dart @@ -5,9 +5,14 @@ void main() { test('synchronized runs code in sequence', () async { final lock = Lock(); var i = 0; - final futures = List.generate(100, (index) => lock.synchronized(() => i++)); + final completionOrder = []; + final futures = List.generate( + 100, + (index) => lock.synchronized(() => i++) + ..whenComplete(() => completionOrder.add(index))); final results = await Future.wait(futures); expect(results, List.generate(100, (index) => index)); + expect(completionOrder, List.generate(100, (index) => index)); }); } From ac30bb598946b0fbfb9e278e73cf9451c01e237c Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 28 May 2021 22:05:34 +0200 Subject: [PATCH 10/10] Prepare to release moor 4.3.2 --- moor/CHANGELOG.md | 6 ++++++ moor/lib/src/utils/synchronized.dart | 2 -- moor/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/moor/CHANGELOG.md b/moor/CHANGELOG.md index c97f39a9..99a9f11e 100644 --- a/moor/CHANGELOG.md +++ b/moor/CHANGELOG.md @@ -1,3 +1,9 @@ +## 4.3.2 + +- Fix `insertReturning` not updating streams +- Fix streams emitting stale data if a new subscriber attaches immediately + after an update. + ## 4.3.1 - Fix encoding table updates without a kind over isolates diff --git a/moor/lib/src/utils/synchronized.dart b/moor/lib/src/utils/synchronized.dart index aebe8b6b..d5f3648f 100644 --- a/moor/lib/src/utils/synchronized.dart +++ b/moor/lib/src/utils/synchronized.dart @@ -13,8 +13,6 @@ class Lock { final blockCompleted = Completer(); _last = blockCompleted.future; - // Note: We can't use async/await here because this future must complete - // just before the blockCompleted completer. Future callBlockAndComplete() async { try { return await block(); diff --git a/moor/pubspec.yaml b/moor/pubspec.yaml index b0ddfef5..c67c10b1 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: 4.3.1 +version: 4.3.2 repository: https://github.com/simolus3/moor homepage: https://moor.simonbinder.eu/ issue_tracker: https://github.com/simolus3/moor/issues