diff --git a/drift_dev/lib/src/analysis/driver/state.dart b/drift_dev/lib/src/analysis/driver/state.dart index 812df221..3d3a98b7 100644 --- a/drift_dev/lib/src/analysis/driver/state.dart +++ b/drift_dev/lib/src/analysis/driver/state.dart @@ -7,6 +7,7 @@ import '../results/database.dart'; import '../results/element.dart'; import '../results/file_results.dart'; import '../results/query.dart'; +import 'driver.dart'; import 'error.dart'; class FileState { @@ -18,6 +19,8 @@ class FileState { final Map analysis = {}; FileAnalysisResult? fileAnalysis; + bool? _needsModularAccessor; + FileState(this.ownUri); String get extension => url.extension(ownUri.path); @@ -27,15 +30,6 @@ class FileState { return analyzedElements.any((e) => e is BaseDriftAccessor); } - /// Whether an accessor class making queries and imports available should be - /// written for this file if modular analysis is enabled. - bool get hasModularDriftAccessor { - final hasImports = discovery?.importDependencies.isNotEmpty == true; - final hasQuery = analyzedElements.any((e) => e is DefinedSqlQuery); - - return hasImports || hasQuery; - } - /// All analyzed [DriftElement]s found in this library. @visibleForTesting Iterable get analyzedElements { @@ -66,6 +60,23 @@ class FileState { bool elementIsAnalyzed(DriftElementId id) { return analysis[id]?.isUpToDate == true; } + + bool get _definesQuery { + return analyzedElements.any((e) => e is DefinedSqlQuery); + } + + /// Whether an accessor class making queries and imports available should be + /// written for this file if modular analysis is enabled. + /// + /// This is the case if this accessor defines queries or if it transitively + /// imports a modular accessor. + bool needsModularAccessor(DriftAnalysisDriver driver) { + if (_needsModularAccessor != null) return _needsModularAccessor!; + + return _needsModularAccessor = driver.cache.crawl(this).any((e) { + return e._definesQuery; + }); + } } abstract class DiscoveredFileState { diff --git a/drift_dev/lib/src/analysis/results/query.dart b/drift_dev/lib/src/analysis/results/query.dart index 2f36af47..c1fdb431 100644 --- a/drift_dev/lib/src/analysis/results/query.dart +++ b/drift_dev/lib/src/analysis/results/query.dart @@ -840,9 +840,6 @@ class AvailableDriftResultSet { AvailableDriftResultSet(this.name, this.entity, [this.source]); - /// The argument type of this result set when used in a scoped function. - String get argumentType => entity.entityInfoName; - @override int get hashCode => Object.hash(name, entity); diff --git a/drift_dev/lib/src/backends/build/drift_builder.dart b/drift_dev/lib/src/backends/build/drift_builder.dart index 01cd1c5c..548d03c2 100644 --- a/drift_dev/lib/src/backends/build/drift_builder.dart +++ b/drift_dev/lib/src/backends/build/drift_builder.dart @@ -262,7 +262,8 @@ class _DriftBuildRun { } else if (result is DriftDatabase) { final resolved = entrypointState.fileAnalysis!.resolvedDatabases[result.id]!; - final input = DatabaseGenerationInput(result, resolved, const {}); + final input = + DatabaseGenerationInput(result, resolved, const {}, driver); DatabaseWriter(input, writer.child()).write(); // Also write stubs for known custom functions so that the user can @@ -271,14 +272,13 @@ class _DriftBuildRun { } else if (result is DatabaseAccessor) { final resolved = entrypointState.fileAnalysis!.resolvedDatabases[result.id]!; - final input = AccessorGenerationInput(result, resolved, const {}); + final input = + AccessorGenerationInput(result, resolved, const {}, driver); AccessorWriter(input, writer.child()).write(); } } - if (entrypointState.hasModularDriftAccessor) { - ModularAccessorWriter(writer.child(), entrypointState).write(); - } + ModularAccessorWriter(writer.child(), entrypointState, driver).write(); } Future _generateMonolithic(FileState entrypointState) async { @@ -320,12 +320,12 @@ class _DriftBuildRun { .map((k, v) => MapEntry(k, mappedQueries[v] ?? v)); if (result is DriftDatabase) { - final input = - DatabaseGenerationInput(result, resolved, importedQueries); + final input = DatabaseGenerationInput( + result, resolved, importedQueries, driver); DatabaseWriter(input, writer.child()).write(); } else if (result is DatabaseAccessor) { - final input = - AccessorGenerationInput(result, resolved, importedQueries); + final input = AccessorGenerationInput( + result, resolved, importedQueries, driver); AccessorWriter(input, writer.child()).write(); } } diff --git a/drift_dev/lib/src/cli/commands/schema/generate_utils.dart b/drift_dev/lib/src/cli/commands/schema/generate_utils.dart index 8a8b892a..a00e4a98 100644 --- a/drift_dev/lib/src/cli/commands/schema/generate_utils.dart +++ b/drift_dev/lib/src/cli/commands/schema/generate_utils.dart @@ -140,7 +140,7 @@ class GenerateUtilsCommand extends Command { ); final resolved = ResolvedDatabaseAccessor(const {}, const [], schema.schema); - final input = DatabaseGenerationInput(database, resolved, const {}); + final input = DatabaseGenerationInput(database, resolved, const {}, null); DatabaseWriter(input, writer.child()).write(); diff --git a/drift_dev/lib/src/writer/database_writer.dart b/drift_dev/lib/src/writer/database_writer.dart index cbfc492a..5a492746 100644 --- a/drift_dev/lib/src/writer/database_writer.dart +++ b/drift_dev/lib/src/writer/database_writer.dart @@ -4,6 +4,7 @@ import 'package:drift/src/runtime/executor/stream_queries.dart'; import 'package:drift_dev/src/writer/utils/memoized_getter.dart'; import 'package:recase/recase.dart'; +import '../analysis/driver/driver.dart'; import '../analysis/results/file_results.dart'; import '../analysis/results/results.dart'; import '../services/find_stream_update_rules.dart'; @@ -17,7 +18,7 @@ import 'writer.dart'; /// Generates the Dart code put into a `.g.dart` file when running the /// generator. class DatabaseWriter { - DatabaseGenerationInput input; + final DatabaseGenerationInput input; final Scope scope; DriftDatabase get db => input.accessor; @@ -113,11 +114,13 @@ class DatabaseWriter { code: createIndex(scope, entity), ); } else if (entity is DriftView) { + final viewClassName = dbScope.dartCode(dbScope.entityInfoType(entity)); + writeMemoizedGetter( buffer: dbScope.leaf().buffer, getterName: entity.dbGetterName, - returnType: entity.entityInfoName, - code: '${entity.entityInfoName}(this)', + returnType: viewClassName, + code: '$viewClassName(this)', ); } } @@ -139,21 +142,8 @@ class DatabaseWriter { // Also write implicit DAOs for modular imports if (scope.generationOptions.isModular) { for (final import in input.resolvedAccessor.knownImports) { - if (import.hasModularDriftAccessor) { - final type = dbScope.modularAccessor(import.ownUri); - final getter = ReCase(type.toString()).camelCase; - - dbScope.leaf() - ..writeDart(type) - ..write(' get $getter => ') - ..writeUriRef( - ModularAccessorWriter.modularSupport, 'ReadDatabaseContainer') - ..writeln('(this).accessor<') - ..writeDart(type) - ..write('>(') - ..writeDart(type) - ..writeln('.new);'); - } + dbScope.writeGetterForIncludedDriftFile(import, input.driver!, + isAccessor: false); } } @@ -250,8 +240,10 @@ class GenerationInput { final T accessor; final ResolvedDatabaseAccessor resolvedAccessor; final Map importedQueries; + final DriftAnalysisDriver? driver; - GenerationInput(this.accessor, this.resolvedAccessor, this.importedQueries); + GenerationInput( + this.accessor, this.resolvedAccessor, this.importedQueries, this.driver); /// All locally-defined and imported [SqlQuery] elements that are regular /// queries (so no query with [QueryMode.atCreate]). diff --git a/drift_dev/lib/src/writer/drift_accessor_writer.dart b/drift_dev/lib/src/writer/drift_accessor_writer.dart index f9689bba..e9db2830 100644 --- a/drift_dev/lib/src/writer/drift_accessor_writer.dart +++ b/drift_dev/lib/src/writer/drift_accessor_writer.dart @@ -1,5 +1,6 @@ import '../analysis/results/results.dart'; import 'database_writer.dart'; +import 'modules.dart'; import 'queries/query_writer.dart'; import 'writer.dart'; @@ -35,6 +36,13 @@ class AccessorWriter { QueryWriter(classScope.child()).write(query); } + if (scope.generationOptions.isModular) { + for (final import in input.resolvedAccessor.knownImports) { + classScope.writeGetterForIncludedDriftFile(import, input.driver!, + isAccessor: true); + } + } + classScope.leaf().write('}'); } } diff --git a/drift_dev/lib/src/writer/modules.dart b/drift_dev/lib/src/writer/modules.dart index 75812796..9bb49eff 100644 --- a/drift_dev/lib/src/writer/modules.dart +++ b/drift_dev/lib/src/writer/modules.dart @@ -1,6 +1,6 @@ -import 'package:path/path.dart' show url; import 'package:recase/recase.dart'; +import '../analysis/driver/driver.dart'; import '../analysis/driver/state.dart'; import '../analysis/results/results.dart'; import '../utils/string_escaper.dart'; @@ -19,11 +19,12 @@ import 'writer.dart'; class ModularAccessorWriter { final Scope scope; final FileState file; + final DriftAnalysisDriver driver; - ModularAccessorWriter(this.scope, this.file); + ModularAccessorWriter(this.scope, this.file, this.driver); void write() { - if (!file.hasModularDriftAccessor) return; + if (!file.needsModularAccessor(driver)) return; final className = scope.modularAccessor(file.ownUri); final generatedDatabase = scope.drift('GeneratedDatabase'); @@ -42,7 +43,11 @@ class ModularAccessorWriter { referencedElements.addAll(queryElement.references); } - QueryWriter(scope.child()).write(query.value); + final value = query.value; + if (value is SqlSelectQuery) { + referencedElements.addAll(value.readsFromTables); + } + QueryWriter(scope.child()).write(value); } final restOfClass = scope.leaf(); @@ -65,7 +70,9 @@ class ModularAccessorWriter { // Also make imports available final imports = file.discovery?.importDependencies ?? const []; for (final import in imports) { - if (url.extension(import.path) == '.drift') { + final file = driver.cache.knownFiles[import]; + + if (file != null && file.needsModularAccessor(driver)) { final moduleClass = restOfClass.modularAccessor(import); final getterName = ReCase(moduleClass.toString()).camelCase; @@ -87,3 +94,29 @@ class ModularAccessorWriter { static final Uri modularSupport = Uri.parse('package:drift/internal/modular.dart'); } + +extension WriteImplicitDaoGetter on Scope { + void writeGetterForIncludedDriftFile( + FileState import, DriftAnalysisDriver driver, + {required bool isAccessor}) { + assert(generationOptions.isModular); + + if (import.needsModularAccessor(driver)) { + final type = modularAccessor(import.ownUri); + final getter = ReCase(type.toString()).camelCase; + + final db = isAccessor ? 'attachedDatabase' : 'this'; + + leaf() + ..writeDart(type) + ..write(' get $getter => ') + ..writeUriRef( + ModularAccessorWriter.modularSupport, 'ReadDatabaseContainer') + ..writeln('($db).accessor<') + ..writeDart(type) + ..write('>(') + ..writeDart(type) + ..writeln('.new);'); + } + } +} diff --git a/drift_dev/lib/src/writer/queries/query_writer.dart b/drift_dev/lib/src/writer/queries/query_writer.dart index e1d07ead..56398ac0 100644 --- a/drift_dev/lib/src/writer/queries/query_writer.dart +++ b/drift_dev/lib/src/writer/queries/query_writer.dart @@ -76,10 +76,11 @@ class QueryWriter { /// custom return type of a query. void _writeMappingLambda(SqlQuery query) { final resultSet = query.resultSet!; + final queryRow = _emitter.drift('QueryRow'); if (resultSet.singleColumn) { final column = resultSet.columns.single; - _buffer.write('(QueryRow row) => ' + _buffer.write('($queryRow row) => ' '${readingCode(column, scope.generationOptions, options)}'); } else if (resultSet.matchingTable != null) { // note that, even if the result set has a matching table, we can't just @@ -92,7 +93,7 @@ class QueryWriter { _buffer.write('${table.dbGetterName}.mapFromRow'); } else { _buffer - ..write('(QueryRow row) => ') + ..write('($queryRow row) => ') ..write('${table.dbGetterName}.mapFromRowWithAlias(row, const {'); for (final alias in match.aliasToColumn.entries) { @@ -106,7 +107,7 @@ class QueryWriter { _buffer.write('})'); } } else { - _buffer.write('(QueryRow row) '); + _buffer.write('($queryRow row) '); if (query.needsAsyncMapping) { _buffer.write('async '); } @@ -292,7 +293,8 @@ class QueryWriter { final scopedType = scopedTypeName(element); final args = element.availableResultSets - .map((e) => '${e.argumentType} ${e.name}') + .map((e) => + '${_emitter.dartCode(scope.entityInfoType(e.entity))} ${e.name}') .join(', '); root.leaf().write('typedef $scopedType = $type Function($args);'); @@ -421,9 +423,9 @@ class QueryWriter { void _writeUpdateKind(UpdatingQuery update) { if (update.isOnlyDelete) { - _buffer.write(', updateKind: UpdateKind.delete'); + _buffer.write(', updateKind: ${_emitter.drift('UpdateKind.delete')}'); } else if (update.isOnlyUpdate) { - _buffer.write(', updateKind: UpdateKind.update'); + _buffer.write(', updateKind: ${_emitter.drift('UpdateKind.update')}'); } } } diff --git a/drift_dev/lib/src/writer/tables/table_writer.dart b/drift_dev/lib/src/writer/tables/table_writer.dart index b825547e..86276a48 100644 --- a/drift_dev/lib/src/writer/tables/table_writer.dart +++ b/drift_dev/lib/src/writer/tables/table_writer.dart @@ -76,11 +76,11 @@ abstract class TableOrViewWriter { final literalEntries = [ for (final entry in constraints.entries) - 'SqlDialect.${entry.key.name}: ${asDartLiteral(entry.value)},', + '${emitter.drift('SqlDialect.${entry.key.name}')}: ${asDartLiteral(entry.value)},', ]; additionalParams['defaultConstraints'] = - 'GeneratedColumn.constraintsDependsOnDialect({${literalEntries.join('\n')}})'; + '${emitter.drift('GeneratedColumn.constraintsDependsOnDialect')}({${literalEntries.join('\n')}})'; } else { // Constraints are the same regardless of dialect, only generate one set // of them @@ -88,7 +88,7 @@ abstract class TableOrViewWriter { final constraint = asDartLiteral(constraints.values.first); additionalParams['defaultConstraints'] = - 'GeneratedColumn.constraintIsAlways($constraint)'; + '${emitter.drift('GeneratedColumn.constraintIsAlways')}($constraint)'; } } diff --git a/drift_dev/lib/src/writer/tables/view_writer.dart b/drift_dev/lib/src/writer/tables/view_writer.dart index bb69ccec..1e2b9cfa 100644 --- a/drift_dev/lib/src/writer/tables/view_writer.dart +++ b/drift_dev/lib/src/writer/tables/view_writer.dart @@ -30,18 +30,21 @@ class ViewWriter extends TableOrViewWriter { void _writeViewInfoClass() { emitter = scope.leaf(); - buffer.write('class ${view.entityInfoName} extends ViewInfo'); + buffer.write( + 'class ${view.entityInfoName} extends ${emitter.drift('ViewInfo')}'); if (scope.generationOptions.writeDataClasses) { + final viewClassName = emitter.dartCode(emitter.entityInfoType(view)); emitter - ..write('<${view.entityInfoName}, ') + ..write('<$viewClassName, ') ..writeDart(emitter.rowType(view)) ..write('>'); } else { buffer.write('<${view.entityInfoName}, Never>'); } - buffer.writeln(' implements HasResultSet {'); + buffer.writeln(' implements ${emitter.drift('HasResultSet')} {'); - final dbClassName = databaseWriter?.dbClassName ?? 'GeneratedDatabase'; + final dbClassName = + databaseWriter?.dbClassName ?? emitter.drift('GeneratedDatabase'); buffer ..writeln('final String? _alias;') ..writeln('@override final $dbClassName attachedDatabase;') @@ -118,7 +121,7 @@ class ViewWriter extends TableOrViewWriter { } void _writeQuery() { - buffer.write('@override\nQuery? get query => '); + buffer.write('@override\n${emitter.drift('Query?')} get query => '); final source = view.source; if (source is DartViewSource) { diff --git a/drift_dev/test/services/schema/writer_test.dart b/drift_dev/test/services/schema/writer_test.dart index 26062ea3..01ceab41 100644 --- a/drift_dev/test/services/schema/writer_test.dart +++ b/drift_dev/test/services/schema/writer_test.dart @@ -111,7 +111,7 @@ class Database {} ); final resolved = ResolvedDatabaseAccessor(const {}, const [], reader.entities.toList()); - final input = DatabaseGenerationInput(database, resolved, const {}); + final input = DatabaseGenerationInput(database, resolved, const {}, null); // Write the database. Not crashing is good enough for us here, we have // separate tests for verification diff --git a/examples/modular/bin/example.dart b/examples/modular/bin/example.dart index a510aa28..f624d9c2 100644 --- a/examples/modular/bin/example.dart +++ b/examples/modular/bin/example.dart @@ -5,7 +5,7 @@ import 'package:modular/src/users.drift.dart'; void main() async { final database = Database(NativeDatabase.memory(logStatements: true)); - database.usersDrift.findUsers().watch().listen(print); + database.userQueriesDrift.findUsers().watch().listen(print); await database.myAccessor .addUser(user: UsersCompanion.insert(name: 'first_user')); diff --git a/examples/modular/lib/accessor.dart b/examples/modular/lib/accessor.dart index b013afdd..b637b909 100644 --- a/examples/modular/lib/accessor.dart +++ b/examples/modular/lib/accessor.dart @@ -4,7 +4,7 @@ import 'accessor.drift.dart'; import 'database.dart'; @DriftAccessor( - include: {'src/users.drift'}, + include: {'src/user_queries.drift'}, queries: {'addUser': r'INSERT INTO users $user;'}, ) class MyAccessor extends DatabaseAccessor with $MyAccessorMixin { diff --git a/examples/modular/lib/accessor.drift.dart b/examples/modular/lib/accessor.drift.dart index fd80492b..53c935c6 100644 --- a/examples/modular/lib/accessor.drift.dart +++ b/examples/modular/lib/accessor.drift.dart @@ -2,10 +2,13 @@ import 'package:drift/drift.dart' as i0; import 'package:modular/database.dart' as i1; import 'package:modular/src/users.drift.dart' as i2; +import 'package:modular/src/user_queries.drift.dart' as i3; +import 'package:drift/internal/modular.dart' as i4; mixin $MyAccessorMixin on i0.DatabaseAccessor { i2.Users get users => attachedDatabase.users; i2.Follows get follows => attachedDatabase.follows; + i2.PopularUsers get popularUsers => attachedDatabase.popularUsers; Future addUser({required i0.Insertable user}) { var $arrayStartIndex = 1; final generateduser = @@ -17,4 +20,8 @@ mixin $MyAccessorMixin on i0.DatabaseAccessor { updates: {users}, ); } + + i3.UserQueriesDrift get userQueriesDrift => + i4.ReadDatabaseContainer(attachedDatabase) + .accessor(i3.UserQueriesDrift.new); } diff --git a/examples/modular/lib/database.dart b/examples/modular/lib/database.dart index e713a992..12ddbcee 100644 --- a/examples/modular/lib/database.dart +++ b/examples/modular/lib/database.dart @@ -4,7 +4,7 @@ import 'accessor.dart'; import 'database.drift.dart'; @DriftDatabase(include: { - 'src/users.drift', + 'src/user_queries.drift', 'src/posts.drift', 'src/search.drift', }, daos: [ diff --git a/examples/modular/lib/database.drift.dart b/examples/modular/lib/database.drift.dart index 900ec793..f054a60c 100644 --- a/examples/modular/lib/database.drift.dart +++ b/examples/modular/lib/database.drift.dart @@ -5,7 +5,8 @@ import 'package:modular/src/posts.drift.dart' as i2; import 'package:modular/src/search.drift.dart' as i3; import 'package:modular/accessor.dart' as i4; import 'package:modular/database.dart' as i5; -import 'package:drift/internal/modular.dart' as i6; +import 'package:modular/src/user_queries.drift.dart' as i6; +import 'package:drift/internal/modular.dart' as i7; abstract class $Database extends i0.GeneratedDatabase { $Database(i0.QueryExecutor e) : super(e); @@ -14,12 +15,11 @@ abstract class $Database extends i0.GeneratedDatabase { late final i3.SearchInPosts searchInPosts = i3.SearchInPosts(this); late final i2.Likes likes = i2.Likes(this); late final i1.Follows follows = i1.Follows(this); + late final i1.PopularUsers popularUsers = i1.PopularUsers(this); late final i4.MyAccessor myAccessor = i4.MyAccessor(this as i5.Database); - i1.UsersDrift get usersDrift => - i6.ReadDatabaseContainer(this).accessor(i1.UsersDrift.new); - i2.PostsDrift get postsDrift => - i6.ReadDatabaseContainer(this).accessor(i2.PostsDrift.new); - i3.SearchDrift get searchDrift => i6.ReadDatabaseContainer(this) + i6.UserQueriesDrift get userQueriesDrift => i7.ReadDatabaseContainer(this) + .accessor(i6.UserQueriesDrift.new); + i3.SearchDrift get searchDrift => i7.ReadDatabaseContainer(this) .accessor(i3.SearchDrift.new); @override Iterable> get allTables => @@ -33,8 +33,9 @@ abstract class $Database extends i0.GeneratedDatabase { i3.postsUpdate, i3.postsDelete, likes, - i1.usersName, - follows + follows, + popularUsers, + i1.usersName ]; @override i0.StreamQueryUpdateRules get streamUpdateRules => diff --git a/examples/modular/lib/src/posts.drift.dart b/examples/modular/lib/src/posts.drift.dart index 7b007d0a..ff703073 100644 --- a/examples/modular/lib/src/posts.drift.dart +++ b/examples/modular/lib/src/posts.drift.dart @@ -1,8 +1,6 @@ // ignore_for_file: type=lint import 'package:drift/drift.dart' as i0; import 'package:modular/src/posts.drift.dart' as i1; -import 'package:drift/internal/modular.dart' as i2; -import 'package:modular/src/users.drift.dart' as i3; class Post extends i0.DataClass implements i0.Insertable { final int id; @@ -396,8 +394,3 @@ class Likes extends i0.Table with i0.TableInfo { @override bool get dontWriteConstraints => true; } - -class PostsDrift extends i2.ModularAccessor { - PostsDrift(i0.GeneratedDatabase db) : super(db); - i3.UsersDrift get usersDrift => this.accessor(i3.UsersDrift.new); -} diff --git a/examples/modular/lib/src/search.drift.dart b/examples/modular/lib/src/search.drift.dart index 5a23ccda..9c4cc35d 100644 --- a/examples/modular/lib/src/search.drift.dart +++ b/examples/modular/lib/src/search.drift.dart @@ -220,5 +220,4 @@ class SearchDrift extends i2.ModularAccessor { i1.SearchInPosts get searchInPosts => this.resultSet('search_in_posts'); i3.Posts get posts => this.resultSet('posts'); - i3.PostsDrift get postsDrift => this.accessor(i3.PostsDrift.new); } diff --git a/examples/modular/lib/src/user_queries.drift b/examples/modular/lib/src/user_queries.drift new file mode 100644 index 00000000..ddb76414 --- /dev/null +++ b/examples/modular/lib/src/user_queries.drift @@ -0,0 +1,5 @@ +import 'users.drift'; + +findUsers($predicate = TRUE): SELECT * FROM users WHERE $predicate; +findPopularUsers: SELECT * FROM popular_users; +follow: INSERT INTO follows VALUES (?, ?); diff --git a/examples/modular/lib/src/user_queries.drift.dart b/examples/modular/lib/src/user_queries.drift.dart new file mode 100644 index 00000000..8fe27dbd --- /dev/null +++ b/examples/modular/lib/src/user_queries.drift.dart @@ -0,0 +1,47 @@ +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:drift/internal/modular.dart' as i1; +import 'package:modular/src/users.drift.dart' as i2; + +class UserQueriesDrift extends i1.ModularAccessor { + UserQueriesDrift(i0.GeneratedDatabase db) : super(db); + i0.Selectable findUsers({FindUsers$predicate? predicate}) { + var $arrayStartIndex = 1; + final generatedpredicate = $write( + predicate?.call(this.users) ?? const i0.CustomExpression('(TRUE)'), + startIndex: $arrayStartIndex); + $arrayStartIndex += generatedpredicate.amountOfVariables; + return customSelect('SELECT * FROM users WHERE ${generatedpredicate.sql}', + variables: [ + ...generatedpredicate.introducedVariables + ], + readsFrom: { + users, + ...generatedpredicate.watchedTables, + }).asyncMap(users.mapFromRow); + } + + i0.Selectable findPopularUsers() { + return customSelect('SELECT * FROM popular_users', + variables: [], + readsFrom: { + users, + follows, + }).asyncMap(popularUsers.mapFromRow); + } + + Future follow(int var1, int var2) { + return customInsert( + 'INSERT INTO follows VALUES (?1, ?2)', + variables: [i0.Variable(var1), i0.Variable(var2)], + updates: {follows}, + ); + } + + i2.Users get users => this.resultSet('users'); + i2.PopularUsers get popularUsers => + this.resultSet('popular_users'); + i2.Follows get follows => this.resultSet('follows'); +} + +typedef FindUsers$predicate = i0.Expression Function(i2.Users users); diff --git a/examples/modular/lib/src/users.drift b/examples/modular/lib/src/users.drift index d60ae5bb..5acb96fd 100644 --- a/examples/modular/lib/src/users.drift +++ b/examples/modular/lib/src/users.drift @@ -15,4 +15,6 @@ CREATE TABLE follows ( PRIMARY KEY (followed, follower) ); -findUsers($predicate = TRUE): SELECT * FROM users WHERE $predicate; +CREATE VIEW popular_users AS + SELECT * FROM users + ORDER BY (SELECT count(*) FROM follows WHERE followed = users.id); diff --git a/examples/modular/lib/src/users.drift.dart b/examples/modular/lib/src/users.drift.dart index f3fce9e1..b8b6a297 100644 --- a/examples/modular/lib/src/users.drift.dart +++ b/examples/modular/lib/src/users.drift.dart @@ -2,7 +2,6 @@ import 'package:drift/drift.dart' as i0; import 'package:modular/src/users.drift.dart' as i1; import 'package:modular/src/preferences.dart' as i2; -import 'package:drift/internal/modular.dart' as i3; class User extends i0.DataClass implements i0.Insertable { final int id; @@ -451,25 +450,123 @@ class Follows extends i0.Table with i0.TableInfo { bool get dontWriteConstraints => true; } -class UsersDrift extends i3.ModularAccessor { - UsersDrift(i0.GeneratedDatabase db) : super(db); - i0.Selectable findUsers({FindUsers$predicate? predicate}) { - var $arrayStartIndex = 1; - final generatedpredicate = $write( - predicate?.call(this.users) ?? const i0.CustomExpression('(TRUE)'), - startIndex: $arrayStartIndex); - $arrayStartIndex += generatedpredicate.amountOfVariables; - return customSelect('SELECT * FROM users WHERE ${generatedpredicate.sql}', - variables: [ - ...generatedpredicate.introducedVariables - ], - readsFrom: { - users, - ...generatedpredicate.watchedTables, - }).asyncMap(users.mapFromRow); +class PopularUser extends i0.DataClass { + final int id; + final String name; + final String? biography; + final i2.Preferences? preferences; + const PopularUser( + {required this.id, required this.name, this.biography, this.preferences}); + factory PopularUser.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return PopularUser( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + biography: serializer.fromJson(json['biography']), + preferences: Users.$converterpreferencesn.fromJson( + serializer.fromJson?>(json['preferences'])), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'biography': serializer.toJson(biography), + 'preferences': serializer.toJson?>( + Users.$converterpreferencesn.toJson(preferences)), + }; } - i1.Users get users => this.resultSet('users'); + i1.PopularUser copyWith( + {int? id, + String? name, + i0.Value biography = const i0.Value.absent(), + i0.Value preferences = const i0.Value.absent()}) => + i1.PopularUser( + id: id ?? this.id, + name: name ?? this.name, + biography: biography.present ? biography.value : this.biography, + preferences: preferences.present ? preferences.value : this.preferences, + ); + @override + String toString() { + return (StringBuffer('PopularUser(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('biography: $biography, ') + ..write('preferences: $preferences') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, biography, preferences); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.PopularUser && + other.id == this.id && + other.name == this.name && + other.biography == this.biography && + other.preferences == this.preferences); } -typedef FindUsers$predicate = i0.Expression Function(Users users); +class PopularUsers extends i0.ViewInfo + implements i0.HasResultSet { + final String? _alias; + @override + final i0.GeneratedDatabase attachedDatabase; + PopularUsers(this.attachedDatabase, [this._alias]); + @override + List get $columns => [id, name, biography, preferences]; + @override + String get aliasedName => _alias ?? entityName; + @override + String get entityName => 'popular_users'; + @override + String get createViewStmt => + 'CREATE VIEW popular_users AS SELECT * FROM users ORDER BY (SELECT count(*) FROM follows WHERE followed = users.id)'; + @override + PopularUsers get asDslTable => this; + @override + i1.PopularUser map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.PopularUser( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!, + name: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + biography: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}biography']), + preferences: Users.$converterpreferencesn.fromSql(attachedDatabase + .typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}preferences'])), + ); + } + + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', aliasedName, false, + type: i0.DriftSqlType.int); + late final i0.GeneratedColumn name = i0.GeneratedColumn( + 'name', aliasedName, false, + type: i0.DriftSqlType.string); + late final i0.GeneratedColumn biography = i0.GeneratedColumn( + 'biography', aliasedName, true, + type: i0.DriftSqlType.string); + late final i0.GeneratedColumnWithTypeConverter + preferences = i0.GeneratedColumn('preferences', aliasedName, true, + type: i0.DriftSqlType.string) + .withConverter(Users.$converterpreferencesn); + @override + PopularUsers createAlias(String alias) { + return PopularUsers(attachedDatabase, alias); + } + + @override + i0.Query? get query => null; + @override + Set get readTables => const {'users', 'follows'}; +} diff --git a/extras/integration_tests/drift_testcases/lib/database/database.g.dart b/extras/integration_tests/drift_testcases/lib/database/database.g.dart index 86bbee6d..6eb5fbec 100644 --- a/extras/integration_tests/drift_testcases/lib/database/database.g.dart +++ b/extras/integration_tests/drift_testcases/lib/database/database.g.dart @@ -562,7 +562,7 @@ abstract class _$Database extends GeneratedDatabase { ], readsFrom: { friendships, - }).map((QueryRow row) => row.read('_c0')); + }).map((row) => row.read('_c0')); } Selectable friendshipsOf(int user) { @@ -574,7 +574,7 @@ abstract class _$Database extends GeneratedDatabase { readsFrom: { friendships, users, - }).asyncMap((QueryRow row) async { + }).asyncMap((row) async { return FriendshipsOfResult( reallyGoodFriends: row.read('really_good_friends'), user: await users.mapFromRow(row, tablePrefix: 'nested_0'), @@ -587,7 +587,7 @@ abstract class _$Database extends GeneratedDatabase { variables: [], readsFrom: { users, - }).map((QueryRow row) => row.read('_c0')); + }).map((row) => row.read('_c0')); } Selectable settingsFor(int user) { @@ -597,7 +597,7 @@ abstract class _$Database extends GeneratedDatabase { ], readsFrom: { users, - }).map((QueryRow row) => $UsersTable.$converterpreferences + }).map((row) => $UsersTable.$converterpreferences .fromSql(row.readNullable('preferences'))); } diff --git a/tool/misc_integration_test.sh b/tool/misc_integration_test.sh index 265f2619..3c303755 100755 --- a/tool/misc_integration_test.sh +++ b/tool/misc_integration_test.sh @@ -16,6 +16,7 @@ pushd examples/modular echo "Running build runner in modular example" dart pub upgrade dart run build_runner build --delete-conflicting-outputs +dart run bin/example.dart popd pushd examples/migrations_example