diff --git a/example/pubspec.lock b/example/pubspec.lock index 47bb897f..a94ae19a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -28,7 +28,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -196,7 +196,7 @@ packages: path: "../floor" relative: true source: path - version: "1.0.1" + version: "1.1.0" floor_annotation: dependency: transitive description: @@ -210,7 +210,7 @@ packages: path: "../floor_generator" relative: true source: path - version: "1.0.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -372,7 +372,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" sqflite: dependency: transitive description: @@ -449,7 +449,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" timing: dependency: transitive description: diff --git a/floor/lib/floor.dart b/floor/lib/floor.dart index f60f694f..c6a8c3fd 100644 --- a/floor/lib/floor.dart +++ b/floor/lib/floor.dart @@ -8,5 +8,6 @@ export 'package:floor/src/adapter/update_adapter.dart'; export 'package:floor/src/callback.dart'; export 'package:floor/src/database.dart'; export 'package:floor/src/migration.dart'; +export 'package:floor/src/automigration/auto_migration.dart'; export 'package:floor/src/sqflite_database_factory.dart'; export 'package:floor_annotation/floor_annotation.dart'; diff --git a/floor/lib/src/adapter/migration_adapter.dart b/floor/lib/src/adapter/migration_adapter.dart index efc813c0..77596a47 100644 --- a/floor/lib/src/adapter/migration_adapter.dart +++ b/floor/lib/src/adapter/migration_adapter.dart @@ -15,13 +15,14 @@ abstract class MigrationAdapter { ..sort((first, second) => first.startVersion.compareTo(second.startVersion)); + /* if (relevantMigrations.isEmpty || relevantMigrations.last.endVersion != endVersion) { throw StateError( 'There is no migration supplied to update the database to the current version.' ' Aborting the migration.', ); - } + }*/ for (final migration in relevantMigrations) { await migration.migrate(migrationDatabase); diff --git a/floor/lib/src/adapter/query_adapter.dart b/floor/lib/src/adapter/query_adapter.dart index d2bbdb11..9d7698a8 100644 --- a/floor/lib/src/adapter/query_adapter.dart +++ b/floor/lib/src/adapter/query_adapter.dart @@ -17,9 +17,12 @@ class QueryAdapter { Future query( final String sql, { final List? arguments, + final bool isRaw = false, required final T Function(Map) mapper, }) async { - final rows = await _database.rawQuery(sql, arguments); + final rows = await (isRaw + ? _database.rawQuery(sql.replaceAll('?1', arguments![0] as String)) + : _database.rawQuery(sql, arguments)); if (rows.isEmpty) { return null; @@ -34,20 +37,26 @@ class QueryAdapter { Future> queryList( final String sql, { final List? arguments, + final bool isRaw = false, required final T Function(Map) mapper, }) async { - final rows = await _database.rawQuery(sql, arguments); + final rows = await (isRaw + ? _database.rawQuery(sql.replaceAll('?1', arguments![0] as String)) + : _database.rawQuery(sql, arguments)); return rows.map((row) => mapper(row)).toList(); } Future queryNoReturn( final String sql, { final List? arguments, + final bool isRaw = false, }) async { // TODO #94 differentiate between different query kinds (select, update, delete, insert) // this enables to notify the observers // also requires extracting the table name :( - await _database.rawQuery(sql, arguments); + await (isRaw + ? _database.rawQuery(sql.replaceAll('?1', arguments![0] as String)) + : _database.rawQuery(sql, arguments)); } /// Executes a SQLite query that returns a stream of single query results diff --git a/floor/lib/src/automigration/auto_migration.dart b/floor/lib/src/automigration/auto_migration.dart new file mode 100644 index 00000000..747f237f --- /dev/null +++ b/floor/lib/src/automigration/auto_migration.dart @@ -0,0 +1,15 @@ +import 'package:sqflite/sqlite_api.dart'; + +class AutoMigration { + static Future migrate( + final Database database, + List createStatements + ) async { + /// Auto create new tables + for (String statement in createStatements) { + await database.execute(statement); + } + + /// TODO: Auto check new columns + } +} \ No newline at end of file diff --git a/floor/pubspec.lock b/floor/pubspec.lock index a76ad813..a9a1274e 100644 --- a/floor/pubspec.lock +++ b/floor/pubspec.lock @@ -28,7 +28,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -203,7 +203,7 @@ packages: path: "../floor_generator" relative: true source: path - version: "1.0.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -372,7 +372,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" sqflite: dependency: "direct main" description: @@ -449,7 +449,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" timing: dependency: transitive description: diff --git a/floor_annotation/lib/src/query.dart b/floor_annotation/lib/src/query.dart index d078b470..5668e641 100644 --- a/floor_annotation/lib/src/query.dart +++ b/floor_annotation/lib/src/query.dart @@ -3,6 +3,8 @@ class Query { /// The SQLite query. final String value; + final bool? isRaw; + /// Marks a method as a query method. - const Query(this.value); + const Query(this.value, {this.isRaw}); } diff --git a/floor_generator/lib/misc/constants.dart b/floor_generator/lib/misc/constants.dart index 689c1f7e..0fd88ec7 100644 --- a/floor_generator/lib/misc/constants.dart +++ b/floor_generator/lib/misc/constants.dart @@ -1,5 +1,6 @@ abstract class AnnotationField { static const queryValue = 'value'; + static const isRaw = 'isRaw'; static const primaryKeyAutoGenerate = 'autoGenerate'; static const onConflict = 'onConflict'; diff --git a/floor_generator/lib/processor/query_method_processor.dart b/floor_generator/lib/processor/query_method_processor.dart index 08fcbef0..280d6464 100644 --- a/floor_generator/lib/processor/query_method_processor.dart +++ b/floor_generator/lib/processor/query_method_processor.dart @@ -38,6 +38,7 @@ class QueryMethodProcessor extends Processor { final rawReturnType = _methodElement.returnType; final query = QueryProcessor(_methodElement, _getQuery()).process(); + final isRaw = _getIsRaw(); _getQuery(); final returnsStream = rawReturnType.isStream; @@ -85,6 +86,7 @@ class QueryMethodProcessor extends Processor { parameters, queryable, allTypeConverters, + isRaw: isRaw, ); } @@ -99,6 +101,15 @@ class QueryMethodProcessor extends Processor { return query; } + bool _getIsRaw() { + final isRaw = _methodElement + .getAnnotation(annotations.Query)! + .getField(AnnotationField.isRaw) + ?.toBoolValue(); + + return isRaw ?? false; + } + DartType _getFlattenedReturnType( final DartType rawReturnType, final bool returnsStream, diff --git a/floor_generator/lib/value_object/query_method.dart b/floor_generator/lib/value_object/query_method.dart index 39645eae..4e46da22 100644 --- a/floor_generator/lib/value_object/query_method.dart +++ b/floor_generator/lib/value_object/query_method.dart @@ -17,6 +17,8 @@ class QueryMethod { /// Query where the parameter mapping is stored. final Query query; + final bool? isRaw; + final DartType rawReturnType; /// Flattened return type. @@ -36,15 +38,15 @@ class QueryMethod { final Set typeConverters; QueryMethod( - this.methodElement, - this.name, - this.query, - this.rawReturnType, - this.flattenedReturnType, - this.parameters, - this.queryable, - this.typeConverters, - ); + this.methodElement, + this.name, + this.query, + this.rawReturnType, + this.flattenedReturnType, + this.parameters, + this.queryable, + this.typeConverters, + {this.isRaw}); bool get returnsList { final type = returnsStream diff --git a/floor_generator/lib/writer/database_writer.dart b/floor_generator/lib/writer/database_writer.dart index abb4a0b6..11173e26 100644 --- a/floor_generator/lib/writer/database_writer.dart +++ b/floor_generator/lib/writer/database_writer.dart @@ -70,16 +70,16 @@ class DatabaseWriter implements Writer { Method _generateOpenMethod(final Database database) { final createTableStatements = _generateCreateTableSqlStatements(database.entities) - .map((statement) => "await database.execute('$statement');") + .map((statement) => "'$statement',") .join('\n'); final createIndexStatements = database.entities .map((entity) => entity.indices.map((index) => index.createQuery())) .expand((statements) => statements) - .map((statement) => "await database.execute('$statement');") + .map((statement) => "'$statement',") .join('\n'); final createViewStatements = database.views .map((view) => view.getCreateViewStatement()) - .map((statement) => "await database.execute('''$statement''');") + .map((statement) => "'''$statement'''") .join('\n'); final pathParameter = Parameter((builder) => builder @@ -99,6 +99,18 @@ class DatabaseWriter implements Writer { ..requiredParameters.addAll([pathParameter, migrationsParameter]) ..optionalParameters.add(callbackParameter) ..body = Code(''' + final List _createTableStatements = [ + $createTableStatements + ]; + + final List _createIndexStatements = [ + $createIndexStatements + ]; + + final List _createViewStatements = [ + $createViewStatements + ]; + final databaseOptions = sqflite.OpenDatabaseOptions( version: ${database.version}, onConfigure: (database) async { @@ -111,12 +123,17 @@ class DatabaseWriter implements Writer { onUpgrade: (database, startVersion, endVersion) async { await MigrationAdapter.runMigrations(database, startVersion, endVersion, migrations); + await AutoMigration.migrate(database, _createTableStatements); + await callback?.onUpgrade?.call(database, startVersion, endVersion); }, onCreate: (database, version) async { - $createTableStatements - $createIndexStatements - $createViewStatements + Future.wait(_createTableStatements + .map((statement) async => await database.execute(statement))); + Future.wait(_createIndexStatements + .map((statement) async => await database.execute(statement))); + Future.wait(_createViewStatements + .map((statement) async => await database.execute(statement))); await callback?.onCreate?.call(database, version); }, diff --git a/floor_generator/lib/writer/query_method_writer.dart b/floor_generator/lib/writer/query_method_writer.dart index bb42de25..f75451d7 100644 --- a/floor_generator/lib/writer/query_method_writer.dart +++ b/floor_generator/lib/writer/query_method_writer.dart @@ -163,6 +163,7 @@ class QueryMethodWriter implements Writer { String _generateNoReturnQuery(final String query, final String? arguments) { final parameters = StringBuffer(query); if (arguments != null) parameters.write(', arguments: $arguments'); + if (_queryMethod.isRaw!) parameters.write(', isRaw: true'); return 'await _queryAdapter.queryNoReturn($parameters);'; } @@ -174,6 +175,7 @@ class QueryMethodWriter implements Writer { final mapper = _generateMapper(queryable); final parameters = StringBuffer(query)..write(', mapper: $mapper'); if (arguments != null) parameters.write(', arguments: $arguments'); + if (_queryMethod.isRaw!) parameters.write(', isRaw: true'); if (_queryMethod.returnsStream) { // for streamed queries, we need to provide the queryable to know which